为何dtruss未记录二进制程序的stdout写入系统调用?如何解决?
问题背景
我熟悉Linux下的strace,刚接触macOS的dtruss,写了如下Hello World程序:
#include <stdio.h> int main(){ printf("hello world\n"); }
在Linux上用strace ./HelloWorld运行时,能看到包含write系统调用的输出:
strace ./HelloWorld ... write(1, "hello world\n", 12hello world ) = 12
但在macOS上执行sudo dtruss -f ./Hello时,却看不到任何系统调用输出:
sudo dtruss -f ./Hello Password: dtrace: system integrity protection is on, some features will not be available PID/THRD SYSCALL(args) = return hello world
想知道:
- 为什么看不到
write系统调用? - 如何调整
dtruss的调用方式来显示系统调用? - 我原本以为系统完整性保护(SIP)仅适用于系统二进制文件,能否解释SIP对用户程序的限制依据?
解答
1. 看不到write系统调用的原因
- libc实现差异:macOS的
libc对printf的优化逻辑和Linux不同,它不会直接调用write系统调用,而是使用write_nocancel、writev这类带前缀的系统调用变体;另外stdio的缓冲机制可能导致输出缓冲未在进程运行时刷新,系统调用延迟到进程退出后才执行,而dtruss默认不会追踪这一阶段的调用。 - SIP功能限制:开启SIP后,
dtruss依赖的dtrace会被限制部分特权功能,即使是用户自行编译的程序,也无法完整追踪所有系统调用,部分探针会被禁用。
2. 调整dtruss调用方式的方法
- 指定追踪目标系统调用:直接指定要追踪的系统调用(包括变体),覆盖默认追踪列表,命令示例:
sudo dtruss -f -t write,write_nocancel,writev ./Hello - 强制触发缓冲刷新:修改程序,在
printf后添加fflush(stdout);,或者运行时将输出重定向到文件(触发缓冲强制刷新):sudo dtruss -f ./Hello > out.txt - 临时关闭SIP(仅测试用,不推荐):如果需要完整的
dtrace功能,可以重启Mac进入恢复模式,执行csrutil disable关闭SIP,测试完成后再执行csrutil enable重新开启。
3. SIP对用户程序的限制依据
SIP的设计目标是保护整个系统的完整性,并非仅针对系统二进制文件。开启SIP后,dtrace会受到以下限制:
- 禁止追踪系统进程和受保护的内核组件;
- 限制对用户进程的部分探针类型,比如无法使用
pidprovider的部分功能追踪用户级函数调用; - 即使是用户自行编译的程序,
dtrace也无法绕过SIP限制获取完整的系统调用追踪数据,因为SIP会阻止dtrace加载必要的内核模块或执行特权级追踪操作。
内容的提问来源于stack exchange,提问作者merlin2011
相关产品推荐
相关产品推荐

