Si entiendo correctamente, la sección .bss
en archivos ELF se usa para asignar espacio para variables inicializadas cero. Nuestra cadena de herramientas produce archivos ELF, de ahí mi pregunta: ¿la sección .bss
realmente debe contener todos esos ceros? Parece una pérdida de espacio tan horrible que, cuando, digamos, asigno una matriz global de diez megabytes, resulta en diez megabytes de ceros en el archivo ELF. ¿Qué estoy viendo mal aquí?¿Las variables inicializadas cero de la sección .bss ocupan espacio en el archivo elf?
Respuesta
Ha pasado algún tiempo desde que trabajé con ELF. Pero creo que todavía recuerdo esto. No, físicamente no contiene esos ceros. Si observa un encabezado de programa de archivos ELF, verá que cada encabezado tiene dos números: uno es el tamaño del archivo. Y otro es el tamaño que la sección ha asignado cuando en la memoria virtual (readelf -l ./a.out
):
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08048034 0x08048034 0x000e0 0x000e0 R E 0x4
INTERP 0x000114 0x08048114 0x08048114 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD 0x000000 0x08048000 0x08048000 0x00454 0x00454 R E 0x1000
LOAD 0x000454 0x08049454 0x08049454 0x00104 0x61bac RW 0x1000
DYNAMIC 0x000468 0x08049468 0x08049468 0x000d0 0x000d0 RW 0x4
NOTE 0x000128 0x08048128 0x08048128 0x00020 0x00020 R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
encabezados de tipo LOAD
es el que se copia en la memoria virtual cuando se carga el archivo para su ejecución. Otros encabezados contienen otra información, como las bibliotecas compartidas que se necesitan. Como se ve, la FileSize
y MemSiz
difieren significativamente para el encabezado que contiene la sección bss
(la segunda LOAD
uno):
0x00104 (file-size) 0x61bac (mem-size)
Para este ejemplo de código:
int a[100000];
int main() { }
La especificación ELF dice que la parte de un segmento que el tamaño de la memoria es mayor que el tamaño del archivo se completa con ceros en la memoria virtual. El segmento de sección de asignación de la segunda cabecera LOAD
es así:
03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss
lo que hay algunas otras secciones de allí también. Para C++ constructor/destructores. Lo mismo para Java. Luego contiene una copia de la sección .dynamic
y otras cosas útiles para la vinculación dinámica (creo que este es el lugar que contiene las bibliotecas compartidas necesarias entre otras cosas). Después de eso, la sección .data
que contiene variables globales globales y variables globales estáticas. Al final, aparece la sección .bss
, que se llena con ceros en el momento de la carga porque el tamaño del archivo no lo cubre.
Por cierto, puede ver en qué sección de salida se colocará un símbolo particular utilizando la opción del vinculador -M
. Para gcc, usa -Wl,-M
para pasar la opción al vinculador. El ejemplo anterior muestra que a
está asignado dentro de .bss
. Puede ayudar a verificar que los objetos no inicializados realmente terminan en .bss
y no en otro lugar:
.bss 0x08049560 0x61aa0
[many input .o files...]
*(COMMON)
*fill* 0x08049568 0x18 00
COMMON 0x08049580 0x61a80 /tmp/cc2GT6nS.o
0x08049580 a
0x080ab000 . = ALIGN ((. != 0x0)?0x4:0x1)
0x080ab000 . = ALIGN (0x4)
0x080ab000 . = ALIGN (0x4)
0x080ab000 _end = .
GCC mantiene globales sin inicializar en una sección común de forma predeterminada, para la compatibilidad con los compiladores de edad, que permiten tener variables globales definidas dos veces en un programa sin errores de definición múltiple. Use -fno-common
para hacer que GCC use las secciones .bss para archivos de objeto (no hace una diferencia para el ejecutable enlazado final, porque como ve, va a entrar en una sección de salida .bss de todos modos. Esto está controlado por el script del enlazador .Muestrelo con ld -verbose
). Pero eso no debería asustarte, es solo un detalle interno. Ver la página de manual de gcc.
+1. Bien hecho. Agradable y completo – Eddie
Supongo que el tipo de sección NOBITS debe establecerse para permitir esto? –
Wouter. hmm, nunca usé esa bandera. mi archivo de encabezado de máquina para gcc se ve como #define BSS_SECTION_ASM_OP "\ t.section \ t.bss, \" aw \ "" –
Una sección de .bss
no se almacena en un archivo ejecutable. De las secciones más comunes (.text
, .data
, .bss
), solo .text
(código actual) y .data
(datos inicializados) están presentes en un archivo ELF.
Eso no es lo que me dice un readelf en un ejecutable arbitrario. Hay una horseload de secciones en el archivo, incluida la sección .bss. –
No depende de ELF sino de su cadena de compilación (idiomas, herramientas, opciones como depuración, ...). También puede tener sus propias secciones personalizadas. – mouviciel
La sección '.bss' se almacena en el archivo ejecutable para al menos ELF. Pero su contenido no está almacenado, por lo que el tamaño de '.bss' en el archivo es una pequeña constante. En los sistemas operativos con protección de memoria, la sección '.bss' necesita almacenarse de alguna manera para que el cargador pueda organizar la memoria grabable en esa ubicación. Por supuesto, sería razonable pensar que todo lo que sobra de '.bss' en algunos formatos es una contribución a un campo de tamaño asignado pero no copiado. – textshell
La sección .bss
en un archivo ELF se utiliza para datos estáticos que se no inicializado programación, pero es la garantía de ser puesto a cero en tiempo de ejecución. Aquí hay un pequeño ejemplo que explicará la diferencia.
int main() {
static int bss_test1[100];
static int bss_test2[100] = {0};
return 0;
}
En este caso bss_test1
se coloca en el .bss
ya que es inicializado. bss_test2
sin embargo, se coloca en el segmento .data
junto con un montón de ceros. El cargador de tiempo de ejecución básicamente asigna la cantidad de espacio reservado para el .bss
y lo pone a cero antes de que el código de usuario empiece a ejecutarse.
se puede ver la diferencia mediante objdump
, nm
, o utilidades similares:
moozletoots$ objdump -t a.out | grep bss_test
08049780 l O .bss 00000190 bss_test1.3
080494c0 l O .data 00000190 bss_test2.4
Este suele ser uno de los primeros sorprende que los desarrolladores integrados topan ... Nunca inicializar la estática a cero de forma explícita. El cargador de tiempo de ejecución (por lo general) se ocupa de eso. Tan pronto como inicialice algo explícitamente, le está diciendo al compilador/vinculador que incluya los datos en la imagen ejecutable.
en mi plataforma gcc ponga bss_test2 en la sección .bss. podrías haber mencionado la opción de compilación -fno-zero-initialized-in-bss que controla esto. – tristan
Del manual: "Si el destino admite una sección BSS, GCC coloca por defecto las variables que se inicializan a cero en BSS". – OrangeDog
Correcto, .bss no está presente físicamente en el archivo, sino que solo la información sobre su tamaño está presente para que el cargador dinámico asigne la sección .bss para el programa de aplicación. Como regla general solo LOAD, el segmento TLS obtiene la memoria para el programa de aplicación, el resto se usa para el cargador dinámico.
Acerca archivo ejecutable estático, bss secciones también se da en el espacio execuatble aplicación
Embedded donde no hay cargador de esto es común.
Suman
dices, ¿TLS también están cargados, como PT_LOAD? Veo que PT_TLS está incluido en PT_LOAD – osgx
- 1. ¿dónde debería tomarse en memoria la sección .bss del archivo ELF?
- 2. ¿Cómo variables globales inicializadas por el cargador de Elf
- 3. Agregar sección al archivo ELF
- 4. x86 ASM Linux - Usando la Sección .bss
- 5. Variables no inicializadas en Fortran 2003
- 6. ¿Cuál es la ventaja de tener una sección .bss?
- 7. ¿qué parte del archivo ELF debe cargarse en la memoria?
- 8. CSS: Elementos Ocultos todavía ocupan espacio en la salida impresa
- 9. rastrear variables estáticas no inicializadas
- 10. obteniendo el miembro sh_name en un archivo elf de cabecera de sección
- 11. ¿Las columnas anulables ocupan espacio adicional en PostgreSQL?
- 12. ¿Cómo se extrae solo el contenido de una sección ELF
- 13. funciones internas que se ocupan de las variables del ámbito
- 14. ¿Cómo puedo examinar el contenido de una sección de datos de un archivo ELF en Linux?
- 15. archivo ELF a mano
- 16. Herramienta para modificar la sección dinámica de un binario ELF
- 17. Formato de archivo de núcleo ELF
- 18. Cómo hacer un div vacías ocupan espacio
- 19. ¿Los NULL ocupan espacio dentro de los índices postgresql?
- 20. cabecera ELF lectura en C
- 21. Embalaje de un archivo en un archivo ejecutable ELF
- 22. ¿Los miembros de la clase ocupan memoria?
- 23. Si una variable global se inicializa en 0, ¿irá a BSS?
- 24. Las variables globales inicializadas declaradas como "const" van al segmento de texto, mientras que las declaradas "Static" van al segmento de datos. ¿Por qué?
- 25. ¿Debo mantener las variables de instancia en Java siempre inicializadas o no?
- 26. Python: El extracto de las variables de espacio de nombres
- 27. Forzar ciertas variables generadas por el compilador en secciones específicas de ELF (con gcc)
- 28. Tabla de cadenas en ELF
- 29. Sección Flush ELF de la RAM después de la inicialización de la biblioteca
- 30. En Objective-C, podemos poner las variables de instancia en la sección @implementation?
manera rápida para responder a ella: hacer un mundo hola con un 'int es [1000000]' y otro sin, compilar y ver los tamaños compilados :-) A continuación, para comprender realmente, descompilarlo con binutils, o compilar al código de ensamblaje con '-S'. –