Megpróbálom érthetően leírni, hogy mi is a gond, nem triviális.
Szóval, a régebbi Clang fordítók, és a distro-mban évő 17.0.6 is freestanding módban helyesen .text, .data és .bss szekciókat generál.
Azonban a legfrissebb (forrásból telepített) Clang nem, az ilyent eredményez:
readelf -S exfat.o There are 9 section headers, starting at offset 0x1968: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .strtab STRTAB 0000000000000000 00001830 0000000000000132 0000000000000000 0 0 1 [ 2] .text PROGBITS 0000000000000000 00000040 0000000000000d10 0000000000000000 AX 0 0 16 [ 3] .rela.text RELA 0000000000000000 00001110 0000000000000708 0000000000000018 8 2 8 [ 4] .note NOTE 0000000000000000 00000d50 0000000000000084 0000000000000000 A 0 0 4 [ 5] .comment PROGBITS 0000000000000000 00000dd4 0000000000000020 0000000000000001 MS 0 0 1 [ 6] .note.GNU-stack PROGBITS 0000000000000000 00000df4 0000000000000000 0000000000000000 0 0 1 [ 7] .llvm_addrsig LOOS+0xfff4c03 0000000000000000 00001818 0000000000000018 0000000000000000 E 8 0 1 [ 8] .symtab SYMTAB 0000000000000000 00000df8 0000000000000318 0000000000000018 1 7 8 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), D (mbind), l (large), p (processor specific)
Amint látható, BSS szekciónak hűlt helye, van viszont helyette LOOS szekció (ami OS-specifikust jelent), és aminek freestanding módban nem is szabadna léteznie, tehát ez egyértelműen Clang fordító bug.
A kérdésem az, hogy próbáltam a doksit bújni, de nem találtam, viszont hátha valaki itt a HUP-on ismer olyan Clang parancssori kapcsolót, esetleg fordítási opciót, amivel a korábbi működés helyreállítható (azaz hogy freestanding módban NE legyenek OS-specifikus szekciók, csak a hagyományos .text, .data és .bss).
Bármi tipp?
MEGOLDÁS: ezer hála lacos-nak, aki megtalálta az egyik kapcsolót! A hiányzó bss-re is találtam megoldást, az -fno-addrsig -fno-common kapcsolók együttesen eltűntetik a LOOS szekciót és visszahozzák a BSS-t, azaz egy-az-egyben visszaállítják a korábbi működést. Azt nem értem, hogy freestanding módban miért nem ezek az alapértelmezettek, de sebaj, megadható, működik!
Hozzászólások
Elvileg erre a -ffreestanging kapcsoló való. Ha úgy se jó, próbáld meg -ansi kapcsolóval, csak próbából, nehogy valami modern standard miatt próbálja kikapcsolni, mert megváltozott a default.
Lehet tévedek, de elvileg az sem baj, ha nincs ott a BSS szekció, attól még futnia kéne OS-től függetlenül.
“The world runs on Excel spreadsheets.” (Dylan Beattie)
Mindkettő már meg van adva, de azért kössz!
De nekem kéne... Ráadásul amit korábban a BSS-be rakott, az került most ebbe az LOOS-be. Legalábbis az egyik eltűnt, a másik megjelent és egyforma a méretük.
Hasznos információt nem a Type=LOOS alapján találtam, hanem a Section Name=.llvm_addrsig alapján.
Ez szemre egy lld linker feature, nem bug; úgy hívják, hogy "Identical Code Folding". Ennek a gcc BZ-nek a legelső kommentjében jól összefoglalják a feature-t:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105625#c0
A .llvm_addrsig section ahhoz kell, hogy ezt az optimalizálási lépést "biztonságosan" végre lehessen hajtani, amit pedig az --icf=safe kapcsolóval lehet kérni:
https://llvm.org/docs/Extensions.html#sht-llvm-addrsig-section-address-…
https://maskray.me/blog/2024-03-09-a-new-relocation-format-for-elf#llvm…
Tipp: próbáld meg az lld-nek megadni az --icf=none kapcsolót:
https://man.archlinux.org/man/extra/lld/ld.lld.1.en#icf~3
Általános tipp: a readelf olvashatóbb kimenetet produkál a --wide kapcsolóval.
Nem lehet linker feature, mert linkert nem is használok hozzá, csak fordítót. Ráadásul nem is kódot rakja bele (az a .text szekcióba került), hanem a BSS adatotokat (0x18 bájtnyit, pont ekkora volt a régi verzióval a BSS).
Amit itt írnak, az teljes hülyeség. Egyrészt milyen extension az, amit nem lehet kikapcsolni (mert ha nem lehet, akkor az nem is lehet extension, ugye), másrészt asszongya: "This section is used to mark symbols as address-significant, i.e. the address of the symbol is used in a comparison or leaks outside the translation unit.", ami alapján üresnek kéne lennie ennek a szekciónak (mivel csak helyi változókról van eleve szó, tehát nem lehet "leaks outside the translation unit", a mondat másik felének meg nincs is értelme, max. ha dinamikusan linkelnék, ami meg freestanding módban nem is lehetséges).
Azt is írja, hogy "Without this directive, all symbols are considered address-significant.", magyarán mi a francnak van ez a szekció, ha alapból úgyis az összes szimbólumot bele kell rakni??? Normális? És miért nem a szimbólumindexek vannak benne, és miért pont akkora, mint a hiányzó BSS? Ehh... esszerint két bugról beszélünk: a hiányzó BSS, és a freestanding ellenére megjelenő LOOS, de a kettőnek semmi köze egymáshoz, csak véletlenül egyforma a méretük?
Ennél bonyoltultabb a dolog, mert nem használok se LLVM lld-t, se GNU ld-t. Speciális formátumú kimenetre van szükségem, ezért igazából én írtam a linkert hozzá, ami beolvassa az ELF objektumokat és előállítja ez a formátumot.
Ezért kéne tudnom, pontosan hol van és mekkora a BSS, és ezért baj, ha valami gányolt LOOS szekció van helyette. (Eredetileg a.out-ot akartam volna, de a gccből újabban kivették alapból, a Clang-ba meg soha nem is implementálták, ezért a szopóroller a saját formátummal.)
Kis rant: ahogy én látom, ennek a Rui Ueyama nevű faszjancsinak fingja sincs, hogy mi is a linker feladata, és valószínűleg még soha életében nem fordított bare metalra, de még csak statikusan sem linkelt soha, csakis kizárólag mindenféle fos bloated frameworköket linkelt dinamikusan, ezért azt hiszi, csak az létezik... Hogy nem küldték még el a P-be az öreg motorosok? Mármint oké, hogy belerak egy ilyen szart a fosai kedvéért, na de hogy úgy, hogy még ki sem lehet kapcsolni, és minden mást tör vele...?
Ez azért van, mert a C nyelv való rendszerprogramozásra és ezért ha egyszer leírtál egy programot, vagy összeraktál egy buildet akkor az nem törik el. Nem kell folyton hülyeségek miatt újrapeccselni minden régi kódot minden fordítóverzióval, mert ez C, nem csiligány újhullámos szar.
LOL :D
Az azért ugye megvan, hogy ennek itt SEMMI KÖZE magához a C nyelvhez, de még csak a C nyelvű források sem változtak egyetlen bitnyit sem, és például gcc fordítóval továbbra is tökéletesen működik a linkerem, de a Clang linkerével sem okoz gondot?
Mindössze egy fordítóprogram egy opciójának alapértelmezését baszták el az egyik fordítási módban, és amit valószínűleg hamarosan javítani fognak, ha többen is reklamálnak érte. De mégegyszer, a saját linkerével használva továbbra sem lenne gond, csak azért jött ez elő, mert saját linkert kellett írnom.
Ha azt hiszed, ennek bármi köze van a C nyelvhez, akkor add vissza az IT diplomád!
Ezek az esetek amikor valamit újra elő kell venni, guglizni napokat, fórumozgatni és a végén megjavítani mindig úgy szoktak lenni, hogy valójában nem is hiba, meg nem hibája annak amit szeretünk, csak éppen elment vele három nap amit ki kell fizetni és mégis annyival később lesz kész a termék is. De a C nyelvnek semmi köze hozzá, az tökéletes, csak a fordítóval van valami apróság, lényegtelen semmiség.
Lassan írom, hogy még az olyan együgyűek is biztos megértsék, mint Te.
1. Ha Ada-ból vagy Rust-ból fordítottam volna ezt az ELF-et, akkor is pontosan ugyanez lett volna, mert semmi köze a nyelvhez, csakis az ELF specifikációhoz van köze.
2. Mindössze az egyik fordító egyik alapbeállítása változott, ami csak azért volt itt gond, mert a szabvány fordítási környezet HELYETT saját linkert írtam, amibe nem volt még beleimplementálva ez az ELF opció.
Szóval a Clang fordító csinált neked egy problémát, de semmi köze a C-hez az egésznek. Emlékszel, hogy nem is olyan régen egy egész topikot elfloodoltál azzal a gondolattal, hogy ha írsz egy C programot, akkor az működni fog 10 év múlva is változatlanul? Mert "szabvány". Kivéve például a szegmensek nevei: az nem szabvány. Talán megpróbálhatnád átírni Rustra.
Valóban irónikus, és a C sem tökéletes, ezt aláírom. Nem annyira felhőtlen, mint azt mi C fanboyok előadjuk idealizáltan, megvannak a saját problémái, a nyelvnek, szabványainak, fordítóknak is. Másik oldalról meg semmi nem lesz soha hibátlan, egyik nyelv, fordító sem, a Rust sem lesz mindenhatóbb, annak is megvannak a hátrányai.
A C legalább még mindig a legegyszerűbbek, legkonzervatívabbak között van, még általában azzal a legkisebb a fordított bináris mérete, azzal a legjobb a futási sebessége (hacsak nem speciális műfajról van szó, pl. CUDA számításokra optimalizált Fortran fordítóval vetjük össze, és linpack-szerű felhasználás a cél).
Nyilván a Clang/LLVM mindig is erőteljesen újító volt, aki konzervativizmust akar (mind pl. a Linux kernelesek), az inkább marad gcc-n, meg régebbi C-szabványon (C89, 90, 99), meg linkeléskor se újítgat be túl modern módszerekkel, túl agresszív optimalizációkkal (LTO és társai).
Ez a 10 év múlva is változatlanul lefordul, az kb. sose volt igaz C-nél sem, míg nem volt szabványosítva, a C fordítók gyakran egyedi megoldásoztak, aztán meg mikor lett szabvány, azok állandóan változtak, egymással nem feltétlen kompatibilisen. Pl. én a múlt héten belefutottam egy csomó olyan régi CLI program projektjébe, amik már nem fordultak le, pedig nagyon egyszerű, alap programok, de már olyan blőd dolgok miatt nem fordulnak le, hogy már nem tudja rendesen beemelni függőségnek a libcurses-t, meg pl. olyan régi kód, hogy még nincs a függvényeknek visszatérési típusa definiálva, amire a modern fordító nem is warningot dob, hanem kifejezett error-ral bukik el az egész. Ráadásul az -ansi kapcsoló se segített, kézzel kellett a problémás kódrészeket áthegeszteni, pedig ráadásul ne is ilyen ultramodern fordítóval mentem neki, mint a Clang, hanem konzervatív gcc-vel.
Meg pl. most néztem pár napja Tannenbaumnak az egyik régebbi BSDCan-előadását, amiben a modern Minix helyzetét taglalta, meg hogy miért a NetBSD userland-et, pkg/ports, Clang ökoszisztémát vették át alapul a Minix 3+ esetében, miért nem a GNU-s, linuxos toolokat, aminél megjegyezte, hogy pedig próbálkoztak az utóbbival is, de egy csomó minden nem fordult le, mert kiderült, hogy a GNU/linuxos kódok igazából nem C-ben, hanem gcc-ben vannak írva (meg én részemről hibának látom, hogy sokszor x86-ra vannak alapozva, és nem kellően portolhatóan megírva, igaz ez nem mindegyikre igaz, de vannak kivételek sajnos, főleg GPU drivernél, meg egyes nem GNU-s, de linuxos szoftvereknél). Hallatszott rajta, a maró gúny, amivel mondta, a közönség fel is röhögött rajta.
“The world runs on Excel spreadsheets.” (Dylan Beattie)
A leírás alapján ez egy linker-feature _támogatása_ compiler oldalon. Úgynevezett _fejlődés_ .
Gábriel Ákos
Az llvm/clang forrásfában bányászva eljutottam a következő kapcsolóig:
-fno-addrsig
.Miután a forrásban megtaláltam, és tudtam, hogy pontosan mire kell keresni a neten, meglett a dokumentáció is:
https://clang.llvm.org/docs/UsersManual.html#cmdoption-faddrsig
Azt írja:
Ha megadod a clang-nak az
-fno-addrsig
-et, változik valami?(A kapcsoló egyébként elég régi; én most git-blame-mel a következő commit-ig nyomoztam le:
14b468bab620 ("Re-land r337333, "Teach Clang to emit address-significance tables.", which was reverted in r337336.", 2018-07-18)
. Azért nem könnyű megtalálni, mert mostanra a clang opciók reprezentációja a forrásban megváltozott. Például a7694b571d9fd ("[Driver] Add multiclass OptInFlag and OptOutFlag to simplify boolean option definition", 2020-06-02)
után azfaddrsig
sztringre keresve nincs azOptions.td
file-ban találat.)EZ AZ!!!
Sejtettem, hogy kell lennie valami ilyesmi kapcsolónak, de nagyon rosszul kerestem, mert nem találtam. Így utólag már tök logikus, hogy -fno-addrsig a neve! Ezer hála és köszönet!
hu hanyszor volt mar nekem is hogy valaminek a forrasabol kellett kibanyaszni a parametert, mert a doksi/help szar volt...
nem veletlen a mondas, hogy a forraskod a legjobb dokumentacio :)
Ez pont ugyanolyan okos mondás mint hogy mindenkinek van tesztrendszere csak a szerencsésebbeknek van a prodon kívül 1-2 másik is.
Gábriel Ákos
es a legjobb tesztelo a felhasznalo...
Nekem ez a Rui Ueyama nem tűnik annyira hülye gyereknek. Legalábbis nem egy átlag soydev, de abban igazad lehet, hogy bare metalban nem utazott eddig. Védelmére legyen mondva, hogy abba nagyon kevesen is utaznak, lényegében már csak kernel, driver, firmware-fejlesztők, meg esetleg Xen-fejlesztők. A többiek, kb. az összes fejlesztők 99,999999999999%-a tényleg ilyen-olyan framworkökbe, meg dinamikus linkeléssel, bytecode-ra fordítással, stb. tolja.
Ez a bare metal fejlesztési tudás kihalóban lévő műfaj, fekete mágiának számít már.
“The world runs on Excel spreadsheets.” (Dylan Beattie)
Az
-fno-common
-nal kapcsolatban: őszintén szólva nem értem, hogy miért nem az-fno-common
az alapértelmezett beállítás. Ugyanis az-fcommon
definiál egy olyan viselkedést, ami a C szabvány szerint definitálatlan -- és ez minimum zavaró. Például:Bármelyik file-t önmagában tekintve az
int x;
deklaráció tentative definition (kísérleti definíció), amely a file végét elérve external definition-né (külső definícióvá) válik. Ugyanis idézzük pl. a C99 szabványt (6.9.2 External object definitions, 2. bekezdés):Más szóval a szabvány a két fenti forrásfile viselkedését úgy szabja meg, hogy az egyenrangú legyen a következőkkel:
Ezek viszont már (külön-külön) external definition-nek számítanak (6.9.2 External object definitions, 1. bekezdés)::
Továbbá az
x
változónak mindkét file-ban (és mindkét változatban) external linkage-e van (6.2.2 Linkages of identifiers, 5. bekezdés 2. mondat):A két eredeti forrásfile-t egyetlen programba összelinkelve pedig megszegnénk a szabványt (6.9 External definitions, 5. bekezdés 2. mondat; aláhúzásos kiemelés tőlem):
És akkor nézzük meg pl. a gcc viselkedését:
A parancs nem jelez hibát. Itt jön a képbe az, hogy a shall hogyan értelmezendő:
A gcc-re csak azért nem mondhatjuk, hogy megszegi a szabványt, met a 6.9 External definitions, 5. bekezdés -- amely a "pontosan egy külső definíció" előírást tartalmazza -- nem egy Constraints szakaszban található, hanem egy Semantics szakaszban. Az előbbi esetben a gcc viselkedése megszegné a szabványt, így viszont csak az a helyzet, hogy a gcc definiál egy olyan viselkedést, amelyet a szabvány nem definiál. Ennek ellenére a gcc működése szerintem nagyon zavaró.
És akkor az
-fno-common
kapcsolóval ugyanez:Itt már van hibaüzenet:
Csak hogy három projektet említsek, amivel dolgom volt: mind az edk2, mind a qemu, mind a libvirt projektek megadják az
-fno-common
-t. Szerintem állíthatjuk azt, hogy az-fcommon
mint alapértelmezés hiba (vagy minimum tévedés) a fordítókban.Köszönöm a kimerítő választ! Tökéletesen egyetértek, szerintem is az -fno-common-nak kéne az alapértelmezettnek lennie. Sőt, igazából én sem tudok elképzelni olyan gyakorlati esetet, ahol lenne értelme a common-nak (mégis, mi baj lenne már azzal, hogy az inicializálatlan változók a bss-be kerülnek és nem egy fiktív nemlétező szekcióba?), ráadásul ahogy írod, valóban a C-ből fordított objektfájlok esetében még UB is.
Nálam ráadásul le sem fordul az a.c,b.c példád, pontosan ugyanazt a hibát dobja, mint az a2.c,b2c példa. Egyébként megjegyzem, nem a gcc parancs jelez hibát, hanem a linker (ezt ugye azért fontos itt kiemelni, mert ennél a konkrét esetnél én írtam a linkert, szóval nincs se collect2, se ld, plgld van helyettük).
Én inkább nem is azt nem értem, hogy az alapértelmezett viselkedés miért változott, hanem hogy ez miért nincs rendesen lekommunikálva, dokumentálva, aztán meg a felhasználó észreveszi, hogy valami nem stimmel. Ez így elég gáz, hogy random verzióban sunyiban ilyenek húznak meg.
“The world runs on Excel spreadsheets.” (Dylan Beattie)
őszintén szólva nem értem, hogy miért nem az
-fno-common
az alapértelmezett beállításHm... most ugy latom hogy ujabban itten (gcc-12 videken) mar ez az alapertelmezett. Legalabbis a manual szerint. De az ketsegtelen hogy beagyazott cuccoknal mindig megadom, foleg hogy ott vannak regebbi forditok (pl riscv64-unknown-elf-gcc-8.3.0) ahol tenyleg nem az az alapertelmezett... De ujabban (riscv64-unknown-elf-gcc-12.2.0) mar ott is...
Erdekes nagyon, koszi a reszletes leirast :) Tudtam csak nem sejtettem hogy ez az -fno-common azert fontos :)