Sziasztok!
Szinten RISC-V (32bites, beagyazott) tema: probalok egy sima mezei programot leforditani az xpack-riscv-none-elf toolchain segitsegevel. A forditas kapcsan alapjaraton minden jonak tunik, kiveve az hogy a linkeles utan ezt kapom:
$ nm main.elf | sort | grep " T "
20000000 T exit
20000026 T main
20000092 T _start
20000116 T main_task
[...]
Ami ugye erosen nem jo, es ezzel egyetertesben a `readelf --file-header main.elf` is ezt mondja:
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
[...]
Entry point address: 0x20000092
[...]
Merthat a processzor (vagyis annak a bootromja) az olyan hogy 0x20000000-rol szeret(ne) indulni. Ami megfurcsabbba teszi a dolgot, hogy a main() es a main_task() ugyanabbol a forras-modulbol jon, szoval meg egy *.o-bol jovo fuggvenyek koze is teszi be a linker a _start szimbolumnak megfelelo valamit. Ezutobbi papiron lehetne a forditas + linkeles soran kiadott `-ffunction-sections` + `-Wl,--gc-sections` eredmenye is (akar), de ha ezutobbiakat kikapcsolom akkoris ugyanez a helyzet :/ Akkor is ide (egyebkent pont a 0x...92-es cimre) kerul a _start.
Ez az xPack az a newlib-et hasznalja. Eddig csak picolibc-t hasznaltam RISC-V-s projektekre, szoval lehet hogy ez a newlib kinja ugy altalaban (es pl ARMv6-M vagy ARMv7-M eseten is "ilyen").
Mit lehetne itten tenni? Hogyan tudnam eroltetni az linkert (pl a megfelelo -Wl,... kapcsoloval) hogy marpedig a _start az legyen a 0x20000000 cimen es/vagy a .text legelejen? Fuggetlenul attol hogy a .text melyik subsection-jeben mi van? (( Mondjuk ettol fuggetlenul ha atirom a bootrom-ban hogy a 0x20000092-es cimrol induljon a program, akkor sem indul - meg - el, szoval vsz lesz itt mas turpissag is, de ez mas kerdes... ))
Maga a szerkesztes egyebkent olyan hogy a text is a RAM-ba (a 0x20000000-s cimre) van eroltetve, szoval a beagyazott rendszer jelleg ellenere ez most egy teljesen "von Neumann" linkeles: azaz mind a text, mind a data meg a bss (meg a heap) az a 0x20000000 cimtartomanyban van (alul a text, felul a tobbi). Es ez a "von Neumann" linkeles szepen mukodik picolibc eseten, szoval ezzel nem kene hogy gond legyen. Csak a newlib-nek nem tetszik.
thx, A.
- 313 megtekintés
Hozzászólások
Mivel ez az xpack-riscv-none-elf toolchain egy gcc származék, ezért használható vele linker script.
Mit lehetne itten tenni? Hogyan tudnam eroltetni az linkert (pl a megfelelo -Wl,... kapcsoloval) hogy marpedig a _start az legyen a 0x20000000 cimen es/vagy a .text legelejen?
Nem kapcsoló, linker script kell neked.
Maga a szerkesztes egyebkent olyan hogy a text is a RAM-ba (a 0x20000000-s cimre) van eroltetve
Linker script esetén kierőszakolható a konkrét RAM cím is, de szerintem ne szopj ezzel. Helyette add meg a linking address-t, és szépen sorjában tedd bele a text segment-be a text section-öket. Pl:
Ez bepakolja a .text és .text.akármi section-öket a text segment-be úgy, hogy a start.o-ból származó .text section kerüljön a segment legelejére (a "KEEP" elvileg nem kell, csak túlbiztosítás, az csak annyit jelent, hogy még az optimalizáló se mozgassa át, ha eseteg valamiért át akarná mégis mozgatni. Nem szokta akarni egyébként).
.text : {
. = 0x20000000;
KEEP(start.o(.text))
*(.text .text.*)
}
Ha csak a _start függvényt akarod előrerakni, és nem akarsz a fájlnevekkel bajlódni, vagy a start.o-ban több függvény is van és az _start nem a legeslegelső, akkor meg megoldás ez is:
A forrásba meg:
.text : {
. = 0x20000000;
KEEP(*(.text.start))
*(.text .text.*)
}
void __attribute__((section(".text.start"))) _start(void);
beagyazott rendszer jelleg ellenere ez most egy teljesen "von Neumann" linkeles: azaz mind a text, mind a data meg a bss (meg a heap) az a 0x20000000 cimtartomanyban van
Ez meg
.text : {
. = 0x20000000;
KEEP(*(.text.start))
*(.text .text.*)
*(.rodata .rodata.*)
*(.data .data.*)
}
.bss (NOLOAD) : {
. = ALIGN(16);
*(.bss .bss.*)
*(COMMON)
} :text
Itt a turpisság annyi, hogy a bss segment, ami NOLOAD jelzőt kap, azt meg hozzácsapjuk a text segment-hez (lásd a legvégén a blokkbezáró utánni ":text"-et). Rakhattuk volna eleve a bss section-öket is a text segmentbe, de akkor annak a mérete pont annyi lenne, mint a memóriakép mérete, a végén egy valag nullával. Így viszont a text segment mérete továbbra is annyi, de a lementett memóriakép végéről hiányoznak a nullák. (ELF esetén ez úgy mondanám, hogy első esetben filesz = memsz, második esetben filesz + bss = memsz).
- A hozzászóláshoz be kell jelentkezni
Egen, pont ezen gondolkodtam hogy mennyire lenne "csalas" sajat linker scriptet hasznalni. Talan annyira nem, mert a picolibc eseten is, a "von Neumann" linkelesnel sajatot hasznalok. Ott leginkabb azert mert az alapertelmezesben (lasd: /usr/lib/picolibc/riscv64-unknown-elf/lib/picolibc.ld, a sztenderd csomagolt picolibc linker szkript) az eleve definial egy "flash" meg "ram" memory region-t, es most itt nalam nem Harvard jellegu linkeles kell (es persze a -Wl,--defsym=__flash=0x20000000 trukkok sem mukodnek).
De az a baj hogy sajat linker script eseten is modositani kellene a gyari crt0.o-t mert szemmel lathatolag pont ez az altalad is emlitett __attribute__((section(".text.start"))) maradt ki:
/usr/local/xpack-riscv-none-elf/riscv-none-elf/lib/rv32imc/ilp32$ readelf -S crt0.o
There are 9 section headers, starting at offset 0x39c:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 000034 000052 00 AX 0 0 2
[ 2] .rela.text RELA 00000000 000238 000120 0c I 6 1 4
[...]
Mig picolibc eseten ez rendbenlevonek tunik:
/usr/lib/picolibc/riscv64-unknown-elf/lib/rv32imac/ilp32$ readelf -S crt0.o
There are 18 section headers, starting at offset 0xd2c:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 000034 000000 00 AX 0 0 2
[ 2] .text.init.enter PROGBITS 00000000 000034 000090 00 AX 0 0 2
[ 3] .rela.text.i[...] RELA 00000000 000790 000240 0c I 15 2 4
[...]
Azaz ezutobbiban a .text.init-ben van, mig a newlib-ben pedig csak ugy siman a .text-ben. Most meg ezt a szalat sikerult megtalalnom relative nagynehezen a temaban, de itt sem vannak beljebb :/ Es nem tudjak eldonteni hogy mi a helyzet. ARM eseten pedig nem baj ha a _start nem .text elejen van mert a vektortablat igyisugyis letre kell hoznod (olyannyira hogy ugye az kerul a .text elejere), es majd azt linkeled be a vektortabla[1]-be ahova kerul a _start).
Szoval sajat crt0-t meg C eseten csakcsak irogatok (szoktam is, pl AVR-nel), de C++ eseten mar nem csinalnek. (( Es pont azert is kezdtem nezegetni az xPack-ot, mert ott csomagolnak libstdc++ konyvtarat is minden rv32-es archra. Csomagolt Debian alatt meg nem latok embedded rv32 + c++ kombinaciot, se picolibc, se newlib esetben. ))
- A hozzászóláshoz be kell jelentkezni
De az a baj hogy sajat linker script eseten is modositani kellene a gyari crt0.o-t mert szemmel lathatolag pont ez az altalad is emlitett __attribute__((section(".text.start"))) maradt ki
Nem kell, ilyenkor használd a KEEP(crt0.o(.text))
formát (az nem derül ki a kimenetből, hogy vannak-e más függvények is benne, de ha megfelel a szabványnak, akkor nem kéne, hogy a crt0.o-ban bármi más legyen).
Ja, és legyél biztos, hogy megfelelőek a linker kapcsolóid, mert statikus címre linkelés esetén nem szabadna, hogy a végeredményben "rela" rekordjaid legyenek. Mivel a kódod rom-ból fut, ezeket ugyanis senki sem fogja feloldani neked.
Mig picolibc eseten ez rendbenlevonek tunik
Ez nem C, hanem C++ szabvány. Ott előírás, hogy a global constructor-oknak a ".text.init" section-be kell kerülniük, a global destructor-oknak meg a ".text.fini" section-be. A picolibc-t gondolom úgy írták, hogy kompatíbilis legyen a C++-al is, míg a newlib-nél ez nem volt szempont.
(Megjegyzés, Darwin alatt egy tök másik szabvány terjedt el, de ez is előjöhet libc-k esetén: ott a global constructor-ok és global destructor-ok címei két tömbbe kerülnek, és a crt0.o-ban van kötelezően két ciklus, ami végigiterál ezeken, az első a main hívása előtt, a második utánna.)
ARM eseten pedig nem baj ha a _start nem .text elejen van mert a vektortablat igyisugyis letre kell hoznod (olyannyira hogy ugye az kerul a .text elejere), es majd azt linkeled be a vektortabla[1]-be ahova kerul a _start).
Ez nem így van. Erősen chipgyártó függő, nincs olyan, hogy ARM szabvány. RPi alatt például a _start-nak kell a text legelején lennie, és a vektortábla kezdőcíme egyébként meg a VBAR rendszerregiszterben található, és bárhol lehet.
- A hozzászóláshoz be kell jelentkezni
Nem kell, ilyenkor használd a
KEEP(crt0.o(.text))
formát
Ezt hol? A sajat linker scriptben is feluldefinialhatom hogy a crt0.o legyen egy masik szegmensben? De akkor pont nem a "keep" mint szakkifejezes lenne logikus :)
(az nem derül ki a kimenetből, hogy vannak-e más függvények is benne, de ha megfelel a szabványnak, akkor nem kéne, hogy a crt0.o-ban bármi más legyen).
Nem, halistennek nincs benne mas. Ez oke.
Ja, és legyél biztos, hogy megfelelőek a linker kapcsolóid, mert statikus címre linkelés esetén nem szabadna, hogy a végeredményben "rela" rekordjaid legyenek.
Ezzel nincs gond, a vegeredmenyben (main.elf) nincsenek mar .rela.* szegmensek. Amit fentebb bemasoltam az a crt0.o-k felepitese.
(Megjegyzés, Darwin alatt egy tök másik szabvány terjedt el, de ez is előjöhet libc-k esetén: ott a global constructor-ok és global destructor-ok címei két tömbbe kerülnek, és a crt0.o-ban van kötelezően két ciklus, ami végigiterál ezeken, az első a main hívása előtt, a második utánna.)
Ez viszont itt is igy van: itt is vegigszalad a _start a __libc_init_array-n a main() elott es a __libc_fini_array-n a main() utan. Ez newlib-ben is, meg picolibc-ben is van mar. Szoval C-ben is tudok csinalni global konstruktorokat, azzal nincs gond (pl feluldefinialni az stdout-ot majd igy probalom meg, hogy pl a printf() egy eloredefinialt UART-ra irjon, ha ugy hozza a sors).
Ez nem így van. Erősen chipgyártó függő, nincs olyan, hogy ARM szabvány. RPi alatt például a _start-nak kell a text legelején lennie, és a vektortábla kezdőcíme egyébként meg a VBAR rendszerregiszterben található, és bárhol lehet.
Igen, jogos, akkor azt mondom hogy Cortex-M architekturakon :) Cortex-A az valoban lehet teljesen mas, de azt nem ismerem bare metal szinten. Cortex-M-nel alapesetben a 0-s cimen mindig a vektortabla van, es akkor azt implementaciofuggoen vagy atmappeled mashova (Cortex-M0), vagy VTOR-ral atirod ha szukseges es/vagy letezik egyatalan a VTOR.
- A hozzászóláshoz be kell jelentkezni
Ezt hol? A sajat linker scriptben is feluldefinialhatom hogy a crt0.o legyen egy masik szegmensben?
Hát a saját linker scriptedben. Nem definiálsz felül semmit, egyszerűen csak megadod, hogy milyen sorrendben pakolássza be a section-öket a segment-be. De igen, akár rakhatod egy tök másik szegmensbe is, szabad kezed van. (Ha ELF kimenetet generálsz például, akkor ezek virtuális szegmensek csak, és akárhány lehet belőlük, és csak a PHDRS blokkban sorolod be őket tényleges szegmensekké.)
Ja, jut eszembe, olyan is szokott lenni, hogy amikor csak .text van a crt0.o-ban, akkor mellette van még több más crtX.o fájl is. Ekkor az összeszerkesztés sorrendje crt0.o, crti.o, crtbegin.o, (minden más .o), crtend.o, crtn.o. Ilyenkor ha csak a crt0.o-t szerepelteted a linker scriptedben, vagy nem ezt a sorrendet használod, akkor nem lesz jó.
De akkor pont nem a "keep" mint szakkifejezes lenne logikus :)
Mondom, azt értsd úgy, hogy a section relatív pozíciója a segment-en belül. A segment-en belüli címe az, ami "keep", szóval az nem változhat. De csak a biztonság kedvéért raktam bele, valószínűleg nem kell (de megéri berakni, mert abban a nagyon ritka esetben, ha mégis kéne, de nincs, na az szívás).
- A hozzászóláshoz be kell jelentkezni
Ja, jut eszembe, olyan is szokott lenni, hogy amikor csak .text van a crt0.o-ban, akkor mellette van még több más crtX.o fájl is. Ekkor az összeszerkesztés sorrendje crt0.o, crti.o, crtbegin.o, (minden más .o), crtend.o, crtn.o. Ilyenkor ha csak a crt0.o-t szerepelteted a linker scriptedben, vagy nem ezt a sorrendet használod, akkor nem lesz jó.
Ah, koszi, igen, ez hasznos. Ha jol latom ezeket (crti, crtbegin ...) a libgcc tudja beletenni, ha epp, es csak a crtbegin-ben van "erdemi" tartalom. De aztan persze lehet a barmi masban is, mas architekturakon.
- A hozzászóláshoz be kell jelentkezni
Kozben meg a ((section(...))) trukkozessel sikerult egy ilyesmit kifundalni:
void __attribute__((section(".text.unlikely"), naked, used, retain)) _startup(void);
void _start(void);
void _startup(void)
{
/* __asm__ ("j _start\n"); */
_start();
}
Hogy miert unlikely, na az is egy jo dilemma, de az xPack eseten a gyari linker script (elf32lriscv.xn) az ilyen:
.text :
{
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
*(.text.exit .text.exit.*)
*(.text.startup .text.startup.*)
*(.text.hot .text.hot.*)
*(SORT(.text.sorted.*))
*(.text .stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf.em. */
*(.gnu.warning)
}
Az osszes tobbi (.startup, .start, .init) az nem jott be, maradt az unlikely...
Az mas kerdes hogy ez sem jo megoldas, mert pl ez a newlib-es libc szereti hasznalni az ecall-okat ugy bele a vakvilagba, ami meg szinten tobb egyeb kerdest vet fel :/ de az mar (kicsit) mas tema.
- A hozzászóláshoz be kell jelentkezni
Na ez tök kafa, ha jól értem, így maradtál a gyári linker scriptnél, ami jó, mert kevesebb szívás.
Ja, egy megjegyzés: mivel fix toolchain-t használsz, ezért valószínűleg nálad nem gond, de én belefutottam már abba, hogy nem minden linker szereti a "naked" attribútumot (*khm* LLVM/lld).
Hosszas trial-and-error után ez oldotta meg nálam, hogy naked nélkül is garantáltan ne legyen function prologue se function epilogue (gcc-vel is megy):
void __attribute__((noreturn)) _startup(void) {
/* inline assembly-vel kezelve a vermet és a _start hívást meg a visszatérést */
asm volatile(...);
__builtin_unreachable();
}
Iszonyat csúnya, de mivel híváskor nincs még verem, így az inline assembly rész az, ahol a vermet beállítom, emiatt pedig ugye nem lehet function prologue, mert az már használni akarná.
- A hozzászóláshoz be kell jelentkezni
Na ez tök kafa, ha jól értem, így maradtál a gyári linker scriptnél, ami jó, mert kevesebb szívás.
Igenigen, alapjaraton szeret(n)ek maradni a gyari dolgoknal - mert aztan meg ha a sajat herkelmeny mukodik is, nehogy kesobb ebbol legyen a meglepetes :) C-nel meg csakcsak de C++ eseten... na, azt nehez kinyomozni egy headless rendszer eseten :]
Iszonyat csúnya, de mivel híváskor nincs még verem, így az inline assembly rész az, ahol a vermet beállítom, emiatt pedig ugye nem lehet function prologue, mert az már használni akarná.
Uh, de jo hogy mondod ezt, koszi! Merthogy most ezalapjan szurtam ki most hogy ez az xPack-os newlib/nano/nosys-ben levo crt0.so az _nem_ inicializalja az sp-t. Ami ugye feltetelezhetoen onnan jon hogy a newlib (eredetileg?) ARM Cortex-M-ekhez keszult - es ott ugye ezt nem kell mert a vektortabla[0] tartalmazza es az ARM core az onnan be kell hogy rantsa megmielott a vektortabla[1]-re ugrik. De RISC-V eseten az sp (x2) az inicializalatlan bekapcsolas utan (definicio szerint) es csak a pc vesz fel egy (egyebkent implementaciofuggo, azaz a processzorba vasalt) erteket.
- A hozzászóláshoz be kell jelentkezni