Apple LLVM-gcc x86程序集中发生了什么?

Mik*_*ell 7 c macos x86 assembly llvm-gcc

我有兴趣学习更多x86/x86_64程序集.唉,我在Mac上.没问题,对吧?

$ gcc --version
i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 
5658) (LLVM build 2336.11.00)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Run Code Online (Sandbox Code Playgroud)

我在C中写了一个简单的"Hello World",以获得我将要编写的代码类型的基线.我在大学时做了一点x86,并且查了很多教程,但是没有一个看起来像我在这里看到的怪异输出:

.section    __TEXT,__text,regular,pure_instructions
.globl  _main
.align  4, 0x90
_main:
Leh_func_begin1:
pushq   %rbp
Ltmp0:
movq    %rsp, %rbp
Ltmp1:
subq    $32, %rsp
Ltmp2:
movl    %edi, %eax
movl    %eax, -4(%rbp)
movq    %rsi, -16(%rbp)
leaq    L_.str(%rip), %rax
movq    %rax, %rdi
callq   _puts
movl    $0, -24(%rbp)
movl    -24(%rbp), %eax
movl    %eax, -20(%rbp)
movl    -20(%rbp), %eax
addq    $32, %rsp
popq    %rbp
ret
Leh_func_end1:

.section    __TEXT,__cstring,cstring_literals
L_.str:
.asciz   "Hello, World!"

.section    __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support
EH_frame0:
Lsection_eh_frame:
Leh_frame_common:
Lset0 = Leh_frame_common_end-Leh_frame_common_begin
.long   Lset0
Leh_frame_common_begin:
.long   0
.byte   1
.asciz   "zR"
.byte   1
.byte   120
.byte   16
.byte   1
.byte   16
.byte   12
.byte   7
.byte   8
.byte   144
.byte   1
.align  3
Leh_frame_common_end:
.globl  _main.eh
_main.eh:
Lset1 = Leh_frame_end1-Leh_frame_begin1
.long   Lset1
Leh_frame_begin1:
Lset2 = Leh_frame_begin1-Leh_frame_common
.long   Lset2
Ltmp3:
.quad   Leh_func_begin1-Ltmp3
Lset3 = Leh_func_end1-Leh_func_begin1
.quad   Lset3
.byte   0
.byte   4
Lset4 = Ltmp0-Leh_func_begin1
.long   Lset4
.byte   14
.byte   16
.byte   134
.byte   2
.byte   4
Lset5 = Ltmp1-Ltmp0
.long   Lset5
.byte   13
.byte   6
.align  3
Leh_frame_end1:


.subsections_via_symbols
Run Code Online (Sandbox Code Playgroud)

现在......也许事情发生了一些变化,但即使对于汇编代码,这也不是很友好.我很难绕过这个...有人帮助分解这段代码中发生的事情以及为什么需要它?

很多,非常感谢提前.

Ale*_*nze 10

由于问题实际上是关于那些奇怪的标签和数据,而不是关于代码本身的问题,我只是对它们有所了解.

如果程序的指令导致执行错误(例如除以0或访问不可访问的内存区域或尝试执行特权指令),则会导致异常(不是C++异常,而是中断类型)它)并强制CPU在OS内核中执行适当的异常处理程序.如果我们完全不允许这些例外,那么故事就会很短,操作系统就会终止程序.

但是,让程序处理自己的异常有一些优点,因此OS处理程序中的主要异常处理程序会将一些异常反映回程序中进行处理.例如,程序可能会尝试从异常中恢复,或者可以在终止之前保存有意义的崩溃报告.

在任何一种情况下,了解以下内容都很有用:

  • 发生异常的函数,而不仅仅是其中的违规指令
  • 调用该函数的函数,调用该函数的函数等

可能(主要用于调试):

  • 源代码文件的行,从中生成此指令
  • 这些函数调用的行
  • 功能参数

为什么我们需要知道调用树?

好吧,如果程序注册自己的异常处理程序,它通常会像C++ trycatch块一样:

fxn()
{
  try
  {
    // do something potentially harmful
  }
  catch()
  {
    // catch and handle attempts to do something harmful
  }
  catch()
  {
    // catch and handle attempts to do something harmful
  }
}
Run Code Online (Sandbox Code Playgroud)

如果这些catches捕获都没有,则异常传播到调用者的调用者fxn并且可能传递给调用者的调用者fxn,直到有一个catch捕获异常或者直到简单地终止该程序的默认异常处理程序为止.

所以,你需要知道每个try覆盖的代码区域,你需要知道如果立即/ 不捕获异常并且它必须冒泡,如何到达下一个最接近的try(fxn例如在调用者中).trycatch

块的范围try和位置catch易于在可执行文件的特殊部分中编码,并且易于使用(只需对这些范围内的违规指令地址进行二进制搜索).但是找出下一个外部try块更难,因为你可能需要找到发生异常的函数的返回地址.

并且您可能并不总是依赖于rbp+8指向堆栈上的返回地址,因为编译器可能以rbp不再涉及访问函数参数和局部变量的方式优化代码.您也可以通过它们访问它们rsp+something并保存寄存器和一些指令,但考虑到不同的函数在堆栈上为本地分配不同的字节数,并且传递给其他函数的参数和调整方式rsp不同,只需要rspisn 的值足以找出返回地址和调用函数.rsp可以是远离堆栈返回地址的任意数量的字节.

对于这种情况,编译器在可执行文件的专用部分中包含有关函数及其堆栈使用的附加信息.异常处理代码检查此信息,并在异常必须传播到调用函数及其try/ catch块时正确展开堆栈.

因此,以下数据_main.eh包含其他信息.请注意,它main()通过引用Leh_func_begin1和显式编码开头和大小Leh_func_end1-Leh_func_begin1.这条信息允许异常处理代码将main()'s指令识别为main()'s.

它似乎main()也不是很独特,它的一些堆栈/异常信息与其他函数相同,在它们之间共享它是有意义的.所以有一个参考Leh_frame_common.

我不能在结构进一步的评论_main.eh和喜欢这些常数的确切含义14413,因为我不知道这个数据的格式.但通常人们不需要知道这些细节,除非他们是编译器或调试器开发人员.

我希望这可以让您了解这些标签和常量的用途.