Подтвердить что ты не робот

Почему общая программа обычно начинается с 0x8000?

Я не новичок в загрузчике и системном SW, но я не знаю происхождения причины, по которой общая программа начинается с 0x8000. Я уже знаю, что адрес 0x8000 использовался как начальный адрес в обычной программе C/С++.

Соответствует ли минимальный размер загрузчика для общей программы до 0x8000? Или минимальный размер блока ROM, который должен быть назначен загрузчику 32KB? Или есть еще одна причина?

Я хотел бы знать об этом, исторически или логически, и с точки зрения виртуального адреса.


Я ценю все, ваше время и помощь в этом. Чтобы вопрос стал более понятным, вопрос связан с виртуальным адресом не с физическим.

Я в основном согласен с мнением R с точки зрения физической памяти.

Не говоря о конкретной различной системе, например linux (даже в android), общем RTOS (ядро и другие, особенно ARM-компоновщик), все они используют адрес 0x8000 в качестве общей программы начального адреса. такие как crt_begin.o, crt.o и т.д., расположенные в 0x0 с загрузчиком, существуют в этой области.

Поэтому я предполагаю, что минимальный размер загрузчика для общей программы составляет 32 КБ с учетом размера блока, если он будет находиться в BootROM во время загрузки (холодная загрузка).

Уммм, Но я не уверен...

4b9b3361

Ответ 1

В целом, на всех, кроме самых маленьких встроенных системах, разработчик платформы ABI хочет избежать использования самых младших адресов, чтобы можно было избежать захвата нулевых указателей. Наличие нескольких КБ неисключимых адресов дает вам дополнительную безопасность, если нулевой указатель разыменован смещением массива или структуры, как в null_ptr->some_member.

Ответ 2

Это зависит от системы, а программы начинаются с разных адресов на различных систем. В Unix это обычное (или, возможно, даже требуемое Posix) использовать адрес 0 как нулевой указатель и не отображать первой страницы виртуальной памяти, так что разыменование нулевого указателя будет приводит к нарушению сегмента. Я подозреваю, что другие системы, использующие адрес 0 как нулевой указатель ведет себя аналогично (но сколько они резервируют может различаться). (Исторически обычно было отображено первое только и заполнить его нулями, сделайте так, чтобы нулевой указатель вел себя так, как будто это пустая строка, указатель на "". Это около 25 лет, однако.)

Я бы ожидал, что даже сегодня некоторые встроенные системы загружают программа, начинающаяся с адреса 0.

Ответ 3

Это несколько произвольно, а на linux, по крайней мере, решает компоновщик. Общая идея состоит в том, чтобы зарезервировать некоторое пространство для исключения исключений указателя NULL. Чтобы помочь избежать разметки NULL-пространства ядра при выполнении произвольного кода пользователя в режиме ядра, linux не позволяет отображать самую нижнюю часть памяти. /proc/sys/vm/mmap_min_addr управляет самым низким адресом, который вы можете отобразить (вы можете изменить его на 0 и сопоставить страницу в 0, если хотите).

В linux вы можете посмотреть на отображение памяти, просмотрев /proc. Например,

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]

Ответ 4

Я бы заподозрил во многих случаях, что первые 32K были зарезервированы для использования кода/режима работы мониторов. Во многих 8051 платах eval было не так уж необычно, чтобы по умолчанию было 0x1000 или 0x2000 для всех приложений в зависимости от резидентного монитора (некоторые из них также работали отладчиками).

32K может быть вашим местом загрузки u-boot/etc.

Ответ 5

Я считаю, что ответ больше связан с обработкой прерываний. Адреса обработчика прерываний устанавливаются на аппаратном уровне. В Intel 8086 была указана прямая таблица трансляции на коде обработчика прерываний и соответствующая процедура обработки прерываний. Вероятно, это было сделано с помощью комбинаторной схемы и, следовательно, чтобы сохранить прямую совместимость, было бы разумнее разместить их при запуске памяти, а не в конце, чтобы предотвратить изменения каждый раз. Таким образом, начальный адрес выполнения будет находиться на другом конце памяти. Кроме того, было необходимо, чтобы в этом блоке содержалось достаточно кода для загрузки программы сегмента памяти и команды перехода для переключения для выполнения кода с этого кодового адреса.