2012-03-13 5 views
43

Non sono nuovo al bootloader e al sistema SW, ma non conosco l'origine del motivo per cui il programma generale inizia da 0x8000. So già che l'indirizzo 0x8000 è stato utilizzato come indirizzo iniziale nel normale programma C/C++.Perché il programma generale di solito inizia da 0x8000?

La dimensione minima del bootloader per un programma generale è pari a 0x8000? O è la dimensione minima del blocco della ROM che dovrebbe essere allocata al bootloader 32KB? O c'è un'altra ragione?

Mi piacerebbe sapere questo, storicamente o logicamente, e da un punto di vista di indirizzo virtuale.


Apprezzo tutto, il vostro tempo e l'aiuto con questo. Per rendere più chiara la domanda, la domanda è correlata all'indirizzo virtuale non fisico.

Sono sostanzialmente d'accordo con l'opinione di R dal punto di vista dell'indirizzo di memoria fisica.

Senza dire un sistema specifico che è diverso, per esempio linux (anche in Android), RTOS generale (nucleo, e gli altri, in particolare sezione linker ARM), tutti usano l'indirizzo 0x8000 come programma generale di indirizzo iniziale. di nome crt_begin.o, crt.o, ecc. Localizzato a 0x0 con il caricatore esistono in quest'area.

Quindi suppongo che la dimensione minima del bootloader per il programma generale sia di 32 KB considerando la dimensione del blocco se si troverebbe in BootROM in fase di avvio (avvio a freddo).

Ummm, ma non sono sicuro ...

+16

Di quale sistema stai parlando qui? –

+2

Non ho alcuna fonte affidabile per questo, ma posso fare una supposizione qualificata. Storicamente molti processori, in particolare 8 bit, hanno avuto la funzione chiamata [zero page] (http://en.wikipedia.org/wiki/Zero_page) il che significa che le celle di memoria agli indirizzi 0x00 - 0xFF avevano supporto di istruzioni per eseguire più velocemente. Credo che questo sia stato introdotto da Motorola in quei giorni, poiché avevano i registri I/O mappati in memoria sui vecchi MCU come 6800. -> – Lundin

+2

Perciò vorrai che questa prima area di memoria sia occupata da celle RAM o registri speciali . Ha quindi senso che la parte dello spazio degli indirizzi che viene dopo la pagina zero sia della stessa natura: RAM e/o registri. Ciò richiederebbe molto kb, forse fino a 0x6000 o alcuni di questi. Suppongo quindi che fosse comodo inserire la ROM (memoria di programma) in un indirizzo pari e 0x8000 era conveniente. Sono abbastanza sicuro che la risposta a questa domanda possa essere trovata nei primi progetti dei processori Motorola. – Lundin

risposta

19

In generale, su tutti, ma i più piccoli sistemi embedded, la piattaforma ABI progettista vuole evitare di dover mai gli indirizzi più bassi in uso in modo che dereferenziazioni di puntatori nulli possono essere intrappolati. Avere diversi KB di indirizzi non validi ti dà ulteriore sicurezza se il puntatore nullo viene dereferenziato con un array o un offset di membro della struttura, come in null_ptr->some_member.

+3

Non credo che questo sia il motivo, ho lavorato con diversi sistemi embedded in cui l'indirizzo 0 è memoria valida e indirizzabile, mentre allo stesso tempo la NVM parte da 8000. – Lundin

+5

... in particolare poiché l'indirizzo 0x8000 esisteva prima del C la lingua e i puntatori NULL divennero popolari. Forse anche prima che C fosse inventato? – Lundin

+0

Da quello che ho capito, non vuoi che i "veri" puntatori siano 0, mai, anche su sistemi in cui l'HW funziona correttamente. Quindi, se 'malloc()' restituisce 0, sai che è fallito. Pertanto, nei sistemi in cui l'indirizzo 0 non viene intercettato, la memoria viene in genere allocata per scopi specifici quali i gestori di interrupt. – MSalters

6

Dipende dal sistema e i programmi partono da indirizzi diversi su diversi sistemi. Sotto Unix, è normale (o forse richiesto da Posix) utilizzare l'indirizzo 0 come puntatore nullo e non mappare la prima pagina della memoria virtuale , in modo che il riferimento di un puntatore nullo a comporti una violazione del segmento. Ho il sospetto che altri sistemi che utilizzano l'indirizzo come un puntatore nullo si comportino in modo simile (ma quanto possono riservare possono variare). (Storicamente, era normale mappare la prima pagina come letto solo , e riempirla di zeri, fare in modo che un puntatore nullo si comportasse come se fosse una stringa vuota, un puntatore a "". Si tratta di circa 25 anni , tuttavia.)

mi aspetterei che anche oggi, alcuni sistemi embedded vengono caricati il ​​programma partire dall'indirizzo 0.

2

avrei il sospetto che in molti casi il primo 32K è stata riservata per i monitor utilizzo di codice/ram. In molte schede di valutazione 8051 non era insolito impostare come predefinito 0x1000 o 0x2000 per tutte le app a seconda del monitor residente (alcune delle quali funzionavano anche come debugger).

32K potrebbe essere lo spazio per caricatori u-boot/etc.

3

È un po 'arbitrario e su Linux, almeno deciso dal linker. L'idea generale è di riservare dello spazio per catturare le eccezioni del puntatore NULL. Per aiutare a prevenire il doloferimento del puntatore NULL dello spazio del kernel dall'esecuzione di codice utente arbitrario in modalità kernel, Linux impedisce di mappare la parte inferiore della memoria. /proc/sys/vm/mmap_min_addr controlla l'indirizzo più basso che puoi mappare (puoi cambiarlo a 0 e mappare una pagina a 0 se lo desideri).

Su linux è possibile osservare la mappatura della memoria guardando in /proc. Ad esempio,

genwitt ~> cat /proc/self/maps 
00400000-0040c000 r-xp 00000000 08:01 354804        /bin/cat 
0060b000-0060c000 r--p 0000b000 08:01 354804        /bin/cat 
0060c000-0060d000 rw-p 0000c000 08:01 354804        /bin/cat 
01dda000-01dfb000 rw-p 00000000 00:00 0         [heap] 
7f5b25913000-7f5b25a97000 r-xp 00000000 08:01 435953      /lib64/libc-2.14.1.so 
7f5b25a97000-7f5b25c97000 ---p 00184000 08:01 435953      /lib64/libc-2.14.1.so 
7f5b25c97000-7f5b25c9b000 r--p 00184000 08:01 435953      /lib64/libc-2.14.1.so 
7f5b25c9b000-7f5b25c9c000 rw-p 00188000 08:01 435953      /lib64/libc-2.14.1.so 
7f5b25c9c000-7f5b25ca1000 rw-p 00000000 00:00 0 
7f5b25ca1000-7f5b25cc2000 r-xp 00000000 08:01 436061      /lib64/ld-2.14.1.so 
7f5b25cd2000-7f5b25e97000 r--p 00000000 08:01 126248      /usr/lib64/locale/locale-archive 
7f5b25e97000-7f5b25e9a000 rw-p 00000000 00:00 0 
7f5b25ec0000-7f5b25ec1000 rw-p 00000000 00:00 0 
7f5b25ec1000-7f5b25ec2000 r--p 00020000 08:01 436061      /lib64/ld-2.14.1.so 
7f5b25ec2000-7f5b25ec3000 rw-p 00021000 08:01 436061      /lib64/ld-2.14.1.so 
7f5b25ec3000-7f5b25ec4000 rw-p 00000000 00:00 0 
7fff18c37000-7fff18c58000 rw-p 00000000 00:00 0       [stack] 
7fff18d0c000-7fff18d0d000 r-xp 00000000 00:00 0       [vdso] 
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0     [vsyscall]
2

Credo che la risposta sia più correlata alla gestione degli interrupt. Gli indirizzi del gestore di interrupt sono impostati nell'hardware. In Intel 8086, c'era una tabella di traduzione diretta sul codice del gestore di interrupt e la corrispondente routine di gestione degli interrupt. Probabilmente, questo è stato fatto da un circuito combinatorio e quindi, per preservare la compatibilità diretta, sarebbe stato più sensato metterli all'inizio della memoria piuttosto che alla fine per evitare ogni volta le modifiche. Quindi, l'indirizzo di inizio dell'esecuzione si trova all'altra estremità della memoria. Inoltre, era necessario che un codice sufficiente fosse contenuto in quel blocco per caricare un programma di segmento di memoria e un'istruzione di salto per passare all'esecuzione del codice da quell'indirizzo di codice.