linux assembly printf x86-64 glibc

linux - El uso de printf en el ensamblaje conduce a una salida vacía



assembly x86-64 (2)

Intento usar printf desde mi código de ensamblador, este es un ejemplo mínimo que solo debería imprimir hello a stdout:

.section .rodata hello: .ascii "hello/n/0" .section .text .globl _start _start: movq $hello, %rdi #first parameter xorl %eax, %eax #0 - number of used vector registers call printf #exit movq $60, %rax movq $0, %rdi syscall

Lo construyo con

gcc -nostdlib try_printf.s -o try_printf -lc

y cuando lo ejecuto, parece funcionar: la cadena hello se imprime y el estado de salida es 0 :

XXX$ ./try_printf hello XXX$ echo $? 0 XXX$

Pero cuando trato de capturar el texto, es obvio que algo no funciona correctamente:

XXX$ output=$(./try_printf) XXX$ echo $output XXX$

La output variable debe tener el valor hello , pero está vacía.

¿Qué tiene de malo mi uso de printf ?


La biblioteca estándar de C a menudo contiene código de inicialización para las secuencias de E / S estándar: código de inicialización que está omitiendo al definir su propio punto de entrada. Intente definir main lugar de _start :

.globl main main: # _start code here.

y luego compile con gcc try_printf.s -o try_printf ( es decir , sin -nostdlib ).


Use call exit lugar de una _exit raw _exit después de usar funciones stdio como printf.

Como explicó Michael, está bien vincular la biblioteca C dinámicamente. Así es también como se introduce en el libro "Programación de abajo hacia arriba" (ver capítulo 8).

Sin embargo, es importante llamar a exit desde la biblioteca C para finalizar el programa y no exit-syscall , que fue lo que hice mal al llamar a exit-syscall . Según lo insinuado por Michael, la salida hace una gran cantidad de limpieza como chorros de agua.

Eso es lo que sucedió: como se explica here , la biblioteca C almacena las secuencias estándar de la siguiente manera:

  1. Sin almacenamiento en búfer para error estándar.
  2. Si la salida / entrada estándar es una terminal, está almacenada en línea.
  3. Si la salida / entrada estándar no es un terminal, está completamente protegida y, por lo tanto, se necesita una descarga al final de la escritura.

El caso aplicable se decide cuando se llama a printf por primera vez para una secuencia.

Por lo tanto, si se llama a printf_try directamente en el terminal, se puede ver la salida del programa porque hello tiene /n al final (que activa el vaciado en el modo de almacenamiento en línea) y es un terminal, también el 2. caso.

Llamar a printf_try través de $(./printf_try) significa que el stdout ya no es un terminal (en realidad no sé si es un archivo temporal o un archivo de memoria) y, por lo tanto, el caso 3. está en vigor: hay necesidad de una descarga explícita, es decir, una llamada a C- exit .