Linux: ldd, starce, ltrace. Как работают утилиты. Трассировка вызовов.
Linux: ldd, starce, ltrace. Как работают утилиты. Трассировка вызовов.
libc — корневая библиотека языка C (В Linux мы ее видим по пути — /lib64/libc.so.6)
6 — номер версии библиотеки
По сути является ссылкой на библиотеку libc-2.17.so:
# ls -l /lib64/libc.so.6
lrwxrwxrwx. 1 root root 12 Jan 30 04:27 /lib64/libc.so.6 -> libc-2.17.so
Что будем использовать для примера?
ldd — выводит список разделяемых библиотек, используемых указанной утилитой или разделяемой библиотекой
Разделяемые библиотеки содержат реализации функций, необходимых для работы приложений, разделяемых библиотек
Разделяемые библиотеки имеют расширение — «.so» ( аналоги динамических библиотек «.dll» Windows )
Опция -v выводит информацию о версиях используемых разделяемых библиотек
Опция -u выводит список связанных, но не используемых разделяемых библиотек
strace — system trace (трассировка системных вызовов)
ltrace — library trace (трассировка библиотечных вызовов)
Посмотрим, что использует утилита date:
# which date | xargs ldd
linux-vdso.so.1 => (0x00007ffd3d3d8000)
libc.so.6 => /lib64/libc.so.6 (0x00007f931f97d000)
/lib64/ld-linux-x86-64.so.2 (0x00007f931fd4b000)
Посмотрим, как отрабатывают системные вызовы при вызове утилиты date:
# which date | xargs strace
execve(«/bin/date», [«/bin/date»], [/* 22 vars */]) = 0
brk(NULL) = 0x20ab000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f71db8b0000
access(«/etc/ld.so.preload», R_OK) = -1 ENOENT (No such file or directory)
open(«/etc/ld.so.cache», O_RDONLY|O_CLOEXEC) = 3
fstat(3,
mmap(NULL, 57295, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f71db8a2000
close(3) = 0
open(«/lib64/libc.so.6», O_RDONLY|O_CLOEXEC) = 3
read(3, «\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20&\2\0\0\0\0\0». 832) = 832
fstat(3,
mmap(NULL, 3985888, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f71db2c2000
mprotect(0x7f71db485000, 2097152, PROT_NONE) = 0
mmap(0x7f71db685000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c3000) = 0x7f71db685000
mmap(0x7f71db68b000, 16864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f71db68b000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f71db8a1000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f71db89f000
arch_prctl(ARCH_SET_FS, 0x7f71db89f740) = 0
mprotect(0x7f71db685000, 16384, PROT_READ) = 0
mprotect(0x60d000, 4096, PROT_READ) = 0
mprotect(0x7f71db8b1000, 4096, PROT_READ) = 0
munmap(0x7f71db8a2000, 57295) = 0
brk(NULL) = 0x20ab000
brk(0x20cc000) = 0x20cc000
brk(NULL) = 0x20cc000
open(«/usr/lib/locale/locale-archive», O_RDONLY|O_CLOEXEC) = 3
fstat(3,
mmap(NULL, 106075056, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f71d4d98000
close(3) = 0
open(«/etc/localtime», O_RDONLY|O_CLOEXEC) = 3
fstat(3,
fstat(3,
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f71db8af000
read(3, «TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\4\0\0\0\0». 4096) = 3519
lseek(3, -2252, SEEK_CUR) = 1267
read(3, «TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0\5\0\0\0\0». 4096) = 2252
close(3) = 0
munmap(0x7f71db8af000, 4096) = 0
fstat(1,
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f71db8af000
write(1, «Thu Mar 12 17:22:20 EDT 2020\n», 29Thu Mar 12 17:22:20 EDT 2020
) = 29
close(1) = 0
munmap(0x7f71db8af000, 4096) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++
Кратко из вывода strace (набора вызовов к ядру операционной системы) мы видим:
execve — системный вызов, указывает, что для работы команды date необходимо загрузить утилиту по пути /bin/date
open — системные вызовы, которые загружают указанные библиотеки и файлы, в которых есть зависимость (вывод ldd)
read — системные вызовы, осуществляющме чтение оных
Так-же в выводе присутствует ряд действий
Пример: «open(«/etc/localtime», O_RDONLY|O_CLOEXEC) = 3″ — считывается файл с зоной
write — собственно вывод результата работы утилиты (вывод на экран)
Пример трассировки определенных вызовов:
# which date | xargs strace -fe open,read,write,close
open(«/etc/ld.so.cache», O_RDONLY|O_CLOEXEC) = 3
close(3) = 0
open(«/lib64/libc.so.6», O_RDONLY|O_CLOEXEC) = 3
read(3, «\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20&\2\0\0\0\0\0». 832) = 832
close(3) = 0
open(«/usr/lib/locale/locale-archive», O_RDONLY|O_CLOEXEC) = 3
close(3) = 0
open(«/etc/localtime», O_RDONLY|O_CLOEXEC) = 3
read(3, «TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\4\0\0\0\0». 4096) = 3519
read(3, «TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\5\0\0\0\5\0\0\0\0». 4096) = 2252
close(3) = 0
write(1, «Thu Mar 12 17:59:02 EDT 2020\n», 29Thu Mar 12 17:59:02 EDT 2020
) = 29
close(1) = 0
close(2) = 0
+++ exited with 0 +++
Посмотрим, как отрабатывают библиотечные вызовы при вызове утилиты date:
# which date | xargs ltrace
__libc_start_main(0x401ab0, 1, 0x7ffc7e59ae08, 0x4096e0
strrchr(«/bin/date», ‘/’) = «/date»
setlocale(LC_ALL, «») = «en_US.UTF-8»
bindtextdomain(«coreutils», «/usr/share/locale») = «/usr/share/locale»
textdomain(«coreutils») = «coreutils»
__cxa_atexit(0x402c40, 0, 0, 0x736c6974756572) = 0
getopt_long(1, 0x7ffc7e59ae08, «d:f:I::r:Rs:u», 0x60d2a0, nil) = -1
nl_langinfo(0x2006c, 1, 0, 0) = 0x7f890e9a2025
clock_gettime(0, 0x7ffc7e59ac40, 0xc94440, 0) = 0
localtime(0x7ffc7e59abc0) = 0x7f8915103d20
strftime(» Thu», 1024, » %a», 0x7f8915103d20) = 4
fwrite(«Thu», 3, 1, 0x7f89150ff400) = 1
fputc(‘ ‘, 0x7f89150ff400) = 32
strftime(» Mar», 1024, » %b», 0x7f8915103d20) = 4
fwrite(«Mar», 3, 1, 0x7f89150ff400) = 1
fputc(‘ ‘, 0x7f89150ff400) = 32
fwrite(«12», 2, 1, 0x7f89150ff400) = 1
fputc(‘ ‘, 0x7f89150ff400) = 32
fwrite(«17», 2, 1, 0x7f89150ff400) = 1
fputc(‘:’, 0x7f89150ff400) = 58
fwrite(«23», 2, 1, 0x7f89150ff400) = 1
fputc(‘:’, 0x7f89150ff400) = 58
fputc(‘0’, 0x7f89150ff400) = 48
fwrite(«3», 1, 1, 0x7f89150ff400) = 1
fputc(‘ ‘, 0x7f89150ff400) = 32
strlen(«EDT») = 3
fwrite(«EDT», 3, 1, 0x7f89150ff400) = 1
fputc(‘ ‘, 0x7f89150ff400) = 32
fwrite(«2020», 4, 1, 0x7f89150ff400) = 1
__overflow(0x7f89150ff400, 10, 4, 0x30323032Thu Mar 12 17:23:03 EDT 2020
) = 10
exit(0
__fpending(0x7f89150ff400, 0, 64, 0x7f89150ffeb0) = 0
fileno(0x7f89150ff400) = 1
__freading(0x7f89150ff400, 0, 64, 0x7f89150ffeb0) = 0
__freading(0x7f89150ff400, 0, 2052, 0x7f89150ffeb0) = 0
fflush(0x7f89150ff400) = 0
fclose(0x7f89150ff400) = 0
__fpending(0x7f89150ff1c0, 0, 3328, 0xfbad000c) = 0
fileno(0x7f89150ff1c0) = 2
__freading(0x7f89150ff1c0, 0, 3328, 0xfbad000c) = 0
__freading(0x7f89150ff1c0, 0, 4, 0xfbad000c) = 0
fflush(0x7f89150ff1c0) = 0
fclose(0x7f89150ff1c0) = 0
+++ exited (status 0) +++
Из вывода ltrace мы видим:
clock_gettime — по сути функция, получающая время системы
strftime — форматирование в нужном формате
fwrite и fputc — функции вывода
Источник
Русские Блоги
Введение в команду ldd в Linux
В Linux некоторые команды являются общими для всех, например ls, rm, mv, cp и т. Д. Я не думаю, что есть необходимость в подробностях их объяснения. Некоторые команды используются только разработчиками. Как программисты, мы, как программисты, должны понимать некоторые команды, а некоторые даже нужно использовать со знанием дела.
Некоторые люди всегда говорят, что эти команды не важны, и что их можно проверить, когда они используются. Конкретные детали использования можно проверить, но, по крайней мере, вы должны знать, что есть что-то вроде ldd. Я даже не знаю ldd, откуда вы знаете, что делает ldd?
В этой статье мы познакомим вас с командой ldd, хотя она очень проста. Ой, я вдруг вспомнил, у меня есть подруга, ее зовут три символа, первая буква просто l, d, d, что отчасти интересно. В Linux ldd — это сокращение от list, dynamic, dependencies, что означает список зависимостей динамических библиотек. Конечно, вы также можете использовать ldd —help или man ldd, чтобы увидеть его использование. Ниже мы также рассмотрим:
Выполните серию компиляций и используйте команду ldd, чтобы получить:
[[email protected] learn_ldd]$ ls
main.c test.c test.h
[[email protected] learn_ldd]$ gcc -c main.c test.c
[[email protected] learn_ldd]$ gcc main.o test.o
[[email protected] learn_ldd]$ ls
a.out main.c main.o test.c test.h test.o
[[email protected] learn_ldd]$ ./a.out
rainy days
[[email protected] learn_ldd]$
[[email protected] learn_ldd]$
[[email protected] learn_ldd]$
[[email protected] learn_ldd]$ ldd *
a.out:
linux-gate.so.1 => (0x00ba1000)
libc.so.6 => /lib/libc.so.6 (0x0087e000)
/lib/ld-linux.so.2 (0x00858000)
main.c:
ldd: warning: you do not have execution permission for `./main.c’
not a dynamic executable
main.o:
ldd: warning: you do not have execution permission for `./main.o’
not a dynamic executable
test.c:
ldd: warning: you do not have execution permission for `./test.c’
not a dynamic executable
test.h:
ldd: warning: you do not have execution permission for `./test.h’
lddlibc4: cannot read header from `./test.h’
test.o:
ldd: warning: you do not have execution permission for `./test.o’
not a dynamic executable
[[email protected] learn_ldd]$
Вы можете видеть, что a.out зависит от библиотеки libc.so.6, а путь к этой библиотеке — /lib/libc.so.6
Давайте продолжим рассмотрение использования библиотек статических ссылок:
[[email protected] learn_ldd]$ ls
main.c test.c test.h
[[email protected] learn_ldd]$ gcc -c test.c
[[email protected] learn_ldd]$ ar rcs libtest.a test.o
[[email protected] learn_ldd]$ gcc main.c -L. -ltest
[[email protected] learn_ldd]$ ls
a.out libtest.a main.c test.c test.h test.o
[[email protected] learn_ldd]$ ./a.out
rainy days
[[email protected] learn_ldd]$
[[email protected] learn_ldd]$
[[email protected] learn_ldd]$
[[email protected] learn_ldd]$ ldd *
a.out:
linux-gate.so.1 => (0x00e7c000)
libc.so.6 => /lib/libc.so.6 (0x0087e000)
/lib/ld-linux.so.2 (0x00858000)
libtest.a:
ldd: warning: you do not have execution permission for `./libtest.a’
not a dynamic executable
main.c:
ldd: warning: you do not have execution permission for `./main.c’
not a dynamic executable
test.c:
ldd: warning: you do not have execution permission for `./test.c’
not a dynamic executable
test.h:
ldd: warning: you do not have execution permission for `./test.h’
lddlibc4: cannot read header from `./test.h’
test.o:
ldd: warning: you do not have execution permission for `./test.o’
not a dynamic executable
[[email protected] learn_ldd]$
На этот раз с использованием статической библиотеки результат все тот же, сказать нечего.
Давайте продолжим рассмотрение ситуации при использовании библиотек динамической компоновки:
[[email protected] learn_ldd]$ ls
main.c test.c test.h
[[email protected] learn_ldd]$ gcc -c test.c
[[email protected] learn_ldd]$ gcc -shared -fPIC -o libtest.so test.o
[[email protected] learn_ldd]$ gcc main.c -L. -ltest
[[email protected] learn_ldd]$ ls
a.out libtest.so main.c test.c test.h test.o
[[email protected] learn_ldd]$ ./a.out
./a.out: error while loading shared libraries: libtest.so: cannot open shared object file: No such file or directory
[[email protected] learn_ldd]$
[[email protected] learn_ldd]$
[[email protected] learn_ldd]$
[[email protected] learn_ldd]$ ldd *
a.out:
linux-gate.so.1 => (0x00f3d000)
libtest.so => not found
libc.so.6 => /lib/libc.so.6 (0x0087e000)
/lib/ld-linux.so.2 (0x00858000)
libtest.so:
linux-gate.so.1 => (0x0031d000)
libc.so.6 => /lib/libc.so.6 (0x00110000)
/lib/ld-linux.so.2 (0x00858000)
main.c:
ldd: warning: you do not have execution permission for `./main.c’
not a dynamic executable
test.c:
ldd: warning: you do not have execution permission for `./test.c’
not a dynamic executable
test.h:
ldd: warning: you do not have execution permission for `./test.h’
lddlibc4: cannot read header from `./test.h’
test.o:
ldd: warning: you do not have execution permission for `./test.o’
not a dynamic executable
[[email protected] learn_ldd]$
[[email protected] learn_ldd]$ su root
Password:
[[email protected] learn_ldd]# cp libtest.so /usr/lib/
[[email protected] learn_ldd]# ./a.out
rainy days
[[email protected] learn_ldd]# exit
exit
[[email protected] learn_ldd]$ ./a.out
rainy days
[[email protected] learn_ldd]$
[[email protected] learn_ldd]$
[[email protected] learn_ldd]$
[[email protected] learn_ldd]$ ldd a.out
linux-gate.so.1 => (0x00510000)
libtest.so => /usr/libtest.so (0x00fe3000)
libc.so.6 => /lib/libc.so.6 (0x0087e000)
/lib/ld-linux.so.2 (0x00858000)
Прежде всего, мы видим, что a.out зависит от библиотеки libtest.so, но результата не обнаружено. Почему? Поскольку в / usr / lib нет libtest.so, позже я скопировал libtest.so (требуются права root), и все было в порядке. Кроме того, мы также должны увидеть, что зависимые библиотеки libtest.so также можно найти с помощью команды ldd.
Конечно, если вы не хотите писать свою собственную программу, но хотите попробовать команду ldd, вы можете сделать это напрямую следующим образом:
[[email protected] learn_ldd]$ ldd /bin/ls
linux-gate.so.1 => (0x0052b000)
libselinux.so.1 => /lib/libselinux.so.1 (0x00b52000)
librt.so.1 => /lib/librt.so.1 (0x00a5c000)
libcap.so.2 => /lib/libcap.so.2 (0x0489c000)
libacl.so.1 => /lib/libacl.so.1 (0x048c9000)
libc.so.6 => /lib/libc.so.6 (0x0087e000)
libdl.so.2 => /lib/libdl.so.2 (0x00a0c000)
/lib/ld-linux.so.2 (0x00858000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00a13000)
libattr.so.1 => /lib/libattr.so.1 (0x04d99000)
[[email protected] learn_ldd]$ ldd /bin/mv
linux-gate.so.1 => (0x00944000)
libselinux.so.1 => /lib/libselinux.so.1 (0x00b52000)
librt.so.1 => /lib/librt.so.1 (0x00a5c000)
libacl.so.1 => /lib/libacl.so.1 (0x048c9000)
libattr.so.1 => /lib/libattr.so.1 (0x04d99000)
libc.so.6 => /lib/libc.so.6 (0x00110000)
libdl.so.2 => /lib/libdl.so.2 (0x00a0c000)
/lib/ld-linux.so.2 (0x00858000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00a13000)
[[email protected] learn_ldd]$
При реальной разработке и отладке Linux необходимо часто проверять зависимости динамических библиотек, ldd все еще используется довольно часто, особенно когда происходит сбой. Хорошо, здесь кратко представлена команда ldd. Несмотря на простоту, она очень практична, поэтому вы должны ее знать.
Источник