También puedes usar el file
comando (y objdump
también podría ser útil).
Comprueba si tiene un encabezado de programa de tipo INTERP
En el nivel inferior, un ejecutable es estático si no tiene un encabezado de programa con el tipo:
Elf32_Phd.p_type == PT_INTERP
Esto se menciona en la especificación System V ABI.
Recuerde que los encabezados de programa determinan los segmentos ELF, incluidos los de tipo PT_LOAD
que se cargará en la memoria y se ejecutará.
Si ese encabezado está presente, su contenido es exactamente la ruta del cargador dinámico.
readelf
comprobar
Podemos observar esto con readelf
. Primero compila dinámicamente un C hello world:
gcc -o main.out main.c
y luego:
readelf --program-headers --wide main.out
salidas:
Elf file type is DYN (Shared object file)
Entry point 0x1050
There are 11 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x000268 0x000268 R 0x8
INTERP 0x0002a8 0x00000000000002a8 0x00000000000002a8 0x00001c 0x00001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000560 0x000560 R 0x1000
LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x0001bd 0x0001bd R E 0x1000
LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000150 0x000150 R 0x1000
LOAD 0x002db8 0x0000000000003db8 0x0000000000003db8 0x000258 0x000260 RW 0x1000
DYNAMIC 0x002dc8 0x0000000000003dc8 0x0000000000003dc8 0x0001f0 0x0001f0 RW 0x8
NOTE 0x0002c4 0x00000000000002c4 0x00000000000002c4 0x000044 0x000044 R 0x4
GNU_EH_FRAME 0x00200c 0x000000000000200c 0x000000000000200c 0x00003c 0x00003c R 0x4
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
GNU_RELRO 0x002db8 0x0000000000003db8 0x0000000000003db8 0x000248 0x000248 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03 .init .plt .plt.got .text .fini
04 .rodata .eh_frame_hdr .eh_frame
05 .init_array .fini_array .dynamic .got .data .bss
06 .dynamic
07 .note.ABI-tag .note.gnu.build-id
08 .eh_frame_hdr
09
10 .init_array .fini_array .dynamic .got
así que tenga en cuenta el INTERP
el encabezado está ahí, y es muy importante que readelf
incluso dio una vista previa rápida de su contenido corto de 28 (0x1c) bytes:/lib64/ld-linux-x86-64.so.2
, que es la ruta al cargador dinámico (27 bytes de largo + 1 para \0
).
Tenga en cuenta cómo esto reside al lado de los otros segmentos, incluidos, p. aquellos que realmente se cargan en la memoria, como:.text
.
Entonces podemos extraer más directamente esos bytes sin la vista previa con:
readelf -x .interp main.out
que da:
Hex dump of section '.interp':
0x000002a8 2f6c6962 36342f6c 642d6c69 6e75782d /lib64/ld-linux-
0x000002b8 7838362d 36342e73 6f2e3200 x86-64.so.2.
como se explica en:¿Cómo puedo examinar el contenido de una sección de datos de un archivo ELF en Linux?
file
código fuente
file
Los comentarios del código fuente 5.36 en src/readelf.c afirman que también comprueba PT_INTERP
:
/*
* Look through the program headers of an executable image, searching
* for a PT_INTERP section; if one is found, it's dynamically linked,
* otherwise it's statically linked.
*/
private int
dophn_exec(struct magic_set *ms, int clazz, int swap, int fd, off_t off,
int num, size_t size, off_t fsize, int sh_num, int *flags,
uint16_t *notecount)
{
Elf32_Phdr ph32;
Elf64_Phdr ph64;
const char *linking_style = "statically";
encontrado con git grep statically
del mensaje main.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
.
Sin embargo, este comentario parece estar desactualizado en comparación con el código, que en su lugar busca PT_DYNAMIC
:
case PT_DYNAMIC:
linking_style = "dynamically";
doread = 1;
break;
No estoy seguro de por qué se hace esto, y me da pereza profundizar en git log
ahora. En particular, esto me confundió un poco cuando traté de hacer un ejecutable PIE enlazado estáticamente con --no-dynamic-linker
como se muestra en:¿Cómo crear un ELF ejecutable independiente de posición enlazada estáticamente en Linux? que no tiene PT_INTERP
pero tiene PT_DYNAMIC
, y que no espero usar el cargador dinámico.
Terminé haciendo un análisis de fuente más profundo para -fPIE
at:¿Por qué GCC crea un objeto compartido en lugar de un binario ejecutable según el archivo? es probable que la respuesta también esté ahí.
Código fuente del kernel de Linux
El kernel de Linux 5.0 lee el archivo ELF durante la llamada del sistema exec en fs/binfmt_elf.c como se explica en:¿Cómo obtiene el kernel un archivo binario ejecutable que se ejecuta en Linux?
El kernel recorre los encabezados del programa en load_elf_binary
for (i = 0; i < loc->elf_ex.e_phnum; i++) {
if (elf_ppnt->p_type == PT_INTERP) {
/* This is the program interpreter used for
* shared libraries - for now assume that this
* is an a.out format binary
*/
No he leído el código por completo, pero esperaría que solo use el cargador dinámico si INTERP
se encuentra, de lo contrario, ¿qué ruta debería usar?
PT_DYNAMIC
no se usa en ese archivo.
Bonificación:comprueba si -pie
fue usado
Lo expliqué en detalle en:¿Por qué GCC crea un objeto compartido en lugar de un archivo binario ejecutable según el archivo?
ldd /path/to/binary
no debe enumerar ninguna biblioteca compartida si el binario se compila estáticamente.