2012-03-13 19 views
43

Nie jestem nowy dla bootloadera i systemu SW, ale nie znam źródła powodu, dla którego program ogólny zaczyna się od 0x8000. Wiem już, że adres 0x8000 został użyty jako adres początkowy w normalnym programie C/C++.Dlaczego ogólny program zazwyczaj zaczyna się od 0x8000?

Czy minimalny rozmiar bootloadera dla programu ogólnego wynosi do 0x8000? Czy jest to minimalny rozmiar bloku ROM, który należy przydzielić do bootloadera 32KB? Czy jest jeszcze inny powód?

Chciałbym o tym wiedzieć, historycznie lub logicznie, oraz z punktu widzenia wirtualnego adresu.


Doceniam wszystkich, twój czas i pomoc w tym. Aby wyjaśnić pytanie, pytanie dotyczy adresu wirtualnego, a nie fizycznego.

Zasadniczo zgadzam się z opinią R z punktu widzenia pamięci fizycznej.

Bez mówienia konkretnego systemu, który jest różnorodny, na przykład linux (nawet w Androidzie), ogólny RTOS (jądro i inne, szczególnie sekcja łącznika ARM), wszystkie używają adresu 0x8000 jako ogólnego programu adresu startowego. takie o nazwie crt_begin.o, crt.o, itp. Znajdujące się w 0x0 z programem ładującym istnieją w tym obszarze.

Dlatego domyślny minimalny rozmiar bootloadera dla programu ogólnego to 32KB, biorąc pod uwagę rozmiar bloku, jeśli byłby on umieszczony w BootROM podczas startu systemu (cold boot).

Hmmm, ale nie jestem pewien ...

+16

O jakim systemie tu mówisz? –

+2

Nie mam żadnego wiarygodnego źródła tego, ale mogę zgadnąć. Historycznie wiele procesorów, w szczególności 8-bitowych, miało funkcję o nazwie [strona zerowa] (http://en.wikipedia.org/wiki/Zero_page), co oznacza, że ​​komórki pamięci pod adresami 0x00 - 0xFF miały obsługę instrukcji do wykonać szybciej. Wierzę, że zostało to wprowadzone przez firmę Motorola w tamtych czasach, ponieważ posiadały one odwzorowane w pamięci rejestry I/O na starych MCU, takich jak 6800. -> – Lundin

+2

Dlatego chcesz, aby ten pierwszy obszar pamięci był zajęty przez komórki pamięci RAM lub specjalne rejestry . Następnie ma sens, że część przestrzeni adresowej, która pochodzi po stronie zerowej, ma taki sam charakter: pamięć RAM i/lub rejestry. Zajmie to dużo kb, może nawet do 0x6000 lub trochę takich. Następnie zakładam, że wygodnie było umieścić ROM (pamięć programu) na parzystym adresie, a 0x8000 było wygodne. Jestem prawie pewien, że odpowiedź na to pytanie można znaleźć we wczesnych projektach procesorów Motorola. – Lundin

Odpowiedz

19

Generalnie na wszystkich, ale najmniejsze systemy wbudowane, platforma ABI projektant chce uniknąć najniższe adresy w użyciu, dzięki czemu zerowe dereferences wskaźnik może być kiedykolwiek uwięziony. Posiadanie kilku kilobajtów nieważnych adresów daje dodatkowe bezpieczeństwo, jeśli zerowy wskaźnik zostanie usunięty z tablicy lub z przesunięcia elementu, tak jak w null_ptr->some_member.

+3

Nie sądzę, że to jest powód, pracowałem z kilkoma wbudowanymi systemami, w których adres 0 jest prawidłową i adresowalną pamięcią, a jednocześnie NVM zaczyna się od 8000. – Lundin

+5

... szczególnie, ponieważ adres 0x8000 istniał przed C wskaźniki języka i NULL stały się popularne. Może nawet zanim C został wynaleziony? – Lundin

+0

Jak rozumiem, nie chcesz, aby "prawdziwe" wskaźniki były równe 0, nigdy, nawet w systemach, w których HW jest w porządku. Tak więc, jeśli 'malloc()' zwraca 0, wiesz, że się nie udało. Tak więc w systemach, w których adres 0 nie jest pułapką, pamięć jest zwykle przydzielana do określonych celów, na przykład do obsługi przerwań. – MSalters

6

To zależy od systemu, a programy uruchamiane są pod różnymi adresami w różnych systemach na różnych . W systemie Unix jest to zwykle (a może nawet wymagane przez Posix), aby użyć adresu 0 jako wskaźnika pustego, i nie zamapować pierwszej strony pamięci wirtualnej, tak aby wyłuskiwanie wskaźnika zerowego spowodowało naruszenie segmentu przez . Podejrzewam, że inne systemy używające adresu 0 jako wskaźnika pustego zachowują się podobnie (ale ile rezerwują one mogą się różnić). (Historycznie rzecz biorąc, zwykle odwzorowywano pierwszą stronę tylko jako i wypełniamy ją zerami, tak aby wskaźnik zerowy zachowywał się tak, jakby był pustym łańcuchem, wskaźnikiem do "". To cofa się o 25 lat jednak).

Spodziewam się, że nawet dzisiaj, niektóre systemy wbudowane są ładowane program zaczynając od adresu 0.

2

ja podejrzewam w wielu przypadkach pierwsza 32K zostało zarezerwowanych dla monitorów użycie kodu/ram. W wielu tablicach ewaluacyjnych 8051 nie było tak rzadkich, że domyślnie były to 0x1000 lub 0x2000 dla wszystkich aplikacji w zależności od monitora rezydentnego (niektóre z nich również działały jako debuggery).

32K może być przestrzenią ładującą u-boot/etc.

3

Jest to nieco arbitralne, a na Linuxie przynajmniej zdecydował linker. Ogólną zasadą jest zarezerwowanie przestrzeni do przechwycenia wyjątków wskaźnika NULL. Aby pomóc w zapobieganiu przestrzenie jądra, NULL wskazuje, że wykracza poza wykonywanie dowolnego kodu użytkownika w trybie jądra, a linux uniemożliwia mapowanie samego dna pamięci. /proc/sys/vm/mmap_min_addr kontroluje najniższy adres, który możesz mapować (możesz zmienić go na 0 i odwzorować stronę na 0, jeśli chcesz).

Na Linux-ie możesz spojrzeć na mapowanie pamięci, szukając w /proc. Na przykład:

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

Uważam, że odpowiedź jest bardziej związana z obsługą przerwań. Adresy obsługi przerwań są ustawione w sprzęcie. W procesorze Intel 8086 istniała bezpośrednia tabela translacji na kodzie obsługi przerwań i odpowiedniej procedurze obsługi przerwań. Prawdopodobnie dokonano tego za pomocą jakiegoś kombinatorycznego obwodu i dlatego, aby zachować kompatybilność z przodu, rozsądniej byłoby umieścić je na początku pamięci, a nie na końcu, aby zapobiec zmianom za każdym razem. Tak więc adres startowy wykonania znajdowałby się na drugim końcu pamięci. Konieczne było również, aby w tym bloku znajdowała się wystarczająca ilość kodu, aby załadować program segmentu pamięci i instrukcję przejścia, aby przejść do wykonania kodu z tego adresu kodu.

Powiązane problemy