Strace 与 C 可执行文件?

问题描述

我在最近的求职面试中遇到了多个简单的问题。

起初,我被要求编写一个简单的程序,从用户那里获取输入 x 并在内存中分配(使用 malloc)x 个字节。

我简单地写道:

void main()
{
    int x;
    scanf("%d",&x);
    malloc(x);
}

然后我被告知在运行我的可执行文件显示所有被调用的系统调用,所以我去了终端并输入:

strace ./my_program.o

这很好,直到他问了类似的问题:

您在程序上运行 strace 时收到的输出是 可能很乱。并且无法判断哪个系统调用是 在 malloc 执行期间使用。你能建议一个简单的补充吗 到您的 C 代码,以便您能够发现系统调用 无论如何在执行 malloc 期间使用。顺便说一句,你不能 向 strace 添加标志并且您的更改必须在 C 代码中进行。

在这里失去了他。可以对 C 代码进行哪些添加


您的建议的输出示例(仍然没有帮助,因为出于奇怪的原因只有一次写入而不是 2,因为我将 C 代码更改为在 malloc 之前有一个,在 malloc 之后有一个

execve("./a.out",["./a.out"],0x7ffc38701620 /* 50 vars */) = 0
brk(NULL)                               = 0x55df6cc1b000
access("/etc/ld.so.nohwcap",F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload",R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD,"/etc/ld.so.cache",O_RDONLY|O_CLOEXEC) = 3
fstat(3,{st_mode=S_IFREG|0644,st_size=96020,...}) = 0
mmap(NULL,96020,PROT_READ,MAP_PRIVATE,3,0) = 0x7fb9d4900000
close(3)                                = 0
access("/etc/ld.so.nohwcap",F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD,"/lib/x86_64-linux-gnu/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\35\2\0\0\0\0\0"...,832) = 832
fstat(3,{st_mode=S_IFREG|0755,st_size=2030928,8192,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) = 0x7fb9d48fe000
mmap(NULL,4131552,PROT_READ|PROT_EXEC,MAP_PRIVATE|MAP_DENYWRITE,0) = 0x7fb9d42fe000
mprotect(0x7fb9d44e5000,2097152,PROT_NONE) = 0
mmap(0x7fb9d46e5000,24576,MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE,0x1e7000) = 0x7fb9d46e5000
mmap(0x7fb9d46eb000,15072,MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,0) = 0x7fb9d46eb000
close(3)                                = 0
arch_prctl(ARCH_SET_FS,0x7fb9d48ff500) = 0
mprotect(0x7fb9d46e5000,16384,PROT_READ) = 0
mprotect(0x55df6b542000,4096,PROT_READ) = 0
mprotect(0x7fb9d4918000,PROT_READ) = 0
munmap(0x7fb9d4900000,96020)           = 0
fstat(0,{st_mode=S_IFCHR|0620,st_rdev=makedev(136,0),...}) = 0
brk(NULL)                               = 0x55df6cc1b000
brk(0x55df6cc3c000)                     = 0x55df6cc3c000
read(0,"\n",1024)                     = 1
read(0,5
"5\n",1024)                    = 2
fstat(1,...}) = 0
write(1,"__________________________",26__________________________) = 26
lseek(0,SEEK_CUR)                  = -1 ESPIPE (Illegal seek)
exit_group(0)                           = ?
+++ exited with 0 +++

解决方法

您的malloc不会brk值(例如23)发出任何系统调用(例如x

那是因为之前的 scanf 调用必须[内部]调用 malloc。内部 malloc 调用 brk

堆已经分配了足够的空间,因此您的 malloc 可以满足请求调用 brk

为了能够看到这一点,在您的 usleep(1) 调用之前和之后放置一个 malloc。这会生成对 nanosleep 系统调用的无害调用,这些调用充当您的 malloc 调用周围的标记。


这是输入值 strace23 输出:

execve("./fix1",["./fix1"],0x7ffea3366ac0 /* 94 vars */) = 0
brk(NULL)                               = 0x15fc000
arch_prctl(0x3001 /* ARCH_??? */,0x7ffcf4a51d40) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload",R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD,"/etc/ld.so.cache",O_RDONLY|O_CLOEXEC) = 3
fstat(3,{st_mode=S_IFREG|0644,st_size=340324,...}) = 0
mmap(NULL,340324,PROT_READ,MAP_PRIVATE,3,0) = 0x7f15ef9bf000
close(3)                                = 0
openat(AT_FDCWD,"/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 E\2\0\0\0\0\0"...,832) = 832
lseek(3,792,SEEK_SET)                 = 792
read(3,"\4\0\0\0\24\0\0\0\3\0\0\0GNU\0g\225=\371\27\203\227A\277:}7a\216\376\301"...,68) = 68
fstat(3,{st_mode=S_IFREG|0755,st_size=2786704,8192,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) = 0x7f15ef9bd000
lseek(3,68) = 68
lseek(3,864,SEEK_SET)                 = 864
read(3,"\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0",32) = 32
mmap(NULL,1857472,MAP_PRIVATE|MAP_DENYWRITE,0) = 0x7f15ef7f7000
mprotect(0x7f15ef819000,1679360,PROT_NONE) = 0
mmap(0x7f15ef819000,1363968,PROT_READ|PROT_EXEC,MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE,0x22000) = 0x7f15ef819000
mmap(0x7f15ef966000,311296,0x16f000) = 0x7f15ef966000
mmap(0x7f15ef9b3000,24576,0x1bb000) = 0x7f15ef9b3000
mmap(0x7f15ef9b9000,14272,MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS,0) = 0x7f15ef9b9000
close(3)                                = 0
arch_prctl(ARCH_SET_FS,0x7f15ef9be500) = 0
mprotect(0x7f15ef9b3000,16384,PROT_READ) = 0
mprotect(0x403000,4096,PROT_READ)     = 0
mprotect(0x7f15efa3c000,PROT_READ) = 0
munmap(0x7f15ef9bf000,340324)          = 0
fstat(0,{st_mode=S_IFCHR|0620,st_rdev=makedev(0x88,0x1),...}) = 0
brk(NULL)                               = 0x15fc000
brk(0x161d000)                          = 0x161d000
brk(NULL)                               = 0x161d000
read(0,"23\n",1024)                   = 3
nanosleep({tv_sec=0,tv_nsec=1000},NULL) = 0
nanosleep({tv_sec=0,NULL) = 0
lseek(0,SEEK_CUR)                  = -1 ESPIPE (Illegal seek)
exit_group(0)                           = ?
+++ exited with 0 +++

但是,值会导致您的 malloc 发出 mmap 系统调用。

这是 1000000000 值的输出:

execve("./fix1",0x7ffe4ec746d0 /* 94 vars */) = 0
brk(NULL)                               = 0x13df000
arch_prctl(0x3001 /* ARCH_??? */,0x7ffe5a768930) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload",0) = 0x7f61b6e83000
close(3)                                = 0
openat(AT_FDCWD,0) = 0x7f61b6e81000
lseek(3,0) = 0x7f61b6cbb000
mprotect(0x7f61b6cdd000,PROT_NONE) = 0
mmap(0x7f61b6cdd000,0x22000) = 0x7f61b6cdd000
mmap(0x7f61b6e2a000,0x16f000) = 0x7f61b6e2a000
mmap(0x7f61b6e77000,0x1bb000) = 0x7f61b6e77000
mmap(0x7f61b6e7d000,0) = 0x7f61b6e7d000
close(3)                                = 0
arch_prctl(ARCH_SET_FS,0x7f61b6e82500) = 0
mprotect(0x7f61b6e77000,PROT_READ)     = 0
mprotect(0x7f61b6f00000,PROT_READ) = 0
munmap(0x7f61b6e83000,...}) = 0
brk(NULL)                               = 0x13df000
brk(0x1400000)                          = 0x1400000
brk(NULL)                               = 0x1400000
read(0,"1000000000\n",1024)           = 11
nanosleep({tv_sec=0,NULL) = 0
mmap(NULL,1000001536,0) = 0x7f617b30e000
nanosleep({tv_sec=0,SEEK_CUR)                  = -1 ESPIPE (Illegal seek)
exit_group(0)                           = ?
+++ exited with 0 +++

这是我用来生成上述内容的程序。请注意,任何生成的系统调用不是属于 scanfmalloc 的一部分都可以替换 usleep 调用(例如 {{ 1}}):

time

更新:

你能解释一下为什么这行不通吗:[为空间编辑] – ariel

使用 #include <stdio.h> #include <stdlib.h> #include <unistd.h> void main() { int x; scanf("%d",&x); usleep(1); malloc(x); usleep(1); } 应该没问题。它是“无害的”,write 系统调用应该可以正常显示。正如我所提到的,任何系统调用都可以使用[如果它与writescanf会做的任何事情有唯一区别]。

为什么你说“不是 scanf 的一部分”? – 阿里尔

因为[正如我所提到的] malloc 会执行一个 scanf [和一个 malloc--但这可能不会生成系统调用]。

即使数字很大,我也看不到 malloc 被调用。 – 阿里尔

我不确定您的设置中发生了什么。我使用了 1,000,000 来真正强制您的 free 发出系统调用。

malloc 或您的 nanosleep 系统调用都应该用作标记。

您是否在标记调用之间的跟踪中看到 write 调用 [或更多 mmap 调用]?


更新 #2:

另外,我无法获得输入以使其显示 sbrk() 我该怎么做 – ariel

由各个堆管理器决定将使用哪些系统调用 brksbrk 和/或 brk 以及何时使用。

在我的系统 [linux] 上,我使用的是 mmap 中的 malloc。我怀疑它只做 glibc brk

sbrkbrk 非常相似,以至于给定的经理可能会使用其中一个,但不会同时使用两者——YMMV。

来自sbrk

在 Linux 上,sbrk() 被实现为一个库函数,它使用 brk() 系统调用,并进行一些内部簿记,以便它可以 返回旧的中断值。