Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

375
Views
¿Cuál es la diferencia entre "vinculado estáticamente" y "no un ejecutable dinámico" de Linux ldd?

Considere este programa de ensamblaje AMD64:

 .globl _start _start: xorl %edi, %edi movl $60, %eax syscall

Si compilo eso con gcc -nostdlib y ejecuto ldd a.out , obtengo esto:

 statically linked

Si, en cambio, compilo eso con gcc -static -nostdlib y ejecuto ldd a.out , obtengo esto:

 not a dynamic executable

¿Cuál es la diferencia entre un ejecutable statically linked y not a dynamic executable ? Y si mi binario ya estaba vinculado estáticamente, ¿por qué agregar -static afecta algo?

over 4 years ago · Santiago Trujillo
1 answers
Answer question

0

Hay dos cosas separadas aquí:

  • Solicitar un intérprete ELF (ld.so) o no.
    Como #!/bin/sh pero para binarios, se ejecuta antes de tu _start .
    Esta es la diferencia entre un ejecutable estático y uno dinámico.
  • La lista de bibliotecas enlazadas dinámicamente para que ld.so cargue está vacía.
    Aparentemente, esto es lo que ldd llama "vinculado estáticamente", es decir, que cualquier biblioteca que haya vinculado en el momento de la compilación era biblioteca estática.

Otras herramientas como file y readelf brindan más información y usan terminología que coincide con lo que espera.


Su GCC está configurado para que -pie sea el valor predeterminado , y gcc no hace un pastel estático para el caso especial de bibliotecas no dinámicas.

  • gcc -nostdlib simplemente crea un PIE que no se vincula a ninguna biblioteca pero que, por lo demás, es idéntico a un PIE normal, especificando un intérprete ELF.
    ldd llama confusamente a esto "vinculado estáticamente".
    file : ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2 ...
  • gcc -nostdlib -static anula el valor predeterminado de -pie y lo convierte en un verdadero ejecutable estático.
    file : ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked ...
  • gcc -nostdlib -no-pie también elige hacer un ejecutable estático como una optimización para el caso en que no haya bibliotecas dinámicas en absoluto. Dado que un ejecutable que no sea PIE no podría haber sido ASLRed de todos modos, esto tiene sentido. Byte por byte idéntico al caso -static .
  • gcc -nostdlib -static-pie crea un ejecutable ASLRable que no necesita un intérprete ELF. GCC no hace esto de forma predeterminada para gcc -pie -nostdlib , a diferencia del caso sin tarta en el que elige eludir ld.so cuando no hay bibliotecas vinculadas dinámicamente involucradas.
    file : ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), statically linked ...

    -static-pie es oscuro, rara vez se usa y file anterior no lo identifica como vinculado estáticamente.

-nostdlib no implica -no-pie o -static , y -static-pie debe especificarse explícitamente para obtenerlo.

gcc -static-pie invoca ld -static -pie , por lo que ld tiene que saber lo que eso significa. A diferencia del caso que no es PIE, en el que no tiene que solicitar explícitamente un ejecutable dinámico, solo obtiene uno si pasa ld cualquier biblioteca .so . Creo que es por eso que obtienes un ejecutable estático de gcc -nostdlib -no-pie - GCC no tiene que hacer nada especial, solo ld haciendo esa optimización.

Pero ld no habilita -static implícitamente cuando se especifica -pie , incluso cuando no hay bibliotecas compartidas para vincular.


Detalles

Ejemplos generados con gcc --version gcc (Arch Linux 9.3.0-1) 9.3.0
ld --version GNU ld (GNU Binutils) 2.34 (también readelf es binutils)
ldd --version ldd (GNU libc) 2.31
file --version file-5.38 - tenga en cuenta que la detección estática ha cambiado en parches recientes, con Ubuntu seleccionando un parche inédito. (Gracias @Joseph por el trabajo de detección): esto en 2019 detectó dinámica = tener un PT_INTERP para manejar el pastel estático, pero se revirtió para detectar en función de PT_DYNAMIC, por lo que las bibliotecas compartidas cuentan como dynamic . Error #948269 de Debian . static-pie es una característica oscura que rara vez se usa.

GCC termina ejecutando ld -pie exit.o con una ruta de vinculación dinámica especificada y sin bibliotecas. (Y un montón de otras opciones para admitir la posible optimización del tiempo de enlace de LTO, pero las claves aquí son -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie . collect2 es solo un contenedor alrededor de ld . )

 $ gcc -nostdlib exit.s -v # output manually line wrapped with \ for readability ... COLLECT_GCC_OPTIONS='-nostdlib' '-v' '-mtune=generic' '-march=x86-64' /usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/collect2 \ -plugin /usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/liblto_plugin.so \ -plugin-opt=/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/lto-wrapper \ -plugin-opt=-fresolution=/tmp/ccoNx1IR.res \ --build-id --eh-frame-hdr --hash-style=gnu \ -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie \ -L/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0 \ -L/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../lib -L/lib/../lib \ -L/usr/lib/../lib \ -L/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../.. \ /tmp/cctm2fSS.o

Obtiene un PIE dinámico sin dependencias de otras bibliotecas. Ejecutarlo todavía invoca el "intérprete ELF" /lib64/ld-linux-x86-64.so.2 que se ejecuta antes de saltar a su _start . (Aunque el núcleo ya ha asignado los segmentos ELF del ejecutable a las direcciones virtuales ASLRed, junto con el texto/datos/bss de ld.so).

file y readelf son más descriptivos.

PIE ejecutable no estático de gcc -nostdlib

 $ gcc -nostdlib exit.s -o exit-default $ ls -l exit-default -rwxr-xr-x 1 peter peter 13536 May 2 02:15 exit-default $ ldd exit-default statically linked $ file exit-default exit-default: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=05a4d1bdbc94d6f91cca1c9c26314e1aa227a3a5, not stripped $ readelf -a exit-default ... Type: DYN (Shared object file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x1000 ... Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040 0x00000000000001f8 0x00000000000001f8 R 0x8 INTERP 0x0000000000000238 0x0000000000000238 0x0000000000000238 0x000000000000001c 0x000000000000001c R 0x1 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x00000000000002b1 0x00000000000002b1 R 0x1000 LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000 0x0000000000000009 0x0000000000000009 RE 0x1000 ... (the Read+Exec segment to be mapped at virt addr 0x1000 is where your text section was linked.)

Si lo rastreas también puedes ver las diferencias:

 $ gcc -nostdlib exit.s -o exit-default $ strace ./exit-default execve("./exit-default", ["./exit-default"], 0x7ffe1f526040 /* 51 vars */) = 0 brk(NULL) = 0x5617eb1e4000 arch_prctl(0x3001 /* ARCH_??? */, 0x7ffcea703380) = -1 EINVAL (Invalid argument) access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9ff5b3e000 arch_prctl(ARCH_SET_FS, 0x7f9ff5b3ea80) = 0 mprotect(0x5617eabac000, 4096, PROT_READ) = 0 exit(0) = ? +++ exited with 0 +++

vs. -static y -static-pie la primera instrucción ejecutada en el espacio de usuario es su _start (que también puede verificar con GDB usando starti ).

 $ strace ./exit-static-pie execve("./exit-static-pie", ["./exit-static-pie"], 0x7ffcdac96dd0 /* 51 vars */) = 0 exit(0) = ? +++ exited with 0 +++

gcc -nostdlib -static-pie

 $ gcc -nostdlib -static-pie exit.s -o exit-static-pie $ ls -l exit-static-pie -rwxr-xr-x 1 peter peter 13440 May 2 02:18 exit-static-pie peter@volta:/tmp$ ldd exit-static-pie statically linked peter@volta:/tmp$ file exit-static-pie exit-static-pie: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=daeb4a8f11bec1bb1aaa13cd48d24b5795af638e, not stripped $ readelf -a exit-static-pie ... Type: DYN (Shared object file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x1000 ... Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000229 0x0000000000000229 R 0x1000 LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000 0x0000000000000009 0x0000000000000009 RE 0x1000 ... (no Interp header, but still a read+exec text segment)

Tenga en cuenta que las direcciones siguen siendo relativas a la base de la imagen, dejando ASLR en manos del kernel.

Sorprendentemente, ldd no dice que no es un ejecutable dinámico. Eso podría ser un error o un efecto secundario de algún detalle de implementación.


gcc -nostdlib -static ejecutable estático tradicional no PIE de la vieja escuela

 $ gcc -nostdlib -static exit.s -o exit-static $ ls -l exit-static -rwxr-xr-x 1 peter peter 4744 May 2 02:26 exit-static peter@volta:/tmp$ ldd exit-static not a dynamic executable peter@volta:/tmp$ file exit-static exit-static: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=1b03e3d05709b7288fe3006b4696fd0c11fb1cb2, not stripped peter@volta:/tmp$ readelf -a exit-static ELF Header: ... Type: EXEC (Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x401000 ... (Note the absolute entry-point address nailed down at link time) (And that the ELF type is EXEC, not DYN) Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x000000000000010c 0x000000000000010c R 0x1000 LOAD 0x0000000000001000 0x0000000000401000 0x0000000000401000 0x0000000000000009 0x0000000000000009 RE 0x1000 NOTE 0x00000000000000e8 0x00000000004000e8 0x00000000004000e8 0x0000000000000024 0x0000000000000024 R 0x4 Section to Segment mapping: Segment Sections... 00 .note.gnu.build-id 01 .text 02 .note.gnu.build-id ...

Esos son todos los encabezados del programa; a diferencia de pie / static-pie, no estoy omitiendo nada, solo otras partes completas de la salida readelf -a .

También tenga en cuenta las direcciones virtuales absolutas en los encabezados del programa que no le dan al núcleo una opción en el espacio de direcciones virtuales para mapear el archivo. Esta es la diferencia entre los tipos EXEC y DYN de objetos ELF. Los ejecutables PIE son objetos compartidos con un punto de entrada, lo que nos permite obtener ASLR para el ejecutable principal. Los ejecutables EXEC reales tienen un diseño de memoria elegido en el momento del enlace.


ldd aparentemente solo informa "no es un ejecutable dinámico" cuando ambos:

  • sin ruta de intérprete ELF (vinculador dinámico)
  • Tipo ELF = EXEC
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!