OT hadanka s (GNU) linker skriptom

Jan Waclawek konfera na efton.sk
Úterý Prosinec 14 20:21:00 CET 2010


Nasledujuca dlha a nudna rozpravka sa tyka GNU prostriedkov gcc/ld a hadanka je zaujimava len pre zarytych privrzencov GNU prostriedkov (ano, je urcena priamo pre pana kolegu MilanaB, aj ked samozrejme nie su vyluceni ani ostatni zaujemcovia o dlhe a nudne hadanky).

Toto chce trocha uvodu: ARM Cortex-M3 (okrem inych) ma tzv. bit-banding, t.j. urcita cast adresneho priestoru je odzrkadlena bit po bite do najnizsich bitov celych slov v inej (tzv. alias) casti adresneho priestoru, napr. piaty bit v byte z adresy 0x2000.0000 sa da nulovat/nastavit zapisanim nuly/jednicky na adresu 0x2200.0014 (bytova adresa piateho 4-bytoveho slova v "alias" oblasti). Bol to zrejme pokus o uspokojenie tradicnych '51-tkovych programatorov, alebo tak nejak. 


Chcel som si to vyskusat na jednom mensom poli, 
uint8_t AHB_SRAM a[ARRAY_SIZE];

pricom AHB_SRAM je makro (typu __attribute__((__section__(".ahb_ram")))) ktorym sa umiestni pole do bit-band-ovanej oblasti (normalne su totiz premenne v inej, ne-bit-band-ovanej casti SRAM).

Potreboval som teda z adresy pola vytvorit adresu v tej alias oblasti. Na to je vzorec typu (((a.byte - 0x20000000) << 5) + 0x22000000) + (a.bit << 2), alebo tak nejak. Potial je to jednoduche - clovek si spravi prislusne makro, napr.
#define LPC_SRAM_BITBAND_BASE (0x20000000UL)
#define LPC_SRAM_ALIAS_BASE   (0x22000000UL)
#define LPC_SRAM_ALIASED(a)   ((((a) - LPC_SRAM_BITBAND_BASE) * 32) + LPC_SRAM_ALIAS_BASE)

Chcel som vytvorit pole ukazovatelov do alias oblasti, aby som ich potom v pripade potreby (akoze taku potrebu naozaj mam) mohol poprehadzovat; takze som urobil nieco ako:
uint8_t * p[] = {
  LPC_SRAM_ALIASED((uint32_t)&a[0]) + 0 * 4,
  LPC_SRAM_ALIASED((uint32_t)&a[0]) + 1 * 4,
  LPC_SRAM_ALIASED((uint32_t)&a[0]) + 2 * 4,
};

ale na toto kompilator bude frflat nieco v zmysle, ze inicializator nie je konstanta.

Tu prichadza otazka c. 1: preco?

Na zmatenie dodam, ze ak by inicializator vyzeral nasledovne:

(uint32_t)&a[0]) + 0 * 4,

tak ho kompilator akceptuje; akurat ze to samozrejme neukazuje na spravnu (alias) adresu na ktoru chceme.

--- Prezradim, preco.

Adresu pola kompilator nevie, pretoze sa urci az pri linkovani. Kompilator vsak vie do pola ulozit znacky pre kompilator, ze ich ma nahradit pri linkovani urcenym symbolom (v tomto pripade adresou); to vie urobit aj s konstantnym ofsetom (lebo linker ma taku vlastnost), ale nevie to urobit s lubovolnym vyrazom (lebo linker taku vlastnost nema).

Kedze som nechcel (z cvicnych dovodov) pole umiestnovat na pevnu adresu, rozhodol som sa, ze ten vyraz dam vypocitat linkeru - linker predsa v linker skripte vie pocitat vyrazy, vie ich priradit symbolom, a ja take symboly mozem pouzit v programe. Pole som si musel umiestnit do separatnej vstupnej sekcie:

uint8_t __attribute__((__section__(.a))) a[ARRAY_SIZE];
extern uint8_t _a_alias;
uint8_t * p[] = {
  (uint32_t)&_a_alias + 0 * 4,
  (uint32_t)&_a_alias + 1 * 4,
  (uint32_t)&_a_alias + 2 * 4,
};

a do linker skriptu pribudlo:

MEMORY
{
    AHB_RAM  (rwx) : ORIGIN = 0x2007C000, LENGTH = 0x00008000
}

SECTIONS
{
    .ahb_ram (NOLOAD):
    {
        *(.ahb_ram*)
        PROVIDE (_a_alias = ((. - 0x20000000) << 5) + 0x22000000);
        *(.a)
    } > AHB_RAM
}

Vysledkom je vsak mapa:
.ahb_ram        0x2007c000       0x1c
 *(.ahb_ram*)
                0x4207c000                PROVIDE (_a_alias, (((. - 0x20000000) << 0x5) + 0x22000000))
 *(.a)
 .a
                0x2007c000       0x1c main.o
                0x2007c000                a

a naozaj, konstanty v inicializatore su 0x4207c000, 0x4207c004, 0x4207c008. 
No, lenze ak si rucne spocitam ten vzorcek, tak mi vychadza 0x22F80000...

Otazka c. 2: preco linker nespravne vypocital ten vyraz?
Otazka c. 3: co s tym?

wek




Další informace o konferenci Hw-list