Exemple exécutable minimal
Si un concept n'est pas clair, il y a un exemple plus simple que vous n'avez pas vu qui l'explique.
Dans ce cas, cet exemple est le monde bonjour de l'assemblage Linux x86_64 (pas de libc):
bonjour.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 en amont .
Assemblez et exécutez:
as -o hello.o hello.S
ld -o hello.out hello.o
./hello.out
Produit les résultats attendus:
hello
Maintenant, utilisons strace sur cet exemple:
env -i ASDF=qwer strace -o strace.log -s999 -v ./hello.out arg0 arg1
cat strace.log
Nous utilisons:
strace.log
contient désormais:
execve("./hello.out", ["./hello.out", "arg0", "arg1"], ["ASDF=qwer"]) = 0
write(1, "hello\n", 6) = 6
exit(0) = ?
+++ exited with 0 +++
Avec un exemple aussi minimal, chaque caractère de la sortie est évident:
execve
ligne: montre comment strace
exécuté hello.out
, y compris les arguments CLI et l'environnement comme documenté àman execve
write
ligne: affiche l'appel système d'écriture que nous avons effectué. 6
est la longueur de la chaîne "hello\n"
.
= 6
est la valeur de retour de l'appel système, qui, comme indiqué dans, man 2 write
est le nombre d'octets écrits.
exit
ligne: affiche l'appel système de sortie que nous avons effectué. Il n'y a pas de valeur de retour, car le programme quitte!
Exemples plus complexes
L'application de strace est bien sûr de voir quels appels système les programmes complexes font réellement pour aider à déboguer / optimiser votre programme.
Notamment, la plupart des appels système que vous êtes susceptible de rencontrer sous Linux ont des wrappers glibc, dont beaucoup proviennent de POSIX .
En interne, les wrappers glibc utilisent un assemblage en ligne plus ou moins comme ceci: Comment appeler un appel système via sysenter dans un assemblage en ligne?
L'exemple suivant que vous devriez étudier est un write
monde bonjour POSIX :
principal c
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *msg = "hello\n";
write(1, msg, 6);
return 0;
}
Compiler et exécuter:
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out
Cette fois, vous verrez qu'un tas d'appels système sont effectués par glibc avant main
de configurer un environnement agréable pour main.
C'est parce que nous n'utilisons pas maintenant un programme autonome, mais plutôt un programme glibc plus commun, qui permet la fonctionnalité libc.
Ensuite, à chaque extrémité, strace.log
contient:
write(1, "hello\n", 6) = 6
exit_group(0) = ?
+++ exited with 0 +++
Nous concluons donc que la write
fonction POSIX utilise, surprise !, l' write
appel système Linux .
On observe également que cela return 0
conduit à un exit_group
appel au lieu de exit
. Ha, je ne connaissais pas celui-ci! C'est pourquoi strace
c'est si cool. man exit_group
explique ensuite:
Cet appel système est équivalent à exit (2), sauf qu'il termine non seulement le thread appelant, mais tous les threads du groupe de threads du processus appelant.
Et voici un autre exemple où j'ai étudié quel appel système dlopen
utilise: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710
Testé dans Ubuntu 16.04, GCC 6.4.0, noyau Linux 4.4.0.