2012-02-15 16 views
7

Quando si costruisce un progetto mcu in bare metal basato su gcc, è necessario occuparsi dell'inizializzazione delle sezioni .data e .bss durante l'avvio.Come faccio a sapere da dove la sezione .data deve ottenere i dati di init? (gcc linker)

La sezione .bss è abbastanza semplice, visto che basta compilare l'intera sezione a 0. Ma variabili nella sezione .data ha bisogno di avere i loro dati di inizializzazione in rom/flash e copiati durante l'avvio.

Come sapere dove sono disponibili i dati con i valori di inizializzazione?

Facciamo un esempio.

Diciamo che creo due variabili globali in main.c

unsigned int my_global_variable_one = 1; 
unsigned int my_global_variable_two = 2; 

poi posso usare objdump sul file oggetto per vedere in quale sezione saranno in, ma non riesco a trovare nulla nell'objdump out put dove devono essere inseriti i dati di init.

$ arm-none-eabi-objdump --syms main.o | grep my_global_variable 
00000000 g  O .data 00000004 my_global_variable_one 
00000004 g  O .data 00000004 my_global_variable_two 

Poi posso guardare l'elfo risultante per l'intero sistema, in questo caso main.elf.

$ arm-none-eabi-nm -n main.elf | grep my_global_variable 
20000000 D my_global_variable_one 
20000004 D my_global_variable_two 

Dove posso trovare dove sono, quindi posso copiare i dati? Cosa devo inserire nel mio script linker?

Dovrebbe essere in qualcosa come .text o .rodata, ma come faccio a saperlo?

Come posso controllare dove sono i dati di init per my_global_variable_one?

Posso trovare dove sono questi dati con uno qualsiasi dei comandi binutils come readelf o objdump?

/Grazie


Questo è uno stm32 (Cortex M3) MCU, e la versione CodeBench di gcc viene utilizzato.

risposta

9

Il compilatore inserisce tutto il codice e alcuni dati di sola lettura nella sezione .text. Potrebbe anche esserci una sezione .rodata. Si può avere lo script del linker che mettere in un indirizzo di ROM qualcosa di simile:

. = <rom-base-address>; 
.rodata : { *(.rodata) } 
<other read-only sections go here> 
.text : { *(.text) } 

Il compilatore mette tutti i valori iniziali dei dati scrivibili nella sezione .data, e tutti i simboli che non hanno un valore iniziale in .bss. Il .bss è facile, basta inserirlo nella RAM. Il .data vuole essere in RAM in fase di esecuzione, ma in ROM con carico-tempo, e lo script del linker ti permette di farlo con la parola chiave AT:

. = <ram-base-address>; 
.bss : { *(.bss) } 
.data : AT (ADDR (.text) + SIZEOF (.text)) 
     { *(.data) } 

Ciò significa che la sezione di dati ha ora un diverso LMA e VMA (carica indirizzo di memoria/indirizzo di memoria virtuale). Il tuo programma si aspetta di trovare i dati nel VMA (l'indirizzo di runtime) ma i dati saranno effettivamente presenti nella LMA (potresti dover insegnare al tuo lampeggiatore per farlo), che è subito dopo la sezione .text.

Se è necessario capire dove copiare e da cui è possibile creare i puntatori nello script del linker. Quindi, modificare l'esempio precedente come questo:

.data : AT (ADDR (.text) + SIZEOF (.text)) 
     { _data_lma = LOADADDR(.data); _data_vma = .; 
      *(.data); 
      _data_size = SIZEOF (.data);} 

È quindi possibile fare memcpy (_data_vma, _data_lma, _data_size) nel codice di start-up (anche se si potrebbe avere a portata di mano-codice in assembler?) I simboli che apparirà al vostro programma come costante Globali che risolvono al momento del collegamento. Così, nel codice assembler si potrebbe avere qualcosa di simile:

_data_lma_k: 
    .long _data_lma 

Poi il numero sarà difficile codificato nella sezione .text. Ovviamente, la sintassi dell'assembler potrebbe essere diversa sulla tua piattaforma.

Per ulteriori informazioni, consultare il manuale di linker here

+0

Beh ho notato che i dati viene aggiunto dopo la sezione .text, ma mi ha aiutato a ottenere abbastanza vicino :) – Johan