为什么一个简单的 C 程序需要系统调用? [英] Why would a simple C program need syscalls?

查看:28
本文介绍了为什么一个简单的 C 程序需要系统调用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

int main() {int a=1, b=2;int c=a+b;返回 c;}

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

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

解决方案

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

//不是它的实际实现方式……只是一个草图.无效_开始(){字符 **argv = ...;int argc = ...;//... 其他初始化代码 ...int retcode = main(argc, argv);退出(重新编码);}

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

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

您可以在没有标准库和运行时的情况下进行编译,但您的入口点无法返回......没有任何可返回的内容,没有运行时.

创建裸机程序

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

  • 使用 -ffreestanding.这使得 GCC 生成的代码不假定标准库可用(并具有其他影响).

  • 使用 -nostdlib.这将阻止 GCC 与标准库链接.请注意,无论如何都可能生成 memcmpmemsetmemcpymemmove 调用,因此您可能必须提供这些你自己.

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

void _start(void) {而 (1) { }}

请注意,您不能从 _start 返回!想想……无处可去.当您编译这样的程序时,您可以看到它没有使用任何系统调用,也没有加载器.

<前>$ gcc -ffreestanding -nostdlib test.c

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

<前>$ ldd a.out静态链接$ readelf -d a.out偏移量 0xf30 处的动态部分包含 8 个条目:标签类型名称/值0x000000006ffffef5 (GNU_HASH) 0x2780x0000000000000005 (STRTAB) 0x2b00x0000000000000006 (SYMTAB) 0x2980x000000000000000a (STRSZ) 1 (字节)0x000000000000000b (SYMENT) 24 (字节)0x0000000000000015 (调试) 0x00x000000006ffffffb (FLAGS_1) 标志:PIE0x0000000000000000 (NULL) 0x0

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

<前>$ objdump -d a.outa.out:文件格式elf64-x86-64.text 节的反汇编:00000000000002c0 <_start>:2c0: eb fe jmp 2c0 <_start>

Related to this other question. I am trying to run this simple C program in gem5:

int main() {
    int a=1, b=2;
    int c=a+b;
    return c;
}

And it fails because gem5 doesn't have some syscalls implemented.

My question is, why would a simple program like this require syscalls? This should run bare-metal without trouble. Is there a way to compile this to avoid syscalls? I am using arm-linux-gnueabi-gcc -static -DUNIX to compile it.

解决方案

Without syscalls the program cannot exit. The way it works is typically something like this:

// 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);
}

The exact details depend on the operating system, but exit(), which terminates the process, typically has to be a system call or is implemented with system calls.

Note that this is true for "hosted" C implementations, not for "freestanding" C implementations, and is highly operating-system specific. There are freestanding C implementations can run on bare metal, but hosted C implementations usually need an operating system.

You can compile without standard libraries and without the runtime but your entry point cannot return... there is nothing to return to, without a runtime.

Creating a baremetal program

It is generally possible to compile programs capable of running baremetal.

  • Use -ffreestanding. This makes GCC generate code that does not assume that the standard library is available (and has other effects).

  • Use -nostdlib. This will prevent GCC from linking with the standard library. Note that memcmp, memset, memcpy, and memmove calls may be generated anyway, so you may have to provide these yourself.

At this point you can write your program, but you typically have to use _start instead of main:

void _start(void) {
    while (1) { }
}

Note that you can't return from _start! Think about it... there is nowhere to return to. When you compile a program like this you can see that it doesn't use any system calls and doesn't have a loader.

$ gcc -ffreestanding -nostdlib test.c

We can verify that it loads no libraries:

$ 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

We can also see that it doesn't contain any code that makes system calls:

$ objdump -d a.out

a.out:     file format elf64-x86-64


Disassembly of section .text:

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

这篇关于为什么一个简单的 C 程序需要系统调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆