Strace 与 C 可执行文件?

Strace With C Executable?

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

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

我简单的写了:

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

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

strace ./my_program.o

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

The output you received from running strace on your program was probably very messy. And there’s no way to tell which system call was used during the execution of malloc. Can you suggest a simple addition to your C code, such that you will be able to spot the system call used during the execution of malloc anyway. BTW, You’re not allowed to add flags to strace and Your change must be made in the C code.

我在这里失去了他。可以对 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, "7ELF[=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=]>[=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=][=12=]"..., 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 不会 发出任何系统调用(例如 brksmallx(例如 23)

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

堆已经分配了足够的space,所以你的malloc可以完成请求而无需调用brk.

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


这是输入值 23strace 输出:

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, "7ELF[=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=]>[=10=][=10=][=10=][=10=] E[=10=][=10=][=10=][=10=][=10=]"..., 832) = 832
lseek(3, 792, SEEK_SET)                 = 792
read(3, "[=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=]GNU[=10=]g5=137A7:}7a661"..., 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, "[=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=]GNU[=10=]g5=137A7:}7a661"..., 68) = 68
lseek(3, 864, SEEK_SET)                 = 864
read(3, "[=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=]GNU[=10=][=10=][=10=]0[=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=][=10=]", 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, "7ELF[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]>[=11=][=11=][=11=][=11=] E[=11=][=11=][=11=][=11=][=11=]"..., 832) = 832
lseek(3, 792, SEEK_SET)                 = 792
read(3, "[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]GNU[=11=]g5=137A7:}7a661"..., 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, "[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]GNU[=11=]g5=137A7:}7a661"..., 68) = 68
lseek(3, 864, SEEK_SET)                 = 864
read(3, "[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]GNU[=11=][=11=][=11=]0[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]", 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 +++

这是我用来生成上面的程序。请注意,任何东西 生成的系统调用 不是 部分 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);
}

更新:

Can you please explain why this won't work: [redacted for space] – ariel

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

and why you said "not part of scanf"? – ariel

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

Even with large numbers I don't see malloc being called. – ariel

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

发出系统调用

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

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


更新#2:

Plus, I can't get an input to make it show sbrk() how can I do that – ariel

由各个堆管理器决定使用哪个系统调用 sbrkbrk、and/or mmap 以及何时使用。

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

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

来自 man sbrk:

On Linux, sbrk() is implemented as a library function that uses the brk() system call, and does some internal bookkeeping so that it can return the old break value.