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:
execvelínea: muestra cómo se straceejecuta hello.out, incluidos los argumentos de CLI y el entorno tal como se documenta enman execve
writelínea: muestra la llamada al sistema de escritura que realizamos. 6es la longitud de la cadena "hello\n".
= 6es el valor de retorno de la llamada al sistema, que como se documenta en man 2 writeel número de bytes escritos.
exitlí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 writehola 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 mainde 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.logcontiene:
write(1, "hello\n", 6) = 6
exit_group(0) = ?
+++ exited with 0 +++
Entonces concluimos que la writefunción POSIX usa, ¡sorpresa !, la writellamada al sistema Linux .
También observamos que return 0conduce a una exit_groupllamada en lugar de exit. ¡Ja, no sabía sobre esto! Por eso stracees tan genial. man exit_groupluego 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 dlopenutiliza: /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.