为什么一个简单的 C 程序需要系统调用? [英] Why would a simple C program need syscalls?
问题描述
与这个其他问题有关.我正在尝试在 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 与标准库链接.请注意,无论如何都可能生成memcmp
、memset
、memcpy
和memmove
调用,因此您可能必须提供这些你自己.
此时您可以编写您的程序,但您通常必须使用 _start
而不是 main
:
void _start(void) {而 (1) { }}
请注意,您不能从 _start
返回!想想……无处可去.当您编译这样的程序时,您可以看到它没有使用任何系统调用,也没有加载器.
我们可以验证它没有加载任何库:
<前>$ 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 thatmemcmp
,memset
,memcpy
, andmemmove
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屋!