Приклад мінімальної експлуатації
Якщо концепція не зрозуміла, є більш простий приклад, який ви ще не бачили, що пояснює це.
У цьому випадку, таким прикладом є привітний світ (без libc) збірки Linux x86_64:
привіт.С
.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 вище за течією .
Зберіть і запустіть:
as -o hello.o hello.S
ld -o hello.out hello.o
./hello.out
Виводить очікувані:
hello
Тепер давайте використаємо стразе на цьому прикладі:
env -i ASDF=qwer strace -o strace.log -s999 -v ./hello.out arg0 arg1
cat strace.log
Ми використовуємо:
strace.log
тепер містить:
execve("./hello.out", ["./hello.out", "arg0", "arg1"], ["ASDF=qwer"]) = 0
write(1, "hello\n", 6) = 6
exit(0) = ?
+++ exited with 0 +++
Маючи такий мінімальний приклад, кожен окремий символ виводить себе очевидним:
execve
рядок: показує, як strace
виконується hello.out
, включаючи аргументи CLI та середовище, як це зафіксовано вman execve
write
рядок: показує виклик системи запису, який ми здійснили. 6
- довжина струни "hello\n"
.
= 6
- це повернене значення системного виклику, яке, як задокументовано, man 2 write
- це кількість записаних байтів.
exit
рядок: показує виклик системи виходу, який ми здійснили. Поверненого значення немає, оскільки програма вийшла з роботи!
Більш складні приклади
Застосування стрази - це, звичайно, щоб побачити, які системні виклики складні програми насправді роблять, щоб допомогти налагодити / оптимізувати вашу програму.
Зокрема, більшість системних дзвінків, з якими ви, швидше за все, зустрічаєтесь в Linux, мають обгортки glibc, багато з яких є POSIX .
Внутрішньо, обгортки glibc використовують вбудовану збірку більш-менш так: Як викликати системний виклик через sysenter під час вбудованого монтажу?
Наступний приклад, який ви повинні вивчити, - це write
світ привіт POSIX :
main.c
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *msg = "hello\n";
write(1, msg, 6);
return 0;
}
Складіть і запустіть:
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out
Цього разу ви побачите, що glibc здійснює купу системних дзвінків, main
щоб створити гарне середовище для main.
Це тому, що зараз ми не використовуємо окрему програму, а більш звичну програму glibc, яка дозволяє функціонувати libc.
Потім на кожному кінці strace.log
міститься:
write(1, "hello\n", 6) = 6
exit_group(0) = ?
+++ exited with 0 +++
Отже, ми робимо висновок, що write
функція POSIX використовує, здивуйте !, write
системний виклик Linux .
Ми також спостерігаємо, що return 0
призводить до exit_group
дзвінка замість exit
. Ха, я не знав про це! Ось чому strace
так круто. man exit_group
потім пояснює:
Цей системний виклик еквівалентний виходу (2), за винятком того, що він завершує не тільки викликовий потік, але й усі потоки в групі потоків процесу виклику.
Ось ще один приклад, де я вивчив, який системний виклик dlopen
використовує: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710
Тестовано в Ubuntu 16.04, GCC 6.4.0, Linux ядрі 4.4.0.