Minimal lauffähiges Beispiel
Wenn ein Konzept nicht klar ist, gibt es ein einfacheres Beispiel, das Sie nicht gesehen haben und das es erklärt.
In diesem Fall ist dieses Beispiel die freistehende (keine libc) Hallo-Welt der Linux x86_64-Assembly:
hallo.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 stromaufwärts .
Zusammenbauen und ausführen:
as -o hello.o hello.S
ld -o hello.out hello.o
./hello.out
Gibt das erwartete aus:
hello
Verwenden wir nun Strace für dieses Beispiel:
env -i ASDF=qwer strace -o strace.log -s999 -v ./hello.out arg0 arg1
cat strace.log
Wir gebrauchen:
strace.log
enthält jetzt:
execve("./hello.out", ["./hello.out", "arg0", "arg1"], ["ASDF=qwer"]) = 0
write(1, "hello\n", 6) = 6
exit(0) = ?
+++ exited with 0 +++
Bei einem so minimalen Beispiel ist jedes einzelne Zeichen der Ausgabe selbstverständlich:
execve
Zeile: Zeigt an, wie sie strace
ausgeführt wird hello.out
, einschließlich der CLI-Argumente und der Umgebung, wie unter dokumentiertman execve
write
Zeile: Zeigt den Schreibsystemaufruf an, den wir durchgeführt haben. 6
ist die Länge der Zeichenfolge "hello\n"
.
= 6
ist der Rückgabewert des Systemaufrufs, der, wie in dokumentiert, man 2 write
die Anzahl der geschriebenen Bytes ist.
exit
Zeile: Zeigt den von uns getätigten Aufruf des Exit-Systems an. Es gibt keinen Rückgabewert, da das Programm beendet wurde!
Komplexere Beispiele
Die Anwendung von strace dient natürlich dazu, festzustellen, welche Systemaufrufe komplexe Programme tatsächlich ausführen, um das Debuggen / Optimieren Ihres Programms zu unterstützen.
Insbesondere haben die meisten Systemaufrufe, die unter Linux wahrscheinlich auftreten, Glibc-Wrapper, viele davon von POSIX .
Intern verwenden die glibc-Wrapper die Inline-Assembly mehr oder weniger folgendermaßen: Wie rufe ich einen Systemaufruf über sysenter in der Inline-Assembly auf?
Das nächste Beispiel, das Sie studieren sollten, ist eine POSIX- write
Hallo-Welt:
Haupt c
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *msg = "hello\n";
write(1, msg, 6);
return 0;
}
Kompilieren und ausführen:
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out
Dieses Mal werden Sie sehen, dass eine Reihe von Systemaufrufen von glibc ausgeführt werden, bevor main
eine schöne Umgebung für main eingerichtet wird.
Dies liegt daran, dass wir jetzt kein freistehendes Programm verwenden, sondern ein allgemeineres glibc-Programm, das libc-Funktionen ermöglicht.
Dann strace.log
enthält an jedem Ende :
write(1, "hello\n", 6) = 6
exit_group(0) = ?
+++ exited with 0 +++
Wir schließen daraus, dass die write
POSIX-Funktion überraschenderweise den Linux write
-Systemaufruf verwendet.
Wir beobachten auch, dass dies return 0
zu einem exit_group
Anruf statt führt exit
. Ha, ich wusste nichts davon! Deshalb strace
ist es so cool. man exit_group
dann erklärt:
Dieser Systemaufruf entspricht exit (2), außer dass nicht nur der aufrufende Thread, sondern alle Threads in der Thread-Gruppe des aufrufenden Prozesses beendet werden.
Und hier ist ein weiteres Beispiel, in dem ich untersucht habe, welche Systemaufrufe dlopen
verwendet werden: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710
Getestet in Ubuntu 16.04, GCC 6.4.0, Linux Kernel 4.4.0.