【问题标题】:Strace With C Executable?带有 C 可执行文件的 Strace?
【发布时间】:2021-09-02 15:58:13
【问题描述】:

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

起初,我被要求编写一个简单的程序,从用户那里获取输入 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, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb9d48fe000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb9d42fe000
mprotect(0x7fb9d44e5000, 2097152, PROT_NONE) = 0
mmap(0x7fb9d46e5000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7fb9d46e5000
mmap(0x7fb9d46eb000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 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, 4096, 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, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
write(1, "__________________________", 26__________________________) = 26
lseek(0, -1, SEEK_CUR)                  = -1 ESPIPE (Illegal seek)
exit_group(0)                           = ?
+++ exited with 0 +++

【问题讨论】:

  • 在对malloc() 的调用周围添加一些printf()(或类似的东西),这样strace 日志就会被它们夹在中间。
  • @Frank 但 printf 将有多个系统调用,这会导致更多的混乱,因为我相信,我想调用一些虚拟系统调用(如果有这样的系统调用)只是为了让事情像我一样清楚(0 ) 之前和之后,但如果例如 malloc 也使用它们,那也无济于事
  • @Frank 尝试了你的建议,但仍然没有帮助我发现代码,因为它们是在一次写入中组合在一起的
  • 所有使用void main()的人都注定要失败。
  • @wildplasser 为什么它有什么问题?

标签: c linux malloc system-calls strace


【解决方案1】:

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

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

堆已经分配了足够的空间,所以你的malloc可以完成请求调用brk

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


这是strace 输入值23 的输出:

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, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f15ef9bd000
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
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, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f15ef7f7000
mprotect(0x7f15ef819000, 1679360, PROT_NONE) = 0
mmap(0x7f15ef819000, 1363968, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f15ef819000
mmap(0x7f15ef966000, 311296, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16f000) = 0x7f15ef966000
mmap(0x7f15ef9b3000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bb000) = 0x7f15ef9b3000
mmap(0x7f15ef9b9000, 14272, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 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, 4096, 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, tv_nsec=1000}, NULL) = 0
lseek(0, -1, SEEK_CUR)                  = -1 ESPIPE (Illegal seek)
exit_group(0)                           = ?
+++ exited with 0 +++

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

这是1000000000 值的输出:

execve("./fix1", ["./fix1"], 0x7ffe4ec746d0 /* 94 vars */) = 0
brk(NULL)                               = 0x13df000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffe5a768930) = -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) = 0x7f61b6e83000
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, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f61b6e81000
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
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, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f61b6cbb000
mprotect(0x7f61b6cdd000, 1679360, PROT_NONE) = 0
mmap(0x7f61b6cdd000, 1363968, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f61b6cdd000
mmap(0x7f61b6e2a000, 311296, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16f000) = 0x7f61b6e2a000
mmap(0x7f61b6e77000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bb000) = 0x7f61b6e77000
mmap(0x7f61b6e7d000, 14272, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f61b6e7d000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7f61b6e82500) = 0
mprotect(0x7f61b6e77000, 16384, PROT_READ) = 0
mprotect(0x403000, 4096, PROT_READ)     = 0
mprotect(0x7f61b6f00000, 4096, PROT_READ) = 0
munmap(0x7f61b6e83000, 340324)          = 0
fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x1), ...}) = 0
brk(NULL)                               = 0x13df000
brk(0x1400000)                          = 0x1400000
brk(NULL)                               = 0x1400000
read(0, "1000000000\n", 1024)           = 11
nanosleep({tv_sec=0, tv_nsec=1000}, NULL) = 0
mmap(NULL, 1000001536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f617b30e000
nanosleep({tv_sec=0, tv_nsec=1000}, NULL) = 0
lseek(0, -1, SEEK_CUR)                  = -1 ESPIPE (Illegal seek)
exit_group(0)                           = ?
+++ exited with 0 +++

这是我用来生成上述内容的程序。请注意,anything 生成的系统调用 不是scanfmalloc 的一部分,可以替换usleep 调用(例如time):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void
main()
{
    int x;

    scanf("%d", &x);
    usleep(1);
    malloc(x);
    usleep(1);
}

更新:

您能否解释一下为什么这不起作用:[已编辑空间] – ariel

使用write 应该没问题。这是“无害的”,write 系统调用应该显示得很好。正如我所提到的,任何系统调用都可以使用 [如果它与 scanfmalloc 会做的任何事情都有独特的区别]。

为什么你说“不是 scanf 的一部分”? ——爱丽儿

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

即使数字很大,我也没有看到 malloc 被调用。 ——爱丽儿

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

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

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


更新 #2:

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

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

在我的系统 [linux] 上,我使用来自 glibcmalloc。我的怀疑是它只做brk sbrk

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

来自man sbrk

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

【讨论】:

  • 另外,我无法获得输入以使其显示 sbrk() 我该怎么做
  • 例如:brk(NULL) = 0x56259e935000 brk(0x56259e956000) = 0x56259e956000 read(0, 99999 "99999\n", 1024) = 6 nanosleep({tv_sec=0, tv_nsec=1000}, NULL) = 0 nanosleep({tv_sec=0, tv_nsec=1000}, NULL) = 0 我们可以看到,在我的输入分配之前对输入 4096 的读取更大...
  • 你在帖子中提出的所有问题都很清楚并解决了我的问题,只有这个仍然开放
猜你喜欢
  • 2015-08-17
  • 2016-02-23
  • 1970-01-01
  • 1970-01-01
  • 2015-08-07
  • 2015-11-24
  • 2013-11-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多