2009-12-31 1 views
5

Sto usando il linguaggio C su Windows. Questa domanda faceva precedentemente parte di What happens to identifiers in a program?. L'ho rotto per ridurre no. di domande. Questa è una query standalone (non dipende dalla domanda precedente)non chiaro con il lavoro del linker

Se non c'è nulla da collegare (ad esempio non sto usando alcuna libreria, so che non sarà di alcuna utilità.) Il linker cambierà l'output del codice oggetto dell'assembler? Se sì, cosa cambia?

Ho sentito che LINKER esegue anche un'operazione di mappatura della memoria. Non capisco come. Il programma non è in esecuzione, è solo nella fase di produzione. Come potrebbe il linker mappare alla memoria? Come sarebbe? Quali sono tutte le funzioni di LINKER?

Quando le persone fanno riferimento a "relocation", "address binding". Non capisco cosa significano. Che cos'è & qual è il suo scopo?

Alcuni debugger mostrano informazioni come: stack di chiamate: 0xfffef32, 0xf3234fe ecc. È in fase di esecuzione giusto? o sono gli indirizzi di memoria della cosiddetta "mappatura della memoria" del linker?

quando le persone si riferiscono a qualcosa come symbols o symbol table. Significano identificatori (nomi di variabili, nomi costanti, nomi di funzioni)?

Ho cercato informazioni su Internet ma non ho trovato nulla di utile. Può essere che non sono sicuro di cosa cercare. Non voglio leggere grandi libri su questo. Ma se ci sono articoli, tutorial che chiariscono concetti. Sarebbe anche utile.

Sono un programmatore alle prime armi. Quindi, sarebbe bello poterlo spiegare in termini semplici ma tecnici.

+0

Sarebbe utile specificare la lingua e il linker che si sta utilizzando. Linker diversi possono fare cose leggermente diverse. –

risposta

1

Lavorerò con C per questa discussione.

È raro che un programma C non faccia riferimento ad almeno alcune funzioni della libreria; quindi, anche se il tuo codice si trova in un solo modulo (file), solitamente ci saranno riferimenti alle funzioni della libreria. Nella forma compilata del programma, tali riferimenti si trovano in una tabella di riferimenti esterni, ad esempio una tabella in cui i nomi testuali vengono visualizzati insieme alle posizioni nel programma che desiderano fare riferimento a tali indirizzi esterni.

Il lavoro del linker consiste nel concatenare il programma in un singolo file con qualsiasi altro modulo che utilizza e quindi abbinare definizioni esterne in un modulo con riferimenti esterni in un altro, cioè applicare patch a tutti i riferimenti incrociati con l'app in modo tale che le chiamate colpisci gli indirizzi corretti

Anche se non si fa riferimento a nessun modulo esterno, il collegamento probabilmente dovrà convertire alcuni riferimenti relativi nel proprio codice in quelli assoluti; Ad esempio, una volta che "sa" dove nel file si trova il tuo codice, può assegnare gli indirizzi finali corretti alle cose.

4

Quando si compila un file sorgente, in genere viene diviso dal compilatore/assemblatore in diverse sezioni. Come un esempio ipotetico immaginare che le seguenti sezioni vengono utilizzate:

  • .text - contiene tutto il codice eseguibile
  • .CONST - contiene i dati costanti
  • .data - contiene leggere/scrivere dati inizializzati
  • .BSS - contiene lettura/scrittura dati non inizializzati

In un singolo file sorgente, il compilatore/assemblatore alloca la roba adeguata alle sezioni corrispondenti e fornisce i simboli che vengono utilizzati gli offset nella sezione partendo da zero.

Ad esempio:

int i; 
const j = 3; 
int k = 4; 
int l; 
int main() 
{ 
return 1; 
} 

Ciò può comportare la seguente tabella dei simboli:

Symbol Section Offset 
i  .bss 0 
j  .const 0 
k  .data 0 
l  .bss 4 
main .text 0 

Nel file oggetto, oltre alla tabella dei simboli, i dati di ciascuna sezione potrebbe essere mantenuta . In questo esempio, la sezione .text conterrà il codice oggetto per "return 1", la sezione const conterrà 3, la sezione data conterrà 4. La sezione .bss non dovrebbe essere nel file oggetto, perché le variabili non sono stati inizializzati.

La prima cosa che un linker potrebbe fare è concatenare tutte le sezioni del file oggetto di input e regolare di conseguenza gli offset di simbolo.

Ora arriviamo a ciò che viene chiamato "relocation" o "address binding". Diciamo che in un sistema ipotetico, il codice eseguibile inizia all'indirizzo 0x1000. Diciamo anche che le sezioni di dati di un programma vogliono iniziare a un confine di pagina pari dopo il codice eseguibile. Il linker assegnerebbe 0x1000 come base delle sezioni .text concatenate e regolerà tutti i simboli. Quindi la base delle sezioni .const, .data e .bss in modo simile per metterle in luoghi appropriati in memoria.

A volte ci sono riferimenti simbolici in una sezione. Questi riferimenti devono essere aggiornati dal linker per riflettere la posizione finale del simbolo a cui si fa riferimento. Il file oggetto potrebbe contenere "record" di delocalizzazione che sembrano

section offset symbol 
.text 0x1234 foo 

il linker andare ad ogni compensazione in ogni sezione e aggiornare il valore lì a riflettere il valore del simbolo finale.

Al termine, il file oggetto "assoluto" risultante può essere caricato in memoria (al posto giusto, ovviamente!) Ed eseguito.

+0

grazie. E 'stato molto utile. Ma per quanto riguarda le funzioni? Hanno offset simili? In tal caso, come sarebbe calcolato l'offset? quando è necessario fare riferimento a una variabile o chiamare una funzione. Come lo fa? diciamo ipoteticamente se ho bisogno di 'l = k + 2'. Come fa? – Alice

+0

Non sono chiaro: 'Diciamo che in un sistema ipotetico, il codice eseguibile inizia all'indirizzo 0x1000. Cosa intendi con codice eseguibile? .text section? del altro file oggetto libreria? 0x1000 è sfalsato? poco confuso. - Non ho capito: 'Diciamo anche che le sezioni di dati di un programma vogliono iniziare a un confine di pagina pari dopo il codice eseguibile. – Alice

+0

Sì, nel mio codice di esempio eseguibile risiede nella sezione .text (S). sono concatenati insieme durante il collegamento. Gli offset per le funzioni sono regolati come qualsiasi altro simbolo. Sebbene il .l'offset del testo potrebbe iniziare da 0x1000, quando tutte le sezioni .text sono combinate, la dimensione finale potrebbe essere molto più grande di quella. Se il codice contiene l = k + 2, il codice oggetto che esegue l'operazione avrà gli indirizzi di l e k regolati (a causa dei record di rilocazione) sui valori calcolati finali. –

1

Non una risposta, solo un suggerimento: acquistare "Linkers and Loaders", leggerlo alcune volte. È straordinariamente utile.

+1

Un ottimo libro: l'autore ha reso disponibili online i capitoli del manoscritto originale all'indirizzo http://www.iecc.com/linker/ –

+0

"poche volte" ?? Dio mio! È così difficile? – Alice

+1

@ Link I linker sono concettualmente abbastanza semplici, ma abbastanza complessi nella pratica. E ogni buon libro merita di essere letto più di una volta. –

1

Se non c'è nulla da collegare (ad esempio non sto utilizzando alcuna libreria, so che non sarà di alcuna utilità.) Il linker cambierà l'output del codice oggetto dell'assembler? Se sì, cosa cambia?

Collega sempre un po 'di codice di inizializzazione. Puoi provare questo, scrivere un programma vuoto e collegarlo, quindi utilizzare objdump -d per disassemblarlo.

Ho sentito che LINKER esegue anche un'operazione di mappatura della memoria. Non capisco come. Il programma non è in esecuzione, è solo nella fase di produzione. Come potrebbe il linker mappare alla memoria? Come sarebbe? Quali sono tutte le funzioni di LINKER?

Ogni sistema ha un layout di memoria che i programmi eseguibili devono seguire per funzionare. Specifica dove vanno le diverse parti del programma (almeno codice, dati inizializzati, dati inizializzati a zero). Il linker deve produrre l'eseguibile in base a queste regole, che variano tra i sistemi, ad es. Windows e Linux.Sui sistemi embedded diventa ancora più interessante, lì il programma è in genere in memoria di sola lettura (Flash) ei dati sono nella RAM, e ci sono intervalli di indirizzi fissi per i diversi tipi di memoria a seconda del tipo di microcontrollore.

Quando le persone fanno riferimento a "relocation", "address binding". Non capisco cosa significano. Che cos'è & qual è il suo scopo?

Binding in generale significa dare un valore a un nome, in questo caso un indirizzo al simbolo per una funzione o variabile globale.

Per quanto riguarda il riposizionamento, in genere si collegano più di un file oggetto e ogni file oggetto specifica gli indirizzi come offset relativi al suo inizio. Quando li metti insieme ognuno ottiene il proprio intervallo di indirizzi e il linker calcola l'indirizzo per un simbolo mappando l'offset nell'intervallo di indirizzi. Questo è chiamato trasferimento.

Alcuni debugger mostrano informazioni come: stack di chiamate: 0xfffef32, 0xf3234fe ecc. È in fase di esecuzione giusto? o sono gli indirizzi di memoria della cosiddetta "mappatura della memoria" del linker?

Questo 0xfffef32 sarebbe un indirizzo tipico nello stack, poiché lo stack di solito è posizionato nella parte superiore della memoria e cresce verso il basso. Lo stack viene utilizzato per gli indirizzi di ritorno, le variabili locali e i parametri delle funzioni effettive. Questi sono locali e memorizzati in indirizzi relativi al puntatore dello stack, quindi non sono gestiti tipicamente dal linker, piuttosto il compilatore conosce già gli offset da usare e li inserisce nel codice assembly.

quando le persone si riferiscono a qualcosa come simboli o tabella dei simboli. Significano identificatori (nomi di variabili, nomi costanti, nomi di funzioni)?

La tabella dei simboli è una tabella che associa i simboli ai valori (numeri, offset, indirizzi). Ci sono alcuni simboli per i tuoi identificatori, ma anche altri per altri usi. I tuoi identificatori possono essere modificati più o meno per diventare simboli, principalmente per evitare conflitti tra nomi (ad esempio la preposizione di "_").

Il linker ha un'opzione --print-map per stampare la tabella dei simboli. Puoi usare -Wl, - print-map se usi gcc per il collegamento.

Se ti piace questo tipo di materiale tecnico di basso livello, dovresti dare un'occhiata alla programmazione incorporata, cioè i microcontrollori di programmazione che sono usati in vari dispositivi elettrici. Per i sistemi desktop come Windows normalmente non è necessario consultare questo tipo di dettagli.