c - Strace 与 C 可执行文件?

标签 c linux malloc system-calls strace

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

起初,我被要求编写一个简单的程序,从用户那里获取输入 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, "\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 不会x 的small 值发出任何系统调用(例如brk) (例如 23)

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

堆已经分配了足够的空间,因此您的 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, "\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 +++

这是我用来生成上述内容的程序。请注意,任何 生成的系统调用不是 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 系统调用应该正常显示。正如我提到的,任何 系统调用都可以使用 [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 和/或 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.

关于c - Strace 与 C 可执行文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68026252/

相关文章:

c - malloc函数内存管理

c - fnmatch 模式匹配

linux - bash 脚本中出现 "Unexpected end of file"错误

linux - 使串行端口通知握手线的更改

linux - 更改 Linux 命令

C 动态分配数组的问题

c - Kconfig 和 C 枚举

c - 在使用 exec 运行另一组进程之前等待并行进程

c++ - 我如何告诉 GCC 为 -l 使用自定义库而不是系统库?

从文本文件创建链接列表