Bil*_*low 5 linux macos assembly return entry-point
我想知道ret从程序的入口点返回是否合法.
NASM示例:
section .text
global _start
_start:
ret
; Linux: nasm -f elf64 foo.asm -o foo.o && ld foo.o
; OS X: nasm -f macho64 foo.asm -o foo.o && ld foo.o -lc -macosx_version_min 10.12.0 -e _start -o foo
Run Code Online (Sandbox Code Playgroud)
ret 从堆栈中弹出一个返回地址并跳转到它.
但是堆栈的顶部字节是程序入口点的有效返回地址,还是我必须调用exit?
此外,上面的程序不会在OS X上发生段错误.它在哪里返回?
Mic*_*tch 11
当您使用MacOS并链接时:
ld foo.o -lc -macosx_version_min 10.12.0 -e _start -o foo
Run Code Online (Sandbox Code Playgroud)
您正在获得动态加载的代码版本._start动态加载器不是真正的切入点.动态加载器作为其最后一步执行C/C++/Objective-C运行时初始化,然后调用使用该-e选项指定的指定入口点.有关分岔和执行流程的Apple文档包含以下段落:
Mach-O可执行文件包含由一组加载命令组成的标头.对于使用共享库或框架的程序,其中一个命令指定用于加载程序的链接器的位置.如果使用Xcode,则始终是/ usr/lib/dyld,标准OS X动态链接器.
当您调用execve例程时,内核首先加载指定的程序文件并检查文件开头的mach_header结构.内核验证该文件似乎是有效的Mach-O文件,并解释存储在标头中的加载命令.然后,内核将load命令指定的动态链接器加载到内存中,并在程序文件上执行动态链接器.
该动态链接程序加载所有的共享库,对主程序链接(从属库)和结合足够的符号的启动程序.然后它调用入口点函数.在构建时,静态链接添加标准的入口点函数的主执行文件从目标文件/usr/lib/crt1.o.此函数为内核设置运行时环境状态,并为C++对象调用静态初始化程序,初始化Objective-C运行时,然后调用程序的主函数
在你的情况下_start.在您正在创建动态链接可执行文件的环境中,您可以执行a ret并让它返回到_start为您执行退出系统调用的调用代码.这就是为什么它不会崩溃.如果您查看生成的目标文件,gobjdump -Dx foo您应该得到:
start address 0x0000000000000000
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000001 0000000000001fff 0000000000001fff 00000fff 2**0
CONTENTS, ALLOC, LOAD, CODE
SYMBOL TABLE:
0000000000001000 g 03 ABS 01 0010 __mh_execute_header
0000000000001fff g 0f SECT 01 0000 [.text] _start
0000000000000000 g 01 UND 00 0100 dyld_stub_binder
Disassembly of section .text:
0000000000001fff <_start>:
1fff: c3 retq
Run Code Online (Sandbox Code Playgroud)
请注意,该start address值为0.而0处的代码为dyld_stub_binder.这是动态加载程序存根,最终设置C运行时环境,然后调用您的入口点_start.如果您没有覆盖它默认的入口点main.
但是,如果您构建为静态可执行文件,则在入口点之前没有执行代码,并且ret应该崩溃,因为堆栈上没有有效的返回地址.在上面引用的文档是这样的:
对于使用共享库或框架的程序,其中一个命令指定用于加载程序的链接器的位置.
以静态生成的可执行文件不使用动态加载dyld与crt1.o嵌入其中.CRT = C运行时库,在MacOS上也包含C++/Objective-C.处理动态加载的过程没有完成,C/C++/Objective-C初始化代码没有被执行,控件被直接传送到你的入口点.
要从链接器命令静态地删除-lc(或-lSystem)并添加-static选项:
ld foo.o -macosx_version_min 10.12.0 -e _start -o foo -static
Run Code Online (Sandbox Code Playgroud)
如果您运行此版本,它应该产生分段错误.gobjdump -Dx foo产生
start address 0x0000000000001fff
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000001 0000000000001fff 0000000000001fff 00000fff 2**0
CONTENTS, ALLOC, LOAD, CODE
1 LC_THREAD.x86_THREAD_STATE64.0 000000a8 0000000000000000 0000000000000000 00000198 2**0
CONTENTS
SYMBOL TABLE:
0000000000001000 g 03 ABS 01 0010 __mh_execute_header
0000000000001fff g 0f SECT 01 0000 [.text] _start
Disassembly of section .text:
0000000000001fff <_start>:
1fff: c3 retq
Run Code Online (Sandbox Code Playgroud)
您应该注意到start_address现在是0x1fff.0x1fff是您指定的入口点(_start).没有动态加载程序存根作为中介.
在Linux下,当您指定自己的入口点时,无论您是构建为静态还是共享可执行文件,它都将分段错误.在本文和动态链接器文档中,有关于如何在Linux上运行ELF可执行文件的良好信息.应该注意的关键点是,与MacOS动态链接器文档不同,Linux没有提及进行C/C++/Objective-C运行时初始化.
Linux动态加载程序(ld.so)和MacOS one(dynld)之间的主要区别在于MacOS动态加载程序通过包含入口点来执行C/C++/Objective-C启动初始化crt1.o.然后代码crt1.o将控制转移到您指定的入口点-e(默认为main).在Linux中,动态加载器不会假设将运行的代码类型.处理完共享对象并初始化后,控制将直接传输到入口点.
FreeBSD(MacOS所基于的)和Linux共享一个共同点.加载64位可执行文件时,创建进程时用户堆栈的布局是相同的.32位进程的堆栈类似,但指针和数据是4字节宽,而不是8.
尽管堆栈上没有返回地址,但还有其他数据表示参数的数量,参数,环境变量和其他信息.这种布局不一样的是什么main函数C/C++的期望.这是一部分Ç启动代码堆栈在创建过程中转化为与兼容的东西Ç调用约定和功能的期望main(argc,argv,envp).
我在此Stackoverflow答案中写了更多关于此主题的信息,该答案显示了静态链接的MacOS可执行文件如何遍历内核在创建进程时传递的程序参数.