为什么在将代码直接编译为可执行文件而不是编译为目标文件然后手动链接时会得到额外的系统调用?

Why do I get extra system calls when compiling code directly to an executable vs. compiling to an object file and then manually linking?

我想在 Ubuntu 上使用 GNU C 编译器编译此 C 代码而不链接任何标准库,只执行以下代码。

static void exit(long long code)
  {asm inline
  ("movq ,%%rax\n"
  "movq %[code],%%rdi\n"
  "syscall"
  :
  :[code]"rm"(code)
  :"rax"
  ,"rdi");}
static void write(long long fd,char *msg,long long len)
  {asm inline
  ("movq [=10=]x1,%%rax\n"
  "movq %[fd],%%rdi\n"
  "movq %[msg],%%rsi\n"
  "movq %[len],%%rdx\n"
  "syscall"
  :
  :[fd]"rm"(fd)
  ,[msg]"rm"(msg)
  ,[len]"rm"(len)
  :"rax"
  ,"rdi"
  ,"rsi"
  ,"rdx");}
#define PRINT(msg) write(1,msg,sizeof(msg))
void _start()
  {PRINT("Hello World.\n");
  exit(0);}

我用cc example.c -ffreestanding -nostartfiles -O3 -o example编译。

当我调用输出文件时,我看到很多额外的系统调用 strace 本不应该存在的:

  1. brk
  2. arch_prctl
  3. 访问
  4. mmap
  5. arch_prctl
  6. 保护

然后我这样编译:cc example.c -c -O3 -o example.o; ld example.o -o example 并且它没有执行额外的系统调用。它甚至使文件大小变小了。

它的objdump -d完全一样。在 objdump -D 中,与第二种情况相比,我在第一种情况下发现了一些额外的符号(_DYNAMIC,__GNU_EH_FRAME_HDR,.interp),但代码中仍然没有任何额外系统调用的迹象。

你知道为什么我用 cc example.c -ffreestanding -nostartfiles -O3 -o example 而不是 cc example.c -c -O3 -o example.o; ld example.o -o example 得到额外的系统调用吗?

我知道发生了什么事了。

如果我用 cc example.c -ffreestanding -nostartfiles -O3 -o example 编译代码,编译器会生成一个动态 linked 的可执行文件。动态 linked 可执行文件有一个 .interp 部分。这就是我在 objdump -D.

中看到的内容

动态 linked 可执行文件通过程序解释器和动态 linker 执行。我看到的其他系统调用来自动态 linker。我仍然不知道为什么可执行文件想要动态 link 程序中没有 link 任何库并希望独立的任何东西。

如果您不希望来自动态 linker 的额外系统调用 - 您应该为 gcc 提供额外的 -static 选项。如果没有动态 linking 发生,编译器不会自动执行此操作。