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

449
Views
¿Tamaño ejecutable mínimo ahora 10 veces más grande después de la vinculación que hace 2 años, para programas pequeños?

Para un curso universitario, me gusta comparar los tamaños de código de programas funcionalmente similares si están escritos y compilados usando gcc/clang versus ensamblador. En el proceso de reevaluar cómo reducir aún más el tamaño de algunos ejecutables, no podía confiar en mis ojos cuando el mismo código ensamblador que ensamblé/vinculé hace 2 años ahora creció más de 10 veces en tamaño después de construirlo nuevamente (que verdadero para múltiples programas, no solo para helloworld):

 $ make as -32 -o helloworld-asm-2020.o helloworld-asm-2020.s ld -melf_i386 -o helloworld-asm-2020 helloworld-asm-2020.o $ ls -l -rwxr-xr-x 1 xxx users 708 Jul 18 2018 helloworld-asm-2018* -rwxr-xr-x 1 xxx users 8704 Nov 25 15:00 helloworld-asm-2020* -rwxr-xr-x 1 xxx users 4724 Nov 25 15:00 helloworld-asm-2020-n* -rwxr-xr-x 1 xxx users 4228 Nov 25 15:00 helloworld-asm-2020-n-sstripped* -rwxr-xr-x 1 xxx users 604 Nov 25 15:00 helloworld-asm-2020.o* -rw-r--r-- 1 xxx users 498 Nov 25 14:44 helloworld-asm-2020.s

El código ensamblador es:

 .code32 .section .data msg: .ascii "Hello, world!\n" len = . - msg .section .text .globl _start _start: movl $len, %edx # EDX = message length movl $msg, %ecx # ECX = address of message movl $1, %ebx # EBX = file descriptor (1 = stdout) movl $4, %eax # EAX = syscall number (4 = write) int $0x80 # call kernel by interrupt # and exit movl $0, %ebx # return code is zero movl $1, %eax # exit syscall number (1 = exit) int $0x80 # call kernel again

El mismo programa hello world, compilado usando GNU as y GNU ld (siempre usando ensamblador de 32 bits) tenía 708 bytes entonces, y ahora ha crecido a 8.5K. Incluso cuando le dice al enlazador que desactive la alineación de la página ( ld -n ), todavía tiene casi 4.2K. strip ping/ sstrip ping tampoco vale la pena.

readelf me dice que el inicio de los encabezados de sección es mucho más tarde en el código (byte 468 frente a 8464), pero no tengo idea de por qué. Se ejecuta en el mismo sistema de arco que en 2018, el Makefile es el mismo y no estoy vinculando ninguna biblioteca (especialmente libc). Supongo que algo con respecto a ld ha cambiado debido al hecho de que el archivo del objeto aún es bastante pequeño, pero ¿qué y por qué?

Descargo de responsabilidad: estoy creando ejecutables de 32 bits en una máquina x86-64.

Editar: estoy usando GNU binutils (as & ld) versión 2.35.1 Aquí hay un archivo codificado en base64 que incluye la fuente y ambos ejecutables (pequeño antiguo, grande nuevo):

 cat << EOF | base64 -d | tar xj QlpoOTFBWSZTWVaGrEQABBp////xebj/7//Xf+a8RP/v3/rAAEVARARAeEADBAAAoCAI0AQ+NAam ytMpCGmpDVPU0aNpGmh6Rpo9QAAeoBoADQaNAADQ09IAACSSGUwaJpTNQGE9QZGhoADQPUAA0AAA AA0aA4AAAABoAAAAA0GgAAAAZAGgAHAAAAANAAAAAGg0AAAADIA0AASJCBIyE8hHpqPVPUPU/VAa fqn6o0ep6BB6TQaNGj0j1ABobU00yeU9JYiuVVZKYE+dKNa3wls6x81yBpGAN71NoylDUvNryWiW E4ER8XkfpaJcPb6ND12ULEqkQX3eaBHP70Apa5uFhWNDy+U3Ekj+OLx5MtDHxQHQLfMcgCHrGayE Dc76F4ZC4rcRkvTW4S2EbJAsbBGbQxSbx5o48zkyk5iPBBhJowtCSwDBsQBc0koYRSO6SgJNL0Bg EmCoxCDAs5QkEmTGmQUgqZNIoxsmwDmDQe0NIDI0KjQ64leOr1fVk6AaVhjOAJjLrEYkYy4cDbyS iXSuILWohNh+PA9Izk0YUM4TQQGEYNgn4oEjGmAByO+kzmDIxEC3Txni6E1WdswBJLKYiANdiQ2K 00jU/zpMzuIhjTbgiBqE24dZWBcNBBAAioiEhCQEIfAR8Vir4zNQZFgvKZa67Jckh6EHZWAWuf6Q kGy1lOtA2h9fsyD/uPPI2kjvoYL+w54IUKBEEYFBIWRNCNpuyY86v3pNiHEB7XyCX5wDjZUSF2tO w0PVlY2FQNcLQcbZjmMhZdlCGkVHojuICHMMMB5kQQSZRwNJkYTKz6stT/MTWmozDCcj+UjtB9Cf CUqAqqRlgJdREtMtSO4S4GpJE2I/P8vuO9ckqCM2+iSJCLRWx2Gi8VSR8BIkVX6stqIDmtG8xSVU kk7BnC5caZXTIynyI0doXiFY1+/Csw2RUQJroC0lCNiIqVVUkTqTRMYqKNVGtCJ5yfo7e3ZpgECk PYUEihPU0QVgfQ76JA8Eb16KCbSzP3WYiVApqmfDhUk0aVc+jyBJH13uKztUuva8F4YdbpmzomjG kSJmP+vCFdKkHU384LdRoO0LdN7VJlywJ2xJdM+TMQ0KhMaicvRqfC5pHSu+gVDVjfiss+S00ikI DeMgatVKKtcjsVDX09XU3SzowLWXXunnFZp/fP3eN9Rj1ubiLc0utMl3CUUkcYsmwbKKrWhaZiLO u67kMSsW20jVBcZ5tZUKgdRtu0UleWOs1HK2QdMpyKMxTRHWhhHwMnVEsWIUEjIfFEbWhRTRMJXn oIBSEa2Q0llTBfJV0LEYEQTBTFsDKIxhgqNwZB2dovl/kiW4TLp6aGXxmoIpVeWTEXqg1PnyKwux caORGyBhTEPV2G7/O3y+KeAL9mUM4Zjl1DsDKyTZy8vgn31EDY08rY+64Z/LO5tcRJHttMYsz0Fh CRN8LTYJL/I/4u5IpwoSCtDViIA= EOF

Actualización: cuando se usa ld.gold en lugar de ld.bfd (al que /usr/bin/ld está vinculado de manera predeterminada), el tamaño del ejecutable se vuelve tan pequeño como se esperaba:

 $ cat Makefile TARGET=helloworld all: as -32 -o ${TARGET}-asm.o ${TARGET}-asm.s ld.bfd -melf_i386 -o ${TARGET}-asm-bfd ${TARGET}-asm.o ld.gold -melf_i386 -o ${TARGET}-asm-gold ${TARGET}-asm.o rm ${TARGET}-asm.o $ make -q $ ls -l total 68 -rw-r--r-- 1 eso eso 200 Dec 1 13:57 Makefile -rwxrwxr-x 1 eso eso 8700 Dec 1 13:57 helloworld-asm-bfd -rwxrwxr-x 1 eso eso 732 Dec 1 13:57 helloworld-asm-gold -rw-r--r-- 1 eso eso 498 Dec 1 13:44 helloworld-asm.s

Tal vez solo usé gold anteriormente sin darme cuenta.

over 4 years ago · Santiago Trujillo
1 answers
Answer question

0

No es 10x en general, es la alineación de página de un par de secciones como dice Jester, según los cambios en el script de enlace predeterminado de ld por razones de seguridad:

  • Primer cambio: asegurarse de que los datos de .data no estén presentes en ninguna de las asignaciones de .text , de modo que ninguno de esos datos estáticos esté disponible para los dispositivos ROP/Spectre en una página ejecutable. (En ld más antiguo, eso significaba que los encabezados de programa asignaban el mismo bloque de disco dos veces, también en un segmento RW sin ejecución para la sección .data real. La asignación ejecutable aún era de solo lectura).

  • Cambio más reciente: separe .rodata de .text en segmentos separados, de nuevo para que los datos estáticos no se asignen a una página ejecutable. Anteriormente, const char code[]= {...} podía convertirse en un puntero de función y llamarse, sin necesidad de mprotect o gcc -z execstack u otros trucos, si quería probar Shellcode de esa manera. (Un cambio de kernel de Linux separado realizado -z execstack solo se aplica a la pila real, no a READ_IMPLIES_EXEC).

  • Consulte ¿Por qué un ejecutable ELF podría tener 4 segmentos LOAD? para este historial, incluido el hecho extraño de que .rodata está en un segmento separado del mapeo de solo lectura para acceder a los metadatos de ELF.

Ese espacio adicional es solo 00 de relleno y se comprimirá bien en un .tar.gz o lo que sea.

Por lo tanto, tiene un límite superior en el peor de los casos de aproximadamente 2x 4k páginas adicionales de relleno, y los ejecutables diminutos están cerca del peor de los casos.

gcc -Wl,--nmagic desactivará la alineación de página de las secciones si lo desea por algún motivo. (vea la página de manual ld(1) ) No sé por qué eso no empaca todo al tamaño anterior. Tal vez verificar el script del enlazador predeterminado arrojaría algo de luz, pero es bastante largo. Ejecute ld --verbose para verlo.

strip el ping no ayudará con el relleno que es parte de una sección; Creo que solo puede eliminar secciones enteras.

ld -z noseparate-code usa el diseño anterior, solo 2 segmentos en total para cubrir las secciones .text y .rodata , y las secciones .data y .bss . (Y los metadatos ELF a los que quiere acceder el enlace dinámico).


Relacionados:

Vinculación con gcc en lugar de ld

Esta pregunta es sobre ld , pero tenga en cuenta que si está usando gcc -nostdlib , también solía hacer un ejecutable estático por defecto. Pero las distribuciones modernas de Linux configuran GCC con -pie como predeterminado, y GCC no creará un pastel estático de manera predeterminada , incluso si no hay bibliotecas compartidas vinculadas. A diferencia del modo -no-pie donde simplemente hará un ejecutable estático en ese caso. (Un pastel estático aún necesita código de inicio para aplicar reubicaciones para cualquier dirección absoluta).

Entonces, el equivalente de ld directamente es gcc -nostdlib -static (lo que implica -no-pie ). O gcc -nostdlib -no-pie debería dejarlo predeterminado en -static cuando no hay bibliotecas compartidas vinculadas. Puede combinar esto con -Wl,--nmagic y/o -Wl,-z -Wl,noseparate-code .

También:

  • Un tutorial torbellino sobre la creación de ejecutables ELF realmente pequeños para Linux : finalmente, hacer un ejecutable de 45 bytes, con el código de máquina para una llamada al _exit metido en el encabezado del programa ELF.

  • FASM puede crear ejecutables bastante pequeños, utilizando su modo en el que genera un ejecutable estático (no un archivo de objeto) directamente sin metadatos de la sección ELF, solo encabezados de programa. (Es complicado depurar con GDB o desensamblar con objdump; la mayoría de las herramientas asumen que habrá encabezados de sección, aunque no son necesarios para ejecutar ejecutables estáticos).

  • ¿Cuál es un número mínimo razonable de instrucciones de ensamblaje para un pequeño programa en C, incluida la configuración?

  • ¿Cuál es la diferencia entre "vinculado estáticamente" y "no un ejecutable dinámico" de Linux ldd? (estática frente a tarta estática frente a tarta (dinámica) que no tiene bibliotecas compartidas).

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!