草庐IT

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

coder 2023-06-19 原文

我想知道从程序的入口点返回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个字节。



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

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

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

有关linux - 我可以在MacOS的_start处通过代码执行 `ret`指令吗? Linux的?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-openid:执行发现时未设置@socket - 2

    我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass

  3. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  4. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  5. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  6. ruby - 我可以使用 Ruby 从 CSV 中删除列吗? - 2

    查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html

  7. ruby - 我可以使用 aws-sdk-ruby 在 AWS S3 上使用事务性文件删除/上传吗? - 2

    我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的

  8. ruby - Chef 执行非顺序配方 - 2

    我遵循了教程http://gettingstartedwithchef.com/,第1章。我的运行list是"run_list":["recipe[apt]","recipe[phpap]"]我的phpapRecipe默认Recipeinclude_recipe"apache2"include_recipe"build-essential"include_recipe"openssl"include_recipe"mysql::client"include_recipe"mysql::server"include_recipe"php"include_recipe"php::modul

  9. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  10. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

    我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

随机推荐