在 C 中编译和 运行 没有 main() 的程序
Compile and run program without main() in C
我试图在 C
中编译并 运行 跟随没有 main()
函数的程序。我已经使用以下命令编译了我的程序。
gcc -nostartfiles nomain.c
并且编译器给出警告
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400340
好的,没问题。然后,我有 运行 个可执行文件(a.out),两个 printf
语句都打印成功,然后得到 段错误 。
所以,我的问题是,为什么成功执行打印语句后会出现段错误?
我的代码:
#include <stdio.h>
void nomain()
{
printf("Hello World...\n");
printf("Successfully run without main...\n");
}
输出:
Hello World...
Successfully run without main...
Segmentation fault (core dumped)
注:
此处,-nostartfiles
gcc 标志阻止编译器在链接时使用标准启动文件
在 C 中,当调用 functions/subroutines 时,堆栈填充为(按顺序):
- 参数,
- Return地址,
- 局部变量,--> 栈顶
main() 作为起点,ELF 以这样一种方式构建程序,即无论先到的指令都会先被推送,在这种情况下,printfs 就是。
现在,程序在没有 return-address OR __end__
的情况下被截断了,事实上它假设堆栈上那个(__end__
)位置的任何东西都是 return-address,但不幸的是它没有,因此它崩溃了。
让我们看看您的程序生成的assembly:
.LC0:
.string "Hello World..."
.LC1:
.string "Successfully run without main..."
nomain:
push rbp
mov rbp, rsp
mov edi, OFFSET FLAT:.LC0
call puts
mov edi, OFFSET FLAT:.LC1
call puts
nop
pop rbp
ret
注意 ret
语句。您的程序的入口点确定为 nomain
,一切都很好。但是一旦函数returns,它就会尝试跳转到调用堆栈上的一个地址……没有填充。这是非法访问,随后出现分段错误。
一个快速的解决方案是在程序结束时调用 exit()
(假设是 C11,我们不妨将函数标记为 _Noreturn
):
#include <stdio.h>
#include <stdlib.h>
_Noreturn void nomain(void)
{
printf("Hello World...\n");
printf("Successfully run without main...\n");
exit(0);
}
事实上,现在您的函数的行为与常规 main
函数非常相似,因为在从 main
调用 return 之后,exit
函数使用 main
的return值。
我试图在 C
中编译并 运行 跟随没有 main()
函数的程序。我已经使用以下命令编译了我的程序。
gcc -nostartfiles nomain.c
并且编译器给出警告
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400340
好的,没问题。然后,我有 运行 个可执行文件(a.out),两个 printf
语句都打印成功,然后得到 段错误 。
所以,我的问题是,为什么成功执行打印语句后会出现段错误?
我的代码:
#include <stdio.h>
void nomain()
{
printf("Hello World...\n");
printf("Successfully run without main...\n");
}
输出:
Hello World...
Successfully run without main...
Segmentation fault (core dumped)
注:
此处,-nostartfiles
gcc 标志阻止编译器在链接时使用标准启动文件
在 C 中,当调用 functions/subroutines 时,堆栈填充为(按顺序):
- 参数,
- Return地址,
- 局部变量,--> 栈顶
main() 作为起点,ELF 以这样一种方式构建程序,即无论先到的指令都会先被推送,在这种情况下,printfs 就是。
现在,程序在没有 return-address OR __end__
的情况下被截断了,事实上它假设堆栈上那个(__end__
)位置的任何东西都是 return-address,但不幸的是它没有,因此它崩溃了。
让我们看看您的程序生成的assembly:
.LC0:
.string "Hello World..."
.LC1:
.string "Successfully run without main..."
nomain:
push rbp
mov rbp, rsp
mov edi, OFFSET FLAT:.LC0
call puts
mov edi, OFFSET FLAT:.LC1
call puts
nop
pop rbp
ret
注意 ret
语句。您的程序的入口点确定为 nomain
,一切都很好。但是一旦函数returns,它就会尝试跳转到调用堆栈上的一个地址……没有填充。这是非法访问,随后出现分段错误。
一个快速的解决方案是在程序结束时调用 exit()
(假设是 C11,我们不妨将函数标记为 _Noreturn
):
#include <stdio.h>
#include <stdlib.h>
_Noreturn void nomain(void)
{
printf("Hello World...\n");
printf("Successfully run without main...\n");
exit(0);
}
事实上,现在您的函数的行为与常规 main
函数非常相似,因为在从 main
调用 return 之后,exit
函数使用 main
的return值。