为什么一个简单的C程序需要系统调用?

tgo*_*eza 4 c gcc arm system-calls gem5

其他问题相关.我试图在gem5中运行这个简单的C程序:

int main() {
    int a=1, b=2;
    int c=a+b;
    return c;
}
Run Code Online (Sandbox Code Playgroud)

它失败了因为gem5没有实现一些系统调用.

我的问题是,为什么像这样的简单程序需要系统调用?这应该毫无困难地运行裸机.有没有办法编译这个以避免系统调用?我正在使用arm-linux-gnueabi-gcc -static -DUNIX来编译它.

Die*_*Epp 11

没有系统调用,程序无法退出.它的工作方式通常是这样的:

// Not how it's actually implemented... just a sketch.
void _start() {
    char **argv = ...;
    int argc = ...;
    // ... other initialization code ...
    int retcode = main(argc, argv);
    exit(retcode);
}
Run Code Online (Sandbox Code Playgroud)

具体细节取决于操作系统,但exit()终止该过程通常必须是系统调用或通过系统调用实现.

请注意,这适用于"托管"C实现,而不适用于"独立"C实现,并且是高度特定于操作系统的.有独立的C实现可以在裸机上运行,​​但托管的C实现通常需要一个操作系统.

您可以在没有标准库的情况下进行编译,也可以在没有运行时的情况下进行编译,但是您的入口点无法返回...如果没有运

创建一个裸机程序

通常可以编译能够运行裸金属的程序.

  • 使用-ffreestanding.这使得GCC生成的代码不会假设标准库可用(并且具有其他效果).

  • 使用-nostdlib.这将阻止GCC与标准库链接.需要注意的是memcmp,memset,memcpy,和memmove呼叫可能会产生无论如何,所以你可能对这些自己提供.

此时您可以编写程序,但通常必须使用_start而不是main:

void _start(void) {
    while (1) { }
}
Run Code Online (Sandbox Code Playgroud)

请注意,您无法退货_start!想一想......没有地方可以回归.编译这样的程序时,您可以看到它没有使用任何系统调用,也没有加载器.

$ gcc -ffreestanding -nostdlib test.c

我们可以验证它没有加载库:

$ ldd a.out                              
    statically linked
$ readelf -d a.out 

Dynamic section at offset 0xf30 contains 8 entries:
  Tag        Type                         Name/Value
 0x000000006ffffef5 (GNU_HASH)           0x278
 0x0000000000000005 (STRTAB)             0x2b0
 0x0000000000000006 (SYMTAB)             0x298
 0x000000000000000a (STRSZ)              1 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x000000006ffffffb (FLAGS_1)            Flags: PIE
 0x0000000000000000 (NULL)               0x0

我们还可以看到它不包含任何进行系统调用的代码:

$ objdump -d a.out

a.out:     file format elf64-x86-64


Disassembly of section .text:

00000000000002c0 <_start>:
 2c0:   eb fe                   jmp    2c0 <_start>