ld: warning: main.elf has a LOAD segment with RWX permissions

Fórumok

Egy FPGA-s projekt kapcsan futottam bele a fenti uzenetbe. A kontextus: sima C kod forditasa beagyazott kornyezetben. A limitaciok miatt a teljes zubehor, init-estul, text-estul, rodata+data+bss-estul egyetlen RAM blokkba kerul, onnan is fut, azaz nem kulonul el a "flash" es a "ram" mint a szokasos esetekben - es emiatt kapom a targybeli hibauzenetet linkeleskor. Ez igy nyilvan rendben is van, de mivel ez tudatos, szeretnek megszabadulni ettol a figyelmeztetestol. Az interwebeken azt irjak hogy az .init meg a .text (READONLY) attributuma ezt megoldja, de sajna ez nem segit. Egyeb otlet? :) 

A rendszer egyebkent RISC-V + picolibc alapu, igy a hozza tartozo alap linker szkriptet (/usr/lib/picolibc/riscv64-unknown-elf/lib/picolibc.ld) modositottam egy kicsit hogy minden egy RAM blokkba keruljon. De egy kicsit el is vesztam az ilyen .text { ... } >ram AT>ram :text meg hasonlo nyalanksagokban is... 

Thx, A.

Hozzászólások

Szerkesztve: 2025. 03. 09., v – 17:47

Egyeb otlet?

Régen kellett ilyesmi, akkor az ld "-N" kapcsolója megoldotta ezt. Nem tudom, hogy a legújabb verziónál is működik-e még ez a workaround.

A limitaciok miatt a teljes zubehor, init-estul, text-estul, rodata+data+bss-estul egyetlen RAM blokkba kerul, onnan is fut

Egyébként ha úgyis linker scriptből helyezed a RAM-ba a szegmenseket, akkor miért nem használsz két egymásutáni szegmenst és jóccakát? Ha az align-t leveszed 8-ra, akkor még a kihasználatlan memóriaterület is minimális lesz a :text és :data szegmens között, nem veszítesz semmit. Init, text rodata sections megy a :text szegmensbe, data, bss meg a :data szegmensbe, a két szegmens meg egymásután a RAM-ba (úgyis csak az első szegmens kezdőcíme az érdekes, ha jól sejtem).

Másik ötlet, hagyd a francba a MEMORY blokkot, helyette a szegmens kezdőcímét állítsd be, a végeredmény ugyanaz lesz:

SECTIONS
{
    . = SEGMENT_START("text-segment", KEZDOCIM) + SIZEOF_HEADERS;
    .text . : {
        ...
    } :text
        ...

Ha nem akarod, hogy a programfejléc is benne legyen, akkor hagyd ki a SIZEOF_HEADERS-t. Ez akkor kell, ha az elf egy-az-egyben töltődik be a memóriába (azaz az elf fájl első bájtra kerül arra címre), és akkor nem kell, ha a kódszegmens első bájtja kerül arra a címre (vagy azért, mert kraftos az elf betöltő, vagy pedig azért, mert linkelés után objconv-val raw binary-vá alakítod). Hogy kell-e, azon múlik, hogy töltöd be végül.

Nagyon fontos, a szekció definícónál van egy PLUSZ PONT (a ".text" és a kettőspont között), ez helyezi el az adott címre, azaz ez a pont váltja ki a MEMORY-t és a kacsacsőrt.

Régen kellett ilyesmi, akkor az ld "-N" kapcsolója megoldotta ezt. Nem tudom, hogy a legújabb verziónál is működik-e még ez a workaround.

Igen, a `man ld` szerint valami hasonlot kene csinalnia, de itt sajna nem segit :/ Kb ugy hatastalan mint a (READONLY) cucc. 

Egyébként ha úgyis linker scriptből helyezed a RAM-ba a szegmenseket, akkor miért nem használsz két egymásutáni szegmenst és jóccakát? 

Igyis automatikusan egymas utan helyezi el, ezzel nincs gond:

20000000 T __text_start
2000005c T __vectors
[...]
200012a4 T __modsi3
200012f4 T __text_end
200012f8 A __data_source
200012f8 D __bss_start
200012f8 D __data_start
200012f8 D __tls_base
20001af8 D __global_pointer$
200028c8 B __bss_end
20020000 T __stack

Raadasul pont 8-ra alignolva, ahogy mondod, ez van a default linker scriptben is :)

A gond inkabb az hogy tenyleg csak egy MEMORY blokk van:

MEMORY
{
    ram(rwx): ORIGIN = DEFINED(__ram  ) ? __ram   : 0x20000000, LENGTH = DEFINED(__ram_size  ) ? __ram_size   : 0x08000
}

Es ezt elso korben megtartanam igy hogy tukrozze a bus_matrix strukturajat ahogy azt hardveresen kialakitottam. Olyat persze lehetne hogy felosztom igy jelkepesen ket reszre ezt a 0x20000000 <= ... < 0x20020000 tartomanyt, az also az (rx), a felso az (rw) es akkor szevasz. De akkor pont a flexibilitasat vesztem el: neha az kell hogy sok program + keves ram, neha kis program tobb memoriat enne, es akkor folyamatosan mozgatni kene a hatarokat. Nem olyan sok az a 128k... 

Másik ötlet, hagyd a francba a MEMORY blokkot, helyette a szegmens kezdőcímét állítsd be, a végeredmény ugyanaz lesz:

Koszi, ezt megnezem, ezt nem ismertem. Ha ez igy flexibilis marad (nem kell kezzel allitgatnom a hatarokat) akkor kb jo is lehet :)

Olyat persze lehetne hogy felosztom igy jelkepesen ket reszre

Erre semmi szükség.

Koszi, ezt megnezem, ezt nem ismertem. Ha ez igy flexibilis marad (nem kell kezzel allitgatom a hatarokat) akkor kb jo is lehet :)

Így van, pont az a lényeg, hogy csak a legelső szegmens címét állítod, aztán már akárhány blokkod lehet a linker scriptben, azokat szépen egymás utáni címre fogja pakolni. Ennek így tök mindegy, hogy végül hány szegmenst alakítasz ki.

Egyedül az a kérdés, hogy hogyan töltöd majd be: ha az elf-et egy-az-egyben, akkor kell a SIZEOF_HEADERS (és ilyenkor a PHDRS blokkban is meg kell adni, hogy a text szegmens magában foglalja az elf fejléceket)

PHDRS {
  text PT_LOAD FILEHDR PHDRS;
  data PT_LOAD;
}
SECTIONS
{
    . = SEGMENT_START("text-segment", __ram) + SIZEOF_HEADERS;
        ...

Ha azonban csak a szegmenseket töltöd be (mert van elf loader vagy mert raw-á konvertálod a fordítás végén), akkor ezekre semmi szükség.

SECTIONS
{
    . = SEGMENT_START("text-segment", __ram);
    .text . : {
        KEEP(*(.init))
        *(.text .gnu.linkonce.t*)
        *(.rodata .rodata.* .gnu.linkonce.r*)
    } :text
    .data : {
        *(.data .data.* .gnu.linkonce.d*)
    } :data
    .bss (NOLOAD) : {
        *(.bss) *(COMMON)
    } :data
    PROVIDE(__ram_size) = . - __ram;
}

A :text kerül fix címre, a :data egyből utánna, a .bss pedig a :data végére. Elvileg a PHDRS-ben is megadható lenne a kezdőcím AT()-el, de nekem azzal voltak gondjaim LLVM lld alatt, ez a SEGMENT_START() viszont ugyanúgy működik ott is és GNU ld alatt is.
Ilyenkor egyébként hasznos a KEEP, ez gondoskodik arról, hogy az init szekció mindig a :text szegmens legelejére kerüljön, és onnan még véletlenül se rakja át máshova a linker, ezért az init legelső bájtja kerül a __ram címre.

ps: egyébként az a különbség a ". = __ram;" és a ". = SEGMENT_START("text-segment", __ram);" között, hogy az előbbi állítja a memóriabeli címet és a fájloffszetet is, míg utóbbi csak a memóriabeli címet módosítja, a fájloffszetet nem, így akármekkora a cím, nem lesz bitang nagy az elf mérete. Elvileg ugyanerre szolgálna az AT() is.