用 strace 和 ltrace 找出用到的 system call 和 library call
前面提到 host 沒有 call gethostbyaddr, 面惡心善的 Scott 大概是查覺我下載了原始碼, 卻沒有找出確認它的方法。於是在另一篇留言裡說可以用 strace、ltrace 或 gdb 輕易做到這事 (幸好我還沒開始試 profiler 啊...)。
strace 和 ltrace 顧名思議, 它們會列出執行期間用到的 system / library call。若不確定有興趣的函式是那個, 可以用 man page 編號來判別。比方 man gethostbyaddr 顯示被分在 section 3 下, 所以 gethostbyaddr 是 library call [*1]。
分別拿 host 和對照組 getent 試的結果, 可以看到 getent 有打開 /etc/hosts (從 strace 那看到的), 並呼叫 gethostbyaddr;而 host 卻兩者皆無。解開疑惑實乃人生一大痛快之事, 感謝 Scott 的指點。
附上參考用指令:
$ ltrace getent hosts 127.0.0.1 2>&1 | grep gethostby
gethostbyaddr("\177", 4, 2) = 0xb7ed0aa0
$ strace getent hosts 127.0.0.1 2>&1 | grep "/etc/hosts"
open("/etc/hosts", O_RDONLY|O_CLOEXEC) = 3
$ strace host 127.0.0.1 2>&1 | grep "/etc/hosts"
$ ltrace host 127.0.0.1 2>&1 | grep gethostby
另外我好奇之下試了傳說中人人都會寫的 C 版 「Hello, world! 」, 結果發現 compiler 很聰明地用 puts 而非 printf。
參考程式如下:
#include <stdio.h>
int main(void)
{
printf("hello, world!\n");
puts("hello, world!\n");
printf("hello, world! %d\n", 3);
return 0;
}
執行結果:
$ ltrace ./f > /dev/null
__libc_start_main(0x80483f4, 1, 0xbf812ab4, 0x8048450, 0x8048440
puts("hello, world!") = 14
puts("hello, world!\n") = 15
printf("hello, world! %d\n", 3) = 16
+++ exited (status 0) +++
接著我用 ltrace 執行 python (ltrace python -c ''), 結果 ltrace 狂噴訊息卻不會停......, 今日閒暇時間用盡這待那天有緣再來研究吧。 2010-02-25 更新
Scott 在留言裡提到也可以用 LD_PRELOAD 抽換動態載入的函式來達到同樣目的 (確認是否有呼叫 gethostbyaddr)。範例程式如下 (稍微修正 Scott 的範例讓它在我的機器能正常 compile):
$ printf '#include <stdio.h>\n#include <assert.h>\nvoid gethostbyaddr(void) { assert(0); }\n' > t.c
$ gcc -fPIC -shared t.c -o t.so
$ LD_PRELOAD=./t.so getent hosts 127.0.0.1
getent: t.c:3: gethostbyaddr: Assertion `0' failed.
Aborted
$ LD_PRELOAD=./t.so host 127.0.0.1
1.0.0.127.in-addr.arpa domain name pointer localhost.
上面的範例透過 LD_PRELOAD 讓程式改用自訂的 gethostbyaddr。如此一來, 抽換掉懷疑的函式再執行指令, 就知道是否有用到了。LD_PRELOAD 的詳細說明可參考 jserv 翻譯的 《Modifying a Dynamic Library Without Changing the Source Code / 在不更動原始程式碼的前提下,修改動態程式庫》。
備註 man man 可看到各 section 的含意, 摘錄如下:
- 1 Executable programs or shell commands
- 2 System calls (functions provided by the kernel)
- 3 Library calls (functions within program libraries)
- 4 Special files (usually found in /dev)
- 5 File formats and conventions eg /etc/passwd
- 6 Games
- 7 Miscellaneous (including macro packages and conven- tions), e.g. man(7), groff(7)
- 8 System administration commands (usually only for root)
- 9 Kernel routines [Non standard]
總共也才九節, 遊戲竟然自成一節......。