Ejemplo ejecutable mínimo
Si un concepto no está claro, hay un ejemplo más simple que no ha visto que lo explica.
En este caso, ese ejemplo es el ensamblado Linux x86_64 hello world independiente (sin libc):
hola s
.text
.global _start
_start:
/* write */
mov $1, %rax /* syscall number */
mov $1, %rdi /* stdout */
mov $msg, %rsi /* buffer */
mov $len, %rdx /* buffer len */
syscall
/* exit */
mov $60, %rax /* exit status */
mov $0, %rdi /* syscall number */
syscall
msg:
.ascii "hello\n"
len = . - msg
GitHub aguas arriba .
Montar y ejecutar:
as -o hello.o hello.S
ld -o hello.out hello.o
./hello.out
Salidas de lo esperado:
hello
Ahora usemos strace en ese ejemplo:
env -i ASDF=qwer strace -o strace.log -s999 -v ./hello.out arg0 arg1
cat strace.log
Usamos:
strace.log
ahora contiene:
execve("./hello.out", ["./hello.out", "arg0", "arg1"], ["ASDF=qwer"]) = 0
write(1, "hello\n", 6) = 6
exit(0) = ?
+++ exited with 0 +++
Con un ejemplo tan mínimo, todos los caracteres de la salida son evidentes:
execve
línea: muestra cómo se strace
ejecuta hello.out
, incluidos los argumentos de CLI y el entorno tal como se documenta enman execve
write
línea: muestra la llamada al sistema de escritura que realizamos. 6
es la longitud de la cadena "hello\n"
.
= 6
es el valor de retorno de la llamada al sistema, que como se documenta en man 2 write
el número de bytes escritos.
exit
línea: muestra la llamada al sistema de salida que hemos realizado. ¡No hay valor de retorno, ya que el programa se cerró!
Ejemplos más complejos
La aplicación de strace es, por supuesto, para ver qué llamadas al sistema están haciendo los programas complejos para ayudar a depurar / optimizar su programa.
Notablemente, la mayoría de las llamadas al sistema que es probable que encuentre en Linux tienen envoltorios glibc, muchos de ellos de POSIX .
Internamente, los envoltorios glibc usan el ensamblaje en línea más o menos de esta manera: ¿Cómo invocar una llamada del sistema a través de sysenter en el ensamblaje en línea?
El siguiente ejemplo que debes estudiar es un POSIX write
hola mundo:
C Principal
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *msg = "hello\n";
write(1, msg, 6);
return 0;
}
Compilar y ejecutar:
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out
Esta vez, verá que glibc está haciendo un montón de llamadas al sistema antes main
de configurar un entorno agradable para main.
Esto se debe a que ahora no estamos utilizando un programa independiente, sino más bien un programa glibc más común, que permite la funcionalidad libc.
Luego, en cada extremo, strace.log
contiene:
write(1, "hello\n", 6) = 6
exit_group(0) = ?
+++ exited with 0 +++
Entonces concluimos que la write
función POSIX usa, ¡sorpresa !, la write
llamada al sistema Linux .
También observamos que return 0
conduce a una exit_group
llamada en lugar de exit
. ¡Ja, no sabía sobre esto! Por eso strace
es tan genial. man exit_group
luego explica:
Esta llamada al sistema es equivalente a la salida (2), excepto que termina no solo el hilo de llamada, sino todos los hilos en el grupo de hilos del proceso de llamada.
Y aquí hay otro ejemplo en el que estudié qué llamada de sistema dlopen
utiliza: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710
Probado en Ubuntu 16.04, GCC 6.4.0, kernel de Linux 4.4.0.