我可以在MacOS _start的代码中执行`ret`指令吗? Linux的? [英] Can I do `ret` instruction from code at _start in MacOS? Linux?

查看:83
本文介绍了我可以在MacOS _start的代码中执行`ret`指令吗? Linux的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道从程序的入口点返回ret是否合法.

I am wondering if it is legal to return with ret from a program's entry point.

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

ret从堆栈中弹出一个返回地址并跳转到该地址.

ret pops a return address from the stack and jumps to it.

但是堆栈的最高字节是在程序入口处的有效返回地址,还是我必须调用exit?

But are the top bytes of the stack a valid return address at the program entry point, or do I have to call exit?

此外,以上程序在OS X上也不存在段错误.返回到哪里?

Also, the program above does not segfault on OS X. Where does it return to?

推荐答案

MacOS动态可执行文件

使用 MacOS 并链接到:

ld foo.o -lc -macosx_version_min 10.12.0 -e _start -o foo

您将获得动态的代码加载版本. _start不是真正的入口点,动态加载器则是.动态加载程序的最后步骤之一是执行 C/C ++/Objective-C 运行时初始化,然后调用由-e选项指定的指定入口点.关于

you are getting a dynamically loaded version of your code. _start isn't the true entry point, the dynamic loader is. The dynamic loader as one of its last steps does C/C++/Objective-C runtime initialization, and then calls your specified entry point specified with the -e option. The Apple documentation about Forking and Executing the Process has these paragraphs:

Mach-O可执行文件包含由一组加载命令组成的头.对于使用共享库或框架的程序,这些命令之一指定用于加载程序的链接器的位置.如果使用Xcode,则始终为标准OS X动态链接器/usr/lib/dyld.

A Mach-O executable file contains a header consisting of a set of load commands. For programs that use shared libraries or frameworks, one of these commands specifies the location of the linker to be used to load the program. If you use Xcode, this is always /usr/lib/dyld, the standard OS X dynamic linker.

当您调用execve例程时,内核首先加载指定的程序文件,并在文件开头检查mach_header结构.内核会验证该文件看起来是有效的Mach-O文件,并解释存储在标头中的加载命令.然后,内核将由load命令指定的动态链接器加载到内存中,并在程序文件上执行动态链接器.

When you call the execve routine, the kernel first loads the specified program file and examines the mach_header structure at the start of the file. The kernel verifies that the file appear to be a valid Mach-O file and interprets the load commands stored in the header. The kernel then loads the dynamic linker specified by the load commands into memory and executes the dynamic linker on the program file.

动态链接器加载主程序链接到的所有共享库(从属库),并绑定足够的符号以启动程序. 然后调用入口点功能.在构建时,静态链接器从对象文件/usr/lib/crt1.o 中将标准入口点功能添加到主可执行文件中. >.此功能设置内核的运行时环境状态,并为C ++对象调用静态初始化程序,初始化Objective-C运行时,然后然后调用程序的主函数.

The dynamic linker loads all the shared libraries that the main program links against (the dependent libraries) and binds enough of the symbols to start the program. It then calls the entry point function. At build time, the static linker adds the standard entry point function to the main executable file from the object file /usr/lib/crt1.o. This function sets up the runtime environment state for the kernel and calls static initializers for C++ objects, initializes the Objective-C runtime, and then calls the program’s main function

在您的情况下为_start.在创建动态链接的可执行文件的环境中,您可以执行ret并将其返回到名为_start的代码,该代码将为您执行退出系统调用.这就是为什么它不会崩溃的原因.如果使用gobjdump -Dx foo查看生成的目标文件,则应获得:

In your case that is _start. In this environment where you are creating a dynamically linked executable you can do a ret and have it return back to the code that called _start which does an exit system call for you. This is why it doesn't crash. If you review the generated object file with gobjdump -Dx foo you should get:

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

请注意,start address为0.0处的代码为dyld_stub_binder.这是动态加载程序存根,它最终建立一个 C 运行时环境,然后调用您的入口点_start.如果您不覆盖入口点,则默认为main.

Notice that start address is 0. And the code at 0 is dyld_stub_binder. This is the dynamic loader stub that eventually sets up a C runtime environment and then calls your entry point _start. If you don't override the entry point it defaults to main.

但是,如果您将其构建为 static 可执行文件,则在您的入口点之前不会执行任何代码,并且ret应该会崩溃,因为堆栈上没有有效的返回地址.在上面引用的文档中是这样的:

If however you build as a static executable, there is no code executed before your entry point and ret should crash since there is no valid return address on the stack. In the documentation quoted above is this:

对于使用共享库或框架的程序,这些命令之一指定用于加载程序的链接器的位置.

For programs that use shared libraries or frameworks, one of these commands specifies the location of the linker to be used to load the program.

静态生成的可执行文件不使用嵌入了crt1.o的动态加载器dyld. CRT = C 运行时库,它在MacOS上也涵盖了C ++/Objective-C.动态加载处理未完成, C/C ++/Objective-C 初始化代码未执行,控制权直接转移到您的入口点.

A statically built executable doesn't use the dynamic loader dyld with crt1.o embedded in it. CRT = C runtime library which covers C++/Objective-C as well on MacOS. The processes of dealing with dynamic loading are not done, C/C++/Objective-C initialization code is not executed, and control is transferred directly to your entry point.

要构建,请从链接器命令中静态删除-lc(或-lSystem)并添加-static选项:

To build statically drop the -lc (or -lSystem) from the linker command and add -static option:

ld foo.o -macosx_version_min 10.12.0 -e _start -o foo -static

如果运行此版本,它将产生分段错误. gobjdump -Dx foo产生

If you run this version it should produce a segmentation fault. gobjdump -Dx foo produces

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

您应注意,start_address现在为0x1fff. 0x1fff是您指定的入口点(_start).没有动态加载程序存根作为中介.

You should notice start_address is now 0x1fff. 0x1fff is the entry point you specified (_start). There is no dynamic loader stub as an intermediary.

Linux 下,当您指定自己的入口点时,无论是作为静态可执行文件还是共享可执行文件进行构建,都会出现分段错误. 文章

Under Linux when you specify your own entry point it will segmentation fault whether you are building as a static or shared executable. There is good information on how ELF executables are run on Linux in this article and the dynamic linker documentation. The key point that should be observed is that the Linux one makes no mention of doing C/C++/Objective-C runtime initialisation unlike the MacOS dynamic linker documentation.

Linux动态加载程序(ld.so)与MacOS one加载程序(dynld)之间的主要区别在于,MacOS动态加载程序通过包含以下项来执行 C/C ++/Objective-C 启动初始化从crt1.o指向.然后,crt1.o中的代码将控制权转移到您使用-e指定的入口点(默认为main).在Linux中,动态加载程序不假设将要运行的代码类型.在处理共享对象并进行初始化之后,控制权将直接转移到入口点.

The key difference between the Linux dynamic loader (ld.so) and the MacOS one (dynld) is that the MacOS dynamic loader performs C/C++/Objective-C startup initialization by including the entry point from crt1.o. The code in crt1.o then transfers control to the entry point you specified with -e (default is main). In Linux the dynamic loader makes no assumption about the type of code that will be run. After the shared objects are processed and initialized control is transferred directly to the entry point.

FreeBSD(基于MacOS的FreeBSD)与Linux有一个共同点.加载64位可执行文件时,创建进程时用户堆栈的布局是相同的. 32位进程的堆栈类似,但是指针和数据的宽度为4个字节,而不是8个字节.

FreeBSD (on which MacOS is based) and Linux share one thing in common. When loading 64-bit executables the layout of the user stack when a process is created is the same. The stack for 32-bit processes is similar but pointers and data are 4 bytes wide, not 8.

尽管堆栈上没有返回地址,但是还有其他数据表示参数数量,参数,环境变量和其他信息.此布局与 C/C ++ 中的main函数所期望的不同不同.它是 C 启动代码的一部分,用于将创建过程时的堆栈转换为与 C 调用约定和函数main(argcargvenvp).

Although there isn't a return address on the stack, there is other data representing the number of arguments, the arguments, environment variables, and other information. This layout is not the same as what the main function in C/C++ expects. It is part of the C startup code to convert the stack at process creation to something compatible with the C calling convention and the expectations of the function main (argc, argv, envp).

我在 Stackoverflow答案中写了关于此主题的更多信息,它显示了静态链接的MacOS可执行文件可以遍历内核在创建进程时传递的程序参数.

I wrote more information on this subject in this Stackoverflow answer that shows how a statically linked MacOS executable can traverse through the program arguments passed by the kernel at process creation.

这篇关于我可以在MacOS _start的代码中执行`ret`指令吗? Linux的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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