User Tools

Site Tools


ficheros_elf_coredumps_y_simbolos

Guía básica: ficheros ELF, Coredumps, objetos y símbolos

Conceptos básicos

Símbolos

En programación, un símbolo es una variable o una función, podemos decir que un símbolo es un nombre que representa un espacio en la memoria. El cual almacena datos (variables que se leen o escriben) o instrucciones (funciones que ejecutan algo). En lo que respecta el kernel de Linux, para facilitar la cooperación entre varias unidades de función del kernel, hay miles de símbolos globales en el núcleo de Linux. Estas variables y funciones globales están listados en System.map (tabla de código estático) y /proc/kallsyms, el cual contiene los símbolos del kernel en ejecución. (estático + dinámico). System.map no sólo es útil para depurar los errores del kernel. Algunos controladores necesitan System.map para resolver los símbolos ya que están enlazados con las cabeceras del kernel en lugar de con glibc. Esto asa con Volatility a la hora de crear perfiles, los cuales no funcionarán correctamente sin el System.map para el kernel particular que se está ejecutando.

0000000000018000 A entry_stack_storage
0000000000019000 A espfix_waddr
0000000000019008 A espfix_stack
ffffffffc02fecd0 t xfs_reflink_inode_has_shared_extents	[xfs]
ffffffffc0269820 t __traceiter_xfs_buf_item_committed	[xfs]
ffffffffc03e88a0 d __SCK__tp_func_xfs_blockgc_flush_all	[xfs]
ffffffffc011b5c0 T VBoxHost_RTStrToUInt8	[vboxdrv]
ffffffffc0207000 r __ksymtab_sdw_intel_acpi_scan	[snd_intel_sdw_acpi]

ffffffffa9603580 T rewind_stack_and_make_dead

La primera columna es la dirección del símbolo, la segunda columna es el tipo de símbolo. Puedes ver las instrucciones detalladas en la página man de “nm”. las siguientes columnas son el nombre del símbolo y desde qué modulo ha sido cargado (Vbox, módulo de sonido, xfs, etc).

Objetos = Código objeto = Fichero objeto

En programación, se llama código objeto al código que resulta de la compilación del código fuente. Este puede ser en lenguaje/código máquina (no confundir con ensamblador) o bytecode (Binario), y puede distribuirse en varios archivos denominados ficheros objeto y pueden tener a su vez diferentes formatos. El código objeto puede ser por tanto una librería, un ejecutable, clase, etc.

NOTA: Código / lenguaje objeto = Código / lenguaje máquina.

Ver librerías compartidas de un binario. Tienen como extensión .so (Shared Object) ya que son objetos que van a ser compartidos.

ldd text2pdf 
	linux-vdso.so.1 (0x00007ffd991f5000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc50f50c000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fc50f8be000)

Otras formas de averiguar qué librerías compartidas necesita un determinado binario para su correcta ejecución sería usando objdump y readelf. Pero estas muestran menos librerías que ldd. Esto se debe a que ldd muestra virtual shared objects que son insertados por el kernel en cada proceso y no tienen ruta en disco. A su vez ldd sigue buscando qué librerías necesitan las actuales librerías compartidas. Es decir, con ldd se obtendrán todas las librerías que una aplicación necesita utilizar en tiempo de ejecución.

objdump -x /usr/bin/nmap | grep NEEDED
readelf -d /usr/bin/nmap | grep NEEDED

NOTA: Si el binario fue compilado estáticamente, ldd avisará diciendo “no es un ejecutable dinámico”. El comando file también nos da una idea de como fue compilado un binario.

file XXX_dinamico
XXX_dinamico: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d57a35a12b3d157e89b26124ec912e8dd913449a, for GNU/Linux 4.4.0, stripped

file XXX_estático 
XXX_estático : ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, stripped

Compilar un binario usando librerías compartidas (Incluye las tablas de símbolos .symtab y .dynsym).

gcc -o programa programa.c -lm

Compilar de manera estática, sin usar librerías compartidas (Incluye tabla de símbolos .symtab que puede ser eliminada con strip, como se verá más adelante).

gcc -static -o programa programa.c -lm

Bytecode

El bytecode es un código intermedio más abstracto que el código máquina. Habitualmente es tratado como un archivo binario que contiene el código objeto o código máquina. Como código intermedio reduce la dependencia respecto del hardware y facilita la interpretación. Algunos sistemas, llamados traductores dinámicos o compiladores just-in-time, traducen el bytecode a código máquina inmediatamente antes de su ejecución para mejorar la velocidad de ejecución.

Los programas en bytecode suelen ser interpretados por un intérprete de bytecode (en general llamado máquina virtual, dado que es análogo a un ordenador). Su ventaja es su portabilidad: el mismo código binario puede ser ejecutado en diferentes plataformas y arquitecturas. Es la misma ventaja que presentan los lenguajes interpretados y su rendimiento suele ser mejor que el de los lenguajes interpretados. A causa de esa mejora en el rendimiento, muchos lenguajes interpretados, de hecho, se compilan para convertirlos en bytecode y después son ejecutados por un intérprete de bytecode. Entre esos lenguajes se encuentran Perl, Gambas, PHP y Python. En el caso de Java se suele trasmitir como bytecode a la máquina receptora, que utiliza un compilador just-in-time para compilar el bytecode en código máquina nativo antes de su ejecución, ahorrando así procesos de interpretación.

Enlazador

Un enlazador (en inglés, linker) es un programa que toma los objetos generados en los primeros pasos del proceso de compilación y la información de todos los recursos necesarios (biblioteca). Quita aquellos recursos que no necesita y enlaza el código objeto con sus bibliotecas con lo que finalmente produce un fichero ejecutable o una biblioteca.

En el caso de los programas enlazados dinámicamente, el enlace entre el programa ejecutable y las bibliotecas se realiza en tiempo de carga o ejecución del programa.

Tabla de símbolos

También llamada tabla de nombres o tabla de identificadores. La tabla de símbolos es una importante estructura de datos creada y mantenida por los compiladores para almacenar información sobre las ocurrencias de diversas entidades. Estas tablas almacenan la información que en cada momento se necesita para hacer alguna tarea, ya sea almacenar información sobre variables como su nombre y tipo, su dirección de memoria, dimensiones de arrays, número de línea de otros identificadores, parámetros de funciones o métodos, etc. Por lo tanto, la gestión de la tabla de símbolos es muy importante y consume gran parte del tiempo de compilación. De ahí que su eficiencia sea crítica.

NOTA: Cuando se habla de “identificadores del lenguaje” se suele referir a nombres de variables, objetos, métodos, funciones, clases, etc

La tabla de símbolos permanece solo en tiempo de compilación, no de ejecución, excepto en interpretes y aquellos casos en que se compile especificando opciones de depuración. Por ejemplo al compilar directamente con gcc en la terminal, se suelen incluir las tablas de símbolo completas en el fichero binario ELF.

Tablas de símbolos “.symtab” y “.dynsym”.

Los objetos compartidos y los ejecutables dinámicos generalmente tienen dos tablas de símbolos distintas, una llamada “.symtab” y la otra “.dynsym”.

La tabla .dynsym solo tiene símbolos globales, los cuales siempre están en symtab pero no a la inversa.

Consultar tabla de símbolos.

text2pdf fue compilado con gcc y como se puede ver tiene la tabla de símbolos “.symtab” incluida.

Con el comando file se puede saber si un ejecutable incluye la tabla “.symtab” (not stripped: todos los símbolos) o solo dynsym (stripped: solo los símbolos necesarios para la ejecución).

file text2pdf 
text2pdf: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.26, BuildID[sha1]=ba4a363f00ff701efe1247b7a107231b27d16a39, not stripped
readelf -s text2pdf 

Symbol table '.dynsym' contains 20 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND putchar@GLIBC_2.2.5 (2)
     ...

Symbol table '.symtab' contains 110 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000400200     0 SECTION LOCAL  DEFAULT    1 
     2: 000000000040021c     0 SECTION LOCAL  DEFAULT    2 
     ...

Eliminar con el comando strip tablas de símbolos que no se requieren en la ejecución. (Elimina la tabla .symtab).

readelf -s text2pdf > not_striped 
du text2pdf 
24
strip text2pdf
du text2pdf 
20
readelf -s text2pdf > striped
diff not_striped striped 

Como se puede ver el tamaño del fichero ELF es menor ahora y la tabla de símbolos .symtab ha desaparecido. Recordar que si se elimina esa tabla los depuradores u otros programas de análisis de binarios pueden no ofrecer toda la información que debieran. Un caso donde tampoco pueda aparecer la tabla .dynsym es cuando se ha compilado el código de manera estática, pero estos si contienen tabla .symtab, la cual puede ser eliminada con el comando strip cuando queramos.

Volcado de memoria / Coredump

Un coredump es un archivo que contiene el espacio de direcciones (memoria) de un proceso cuando el proceso termina inesperadamente. Los volcados pueden producirse bajo demanda mediante un depurador o automáticamente al finalizar el proceso. Los coredumps son activados por el kernel en respuesta a bloqueos del programa, pueden pasarse a un programa auxiliar como systemd-coredump para su posterior procesamiento.

Cuando se quiere por tanto analizar esa información de la memoria, los símbolos son cruciales para que el compilador nos de información detallada sobre lo que ha pasado internamente. Por eso es tan importante que los binarios incluyan ese tipo de tablas al compilarse. De no ser así la información sería mucho más genérica o incluso inexistente. La configuración de volcados puede hacerse de manera global para todas las aplicación que fallen o bien para algunas específicas.

Volcados / Coredumps en Arch Linux

En Arch Linux vienen activados y gestionados por systemd.

Directorio por defecto para los coredumps: /var/lib/systemd/coredump

Desactivar los coredumps en Arch mediante sysctl creando el fichero /usr/lib/sysctl.d/51-coredump-disable.conf

kernel.core_pattern=|/bin/false
sysctl -p /usr/lib/sysctl.d/51-coredump-disable.conf

Desactivar los coredumps en Arch mediante utlimit. Si se establece a 0 el tamaño para los coredumps en /etc/security/limits.conf, estos se desactivan.

* hard core 0

Buscar y visualizar coredumps en Arch.

coredumpctl list      # Listamos los coredumps.
coredumpctl info XXX  # Buscamos información sobre un volcado en concreto.
coredumpctl gdb XXX   # Usamos gdb para obtener más información detallada.
(gdb) bt full         # Una vez dentro de gdb usamos la opción de "backtrace full".
ficheros_elf_coredumps_y_simbolos.txt · Last modified: 2022/10/05 23:28 by busindre