¿Por qué el programa general suele empezar en 0x8000?

8 minutos de lectura

avatar de usuario
OfusJK

No soy nuevo en el cargador de arranque y el SW del sistema, pero no sé el origen de la razón por la cual el programa general comienza en 0x8000. ya se la direccion 0x8000 se ha utilizado como dirección de inicio en el programa C/C++ normal.

¿El tamaño mínimo del cargador de arranque para un programa general toma hasta 0x8000? ¿O el tamaño mínimo de bloque de la ROM que debe asignarse al gestor de arranque es de 32 KB? ¿O hay otra razón?

Me gustaría saber sobre esto, histórica o lógicamente, y desde el punto de vista de una dirección virtual.


Agradezco a todos, su tiempo y ayuda con esto. Para aclarar la pregunta, la pregunta está relacionada con la dirección virtual, no con la física.

Básicamente estoy de acuerdo con la opinión de R desde el punto de vista de la dirección de la memoria física.

Sin decir un sistema específico que es diverso, por ejemplo, Linux (incluso en Android), RTOS general (núcleo y los demás, especialmente la sección del enlazador ARM), todos usan la dirección 0x8000 como programa general de dirección de inicio. tales como crt_begin.o, crt.o, etc. ubicados en 0x0 con cargador existen en esta área.

Por lo tanto, supongo que el tamaño mínimo del cargador de arranque para el programa general es de 32 KB considerando el tamaño del bloque si se ubicara en BootROM en el momento del arranque (arranque en frío).

Ummm, pero no estoy seguro…

  • ¿De qué sistema estás hablando aquí?

    – Jerry Ataúd

    13 de marzo de 2012 a las 6:35

  • No tengo ninguna fuente confiable para esto, pero puedo hacer una conjetura calificada. Históricamente, muchos procesadores, en particular los de 8 bits, han tenido la característica llamada página cero lo que significa que las celdas de memoria en las direcciones 0x00 – 0xFF tenían soporte de instrucciones para ejecutarse más rápido. Creo que esto fue introducido por Motorola en el pasado, ya que tenían registros de E/S asignados a la memoria en las antiguas MCU como 6800. ->

    – Lundin

    13 de marzo de 2012 a las 8:03

  • Por lo tanto, querrá que esta primera área de la memoria esté ocupada por celdas RAM o registros especiales. Entonces tiene sentido que la parte del espacio de direcciones que viene después de la página cero sea de la misma naturaleza: RAM y/o registros. Esto ocuparía muchos kb, tal vez hasta 0x6000 o algo así. Entonces asumo que era conveniente colocar la ROM (memoria de programa) en una dirección uniforme y 0x8000 era conveniente. Estoy bastante seguro de que la respuesta a esta pregunta se puede encontrar en los primeros diseños de procesadores de Motorola.

    – Lundin

    13 de marzo de 2012 a las 8:04

  • Esto podría ser relevante: en.wikipedia.org/wiki/ELF_binary. ¿Quizás el tamaño es para ser compatible con los formatos anteriores?

    – Letargo

    30 de mayo de 2012 a las 22:06


En general, en todos los sistemas integrados excepto en los más pequeños, el diseñador de ABI de la plataforma quiere evitar tener las direcciones más bajas en uso para que las desreferencias de puntero nulo puedan quedar atrapadas. Tener varios KB de direcciones nunca válidas le brinda seguridad adicional si el puntero nulo se desreferencia con una matriz o un desplazamiento de miembro de estructura, como en null_ptr->some_member.

  • No creo que esta sea la razón, he trabajado con varios sistemas integrados donde la dirección 0 es una memoria válida y direccionable, mientras que al mismo tiempo la NVM comienza en 8000.

    – Lundin

    13 de marzo de 2012 a las 7:36

  • …sobre todo porque la dirección 0x8000 existía antes de que el lenguaje C y los punteros NULL se hicieran populares. ¿Quizás incluso antes de que se inventara C?

    – Lundin

    13 de marzo de 2012 a las 8:05

  • @MSalters malloc() devuelve un puntero nulo si falla. No hay ningún requisito de que un puntero nulo sea la dirección 0 (solo que una constante integral que se evalúe en 0 se convierta implícitamente en él, sea lo que sea). Y el lugar donde el sistema coloca elementos como controladores de interrupciones depende del hardware; Intel siempre los ha colocado al principio, pero otros sistemas varían.

    – James Kanze

    13 de marzo de 2012 a las 8:35

  • @JamesKanze: Por supuesto, pero los “sistemas integrados” donde “HW está bien con eso” son sistemas simples sin VM (si tiene una VM, simplemente no mapea (void*)NULL en el espacio del proceso). En esos sistemas, estás tan cerca del metal que realmente quieres NULL ser bit a bit 0. Y sí, los controladores de interrupción son un ejemplo; otras CPU tenían registros de E/S asignados a la memoria en la dirección 0.

    – MSalters

    13 de marzo de 2012 a las 8:45

  • @MSalters Con VM, la solución más simple es no asignar la dirección 0 y usar 0 como una constante de puntero nulo. Sin VM, los sistemas han usado todo tipo de cosas, y definitivamente ha habido sistemas en los que el puntero nulo no era la dirección 0.

    – James Kanze

    13 de marzo de 2012 a las 10:44

Depende del sistema, y ​​los programas comienzan en diferentes direcciones en diferentes sistemas. Bajo Unix, es usual (o tal vez incluso requerido por Posix) usar la dirección 0 como el puntero nulo, y no mapear la primera página de la memoria virtual, por lo que desreferenciar un puntero nulo resultará en una violación del segmento. Sospecho que otros sistemas que usan la dirección 0 como un puntero nulo se comportan de manera similar (pero la cantidad que reservan puede variar). (Históricamente, era habitual mapear la primera página como de solo lectura y llenarla con ceros, por lo que un puntero nulo se comportaría como si fuera una cadena vacía, un puntero a "". Sin embargo, eso se remonta a unos 25 años).

Esperaría que incluso hoy en día, algunos sistemas integrados carguen el programa comenzando en la dirección 0.

Es algo arbitrario, y en Linux, al menos lo decide el enlazador. La idea general es reservar algo de espacio para capturar excepciones de puntero NULL. Para ayudar a evitar que las desreferencias del puntero NULL del espacio del kernel ejecuten código de usuario arbitrario en modo kernel, linux le impide mapear la parte inferior de la memoria. /proc/sys/vm/mmap_min_addr controla la dirección más baja que puede asignar (puede cambiarla a 0 y asignar una página a 0 si lo desea).

En Linux, puede ver el mapeo de memoria mirando en /proc. Por ejemplo,

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]

Sospecharía que en muchos casos los primeros 32K estaban reservados para el uso del código/ram de los monitores. En muchas placas de evaluación 8051, no era raro que el valor predeterminado fuera 0x1000 o 0x2000 para todas las aplicaciones, según el monitor residente (algunas que también funcionaban como depuradores).

32K podría ser su espacio de cargadores u-boot/etc.

Creo que la respuesta está más relacionada con el manejo de interrupciones. Las direcciones del controlador de interrupciones se establecen en el hardware. En Intel 8086, había una tabla de traducción directa en el código del controlador de interrupciones y la rutina de manejo de interrupciones correspondiente. Probablemente, esto se hizo mediante algún circuito combinatorio y, por lo tanto, para preservar la compatibilidad hacia adelante, habría sido más sensato colocarlos al principio de la memoria en lugar de al final para evitar los cambios cada vez. Entonces, la dirección de inicio de la ejecución estaría en el otro extremo de la memoria. Además, era necesario que hubiera suficiente código en ese bloque para cargar un programa de segmento de memoria y una instrucción de salto para cambiar y ejecutar el código desde esa dirección de código.

¿Ha sido útil esta solución?