Exemplo mínimo executável
Se um conceito não estiver claro, há um exemplo mais simples que você ainda não viu que o explica.
Nesse caso, esse exemplo é o hello world independente do assembly Linux x86_64:
ola.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 upstream .
Montar e executar:
as -o hello.o hello.S
ld -o hello.out hello.o
./hello.out
Produz o esperado:
hello
Agora vamos usar strace nesse exemplo:
env -i ASDF=qwer strace -o strace.log -s999 -v ./hello.out arg0 arg1
cat strace.log
Nós usamos:
strace.log agora contém:
execve("./hello.out", ["./hello.out", "arg0", "arg1"], ["ASDF=qwer"]) = 0
write(1, "hello\n", 6) = 6
exit(0) = ?
+++ exited with 0 +++
Com um exemplo tão minimalista, todos os caracteres da saída são evidentes:
execveline: mostra como é straceexecutado hello.out, incluindo argumentos e ambiente da CLI, conforme documentado emman execve
writelinha: mostra a chamada do sistema de gravação que fizemos. 6é o comprimento da string "hello\n".
= 6é o valor de retorno da chamada do sistema, que conforme documentado man 2 writeé o número de bytes gravados.
exitline: mostra a chamada do sistema de saída que fizemos. Não há valor de retorno, pois o programa foi encerrado!
Exemplos mais complexos
A aplicação do strace é, obviamente, para ver quais chamadas do sistema os programas complexos estão realmente fazendo para ajudar a depurar / otimizar seu programa.
Notavelmente, a maioria das chamadas de sistema que você provavelmente encontrará no Linux possui wrappers glibc, muitas delas do POSIX .
Internamente, os wrappers glibc usam montagem inline mais ou menos assim: Como chamar uma chamada de sistema via sysenter na montagem inline?
O próximo exemplo que você deve estudar é um writehello world POSIX :
main.c
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *msg = "hello\n";
write(1, msg, 6);
return 0;
}
Compile e execute:
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out
Desta vez, você verá que várias chamadas de sistema estão sendo feitas pela glibc antes mainpara configurar um ambiente agradável para main.
Isso ocorre porque agora não estamos usando um programa independente, mas um programa glibc mais comum, que permite a funcionalidade libc.
Então, em cada extremidade, strace.logcontém:
write(1, "hello\n", 6) = 6
exit_group(0) = ?
+++ exited with 0 +++
Portanto, concluímos que a writefunção POSIX usa, surpresa !, a writechamada de sistema Linux .
Também observamos que return 0leva a uma exit_groupchamada em vez de exit. Ha, eu não sabia sobre este! É por straceisso que é tão legal. man exit_groupentão explica:
Essa chamada do sistema é equivalente à exit (2), exceto que ela finaliza não apenas o encadeamento de chamada, mas todos os encadeamentos no grupo de encadeamentos do processo de chamada.
E aqui está outro exemplo em que estudei qual chamada de sistema dlopenusa: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710
Testado no Ubuntu 16.04, GCC 6.4.0, kernel do Linux 4.4.0.