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:
execveligne: montre comment straceexécuté hello.out, y compris les arguments CLI et l'environnement comme documenté àman execve
writeligne: affiche l'appel système d'écriture que nous avons effectué. 6est la longueur de la chaîne "hello\n".
= 6est la valeur de retour de l'appel système, qui, comme indiqué dans, man 2 writeest le nombre d'octets écrits.
exitligne: 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 writemonde 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 mainde 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.logcontient:
write(1, "hello\n", 6) = 6
exit_group(0) = ?
+++ exited with 0 +++
Nous concluons donc que la writefonction POSIX utilise, surprise !, l' writeappel système Linux .
On observe également que cela return 0conduit à un exit_groupappel au lieu de exit. Ha, je ne connaissais pas celui-ci! C'est pourquoi stracec'est si cool. man exit_groupexplique 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 dlopenutilise: /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.