linux - 我可以在MacOS的_start处通过代码执行 `ret`指令吗? Linux的?

标签 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
ret从堆栈中弹出返回地址并跳转到该地址。

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

另外,上面的程序在OS X上不存在段错误。返回到哪里?

最佳答案

MacOS动态可执行文件

当您使用和MacOS 并链接到:

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

您将获得动态加载的代码版本。 _start不是真正的入口点,而动态加载器则是。动态加载程序的最后步骤之一是执行C/C++/Objective-C运行时初始化,然后调用用-e选项指定的指定入口点。 Apple有关Forking and Executing the Process的文档包含以下段落:

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.

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.

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查看生成的目标文件,则应获得:
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。这是动态加载程序 stub ,它最终建立一个C运行时环境,然后调用您的入口点_start。如果您不覆盖入口点,则默认为main

MacOS静态可执行文件

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

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.



静态构建的可执行文件不使用嵌入了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

如果运行此版本,它将产生段错误。 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

您应该注意到start_address现在为0x1fff。 0x1fff是您指定的入口点(_start)。没有动态加载程序 stub 作为中介。

的Linux

下,如果您指定自己的入口点,则在Linux 下,无论您要构建为静态可执行文件还是共享可执行文件,都会出现段错误。在articledynamic linker documentation中,有很好的信息说明了如何在Linux上运行ELF可执行文件。应该注意的关键点是,与MacOS动态链接器文档不同,Linux没有提及进行C/C++/Objective-C运行时初始化。

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

流程创建时的堆栈布局

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

enter image description here

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

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

关于linux - 我可以在MacOS的_start处通过代码执行 `ret`指令吗? Linux的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47801580/

相关文章:

linux - bash 脚本中的语法错误 : "(" unexpected -- with ! (*.sh)

c - 在 C 中右移带分配的数组(中止陷阱 : 6)

assembly - 进位标志和减法问题

python - 如何将不同的图片正确插入到单个文件中

c++ - 多线程环境中的 Linux 高分辨率计时器?

linux - 如何在 Ubuntu 16.04 上的 OVH VPS 中将 DHCP 接口(interface)更改为静态接口(interface)

c++ - Unresolved inclusion 错误 eclipse

macos - 安装 tensorflow

c - 理解一个简单的 C 程序生成的汇编代码

visual-studio - 如何在 Visual Studio 2010 中从 C++ 源代码生成汇编代码