如何解决在main vs _start中返回值
注意,这个问题在这里已经有类似的答案,我想指出:
但是,这个问题询问的更多是它们的返回格式以及它们之间的相互关系(我认为上面的问题并未完全涵盖这些问题)。
_start
和main
有什么区别?在我看来,ld
使用_start
,但是gcc
使用main
作为入口点。我注意到的另一个区别是main
似乎返回了%rax
中的值,而_start
返回了%rbx
中的值
以下是我看到这种情况的两种方式的示例:
.globl _start
_start:
mov $1,%rax
mov $2,%rbx
int $0x80
并运行它:
$ as script.s -o script.o; ld script.o -o script; ./script; echo $?
# 2
另一种方式:
.globl main
main:
mov $3,%rax
ret
并运行它:
$ gcc script.s -o script; ./script; echo $?
3
这两种方法有什么区别? main
是否在某个地方自动调用_start
,或者它们之间如何关联?为什么一个在rbx
中返回其值,而另一个在rax
中返回呢?
解决方法
TL:DR:函数返回值和系统调用参数使用单独的寄存器,因为它们是完全不相关的。
使用gcc
进行编译时,它将链接定义_start
的CRT启动代码。 该_start
(间接)调用main
,并将main
的返回值(主要留在EAX中)传递给exit()
库函数 。 (在进行了必要的libc清理(如刷新stdio缓冲区)之后,最终导致退出系统 。)
另请参见Return vs Exit from main function in C-这与您正在执行的操作完全相似,不同之处在于您使用的是_exit()
,它绕过了libc清理,而不是exit()
。 Syscall implementation of exit()
int $0x80
system call),which you shouldn't be using in 64-bit code在EBX中采用其参数。 这不是函数的返回值,而是过程退出状态 。有关系统调用的更多信息,请参见Hello,world in assembly language with Linux system calls?。
请注意,_start
不是函数。它不能以这种方式返回,因为堆栈上没有返回地址。 您正在进行诸如“返回操作系统”之类的随意描述,并将其与函数的“返回值”相混淆。您可以从{{ 1}}(如果需要),但是您不能exit
来main
。
EAX是函数调用约定中ret
大小的值的返回值寄存器。 (因为_start
返回int
,所以忽略了RAX的高32位。但是,main
退出状态只能获取传递给int
的值的低8位。 )
相关:
- Why am I allowed to exit main using ret?
- What happens with the return value of main()?
- where goes the ret instruction of the main
- What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?解释了为什么应使用
$?
,并显示了系统调用后内核内部发生的事情的内核部分。
_start是二进制文件的入口点。 Main是C代码的入口点。
_start特定于工具链,main()特定于语言。
您不能简单地开始执行已编译的C代码,您需要一个引导程序,一些代码可以预示诸如此类的高级语言所需的最少内容,其他语言则具有更长的要求列表,但是对于C语言,您需要通过加载程序(如果在操作系统或引导程序上,或者在这两者上都使用堆栈指针的解决方案,以便有堆栈),则初始化读/写全局数据(通常称为.data),并将数据清零(通常称为.bss)归零。然后引导程序可以调用main()。
因为大多数代码都在某些操作系统上运行,并且操作系统可以/确实将代码加载到ram中,所以它不需要硬入口点要求,因为您需要启动处理器,例如在存在硬入口的情况下点还是有一个硬向量表地址。因此,gnu足够灵活,某些操作系统也足够灵活,因此代码的入口点不必是二进制文件中的第一个机器代码。现在,这并不意味着_start表示入口点本身,例如,如果您对gnu ld使用链接器脚本,则需要告诉链接器入口点ENTRY(_start)。但是工具确实希望找到一个名为_start的标签,并且如果链接器没有发出警告,它会继续运行,但是会发出警告。
main()特定于C语言作为C入口点,引导程序在完成其工作并准备好运行已编译的C代码后会调用该标签。
如果加载到ram中并且二进制文件格式支持它,并且操作系统的加载器支持它,则二进制文件的入口点可以在二进制文件中的任何位置,如二进制文件中所示。
您可以将_start视为二进制文件的入口点,将main视为已编译的C代码的入口点。
C函数的返回值是由C编译器使用的调用约定定义的,编译器作者可以自由地执行他们想要的任何事情,但是在现代,它们通常符合定义的目标(ARM,x86,MIPS等) )定义的约定。为了使C调用约定确切地定义如何根据事物返回什么,因此int main()是int的返回,但float myfun()在约定中可能具有不同的规则。
从二进制文件(甚至可以返回)返回的消息是由独立于高级语言的操作系统或操作环境定义的。因此,在x86处理器上的mac上,该规则可能是x86上Windows上的规则,可能是另一回事,在同一x86上的Ubuntu Linux上,另一条规则可能是bsd,另一条,可能不是Mint Linux,另一条规则等等。
规则和系统调用特定于操作系统,而不是处理器或计算机,或者特定于不是直接与操作系统无关的高级语言(以引导程序或库代码处理,而不以高级语言代码处理)。您应该对其中的许多对象进行系统调用,而不仅仅是返回寄存器中的值,而且显然,对于二进制文件格式错误的操作系统,操作系统必须具有足够的鲁棒性以处理不正确的返回。和/或允许在不退出系统调用的情况下将其作为合法退货,然后在这种情况下,将为不进行系统调用的退货定义规则。
就主要调用_start而言,您自己可以轻松看到此内容:
int main ( void )
{
return(5);
}
readelf显示:
Entry point address: 0x500
objdump显示(这里不是整个输出)
Disassembly of section .init:
00000000000004b8 <_init>:
4b8: 48 83 ec 08 sub $0x8,%rsp
4bc: 48 8b 05 25 0b 20 00 mov 0x200b25(%rip),%rax # 200fe8 <__gmon_start__>
4c3: 48 85 c0 test %rax,%rax
4c6: 74 02 je 4ca <_init+0x12>
4c8: ff d0 callq *%rax
4ca: 48 83 c4 08 add $0x8,%rsp
4ce: c3 retq
...
Disassembly of section .text:
00000000000004f0 <main>:
4f0: b8 05 00 00 00 mov $0x5,%eax
4f5: c3 retq
4f6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
4fd: 00 00 00
...
0000000000000500 <_start>:
500: 31 ed xor %ebp,%ebp
502: 49 89 d1 mov %rdx,%r9
505: 5e pop %rsi
506: 48 89 e2 mov %rsp,%rdx
509: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
50d: 50 push %rax
50e: 54 push %rsp
50f: 4c 8d 05 6a 01 00 00 lea 0x16a(%rip),%r8 # 680 <__libc_csu_fini>
516: 48 8d 0d f3 00 00 00 lea 0xf3(%rip),%rcx # 610 <__libc_csu_init>
51d: 48 8d 3d cc ff ff ff lea -0x34(%rip),%rdi # 4f0 <main>
524: ff 15 b6 0a 20 00 callq *0x200ab6(%rip) # 200fe0 <__libc_start_main@GLIBC_2.2.5>
52a: f4 hlt
52b: 0f 1f 44 00 00 nopl 0x0(%rax,1)
因此您可以看到我上面提到的所有内容。二进制文件的入口点不在二进制文件的开头。 (对于二进制文件)入口点是_start,位于二进制文件中间的某处。在_start之后的某处(不一定像这里看到的那么紧密,可以被其他嵌套调用掩埋)main是从引导代码中调用的。假定在调用C入口点之前,.data和.bss以及堆栈是由加载程序而非引导程序设置的。
因此,在这种情况下,典型的_start是二进制文件的入口点,在为C引导程序启动后的某个地方,它将调用C入口点main()。作为程序员,您可以控制使用哪个链接程序脚本和引导程序,因此不必使用_start作为入口点,就可以创建自己的链接(当然,不能完全是main(),除非您没有完全支持C以及可能与操作系统有关的其他例外。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。