gpt4 book ai didi

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

转载 作者:太空狗 更新时间:2023-10-29 17:06:41 25 4
gpt4 key购买 nike

我有兴趣学习更多 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.

我用 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

现在...也许事情发生了一些变化,但这并不完全友好,即使对于汇编代码也是如此。我很难解决这个问题......有人可以帮助分解这段代码中发生的事情以及为什么需要它吗?

很多,非常感谢。

最佳答案

由于问题实际上是关于那些奇怪的标签和数据,而不是关于代码本身,我只会对它们进行一些说明。

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

然而,让程序处理它们自己的异常是有好处的,因此操作系统处理程序中的主要异常处理程序将一些异常反射(reflect)回程序进行处理。例如,程序可以尝试从异常中恢复,或者可以在终止之前保存有意义的崩溃报告。

无论哪种情况,了解以下内容都是有用的:

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

  • 并且可能(主要用于调试):
  • 生成此指令的源代码文件的行
  • 进行这些函数调用的行
  • 函数参数

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

    好吧,如果程序注册自己的异常处理程序,它通常会像 C++ try 那样做。和 catch块:
    fxn()
    {
    try
    {
    // do something potentially harmful
    }
    catch()
    {
    // catch and handle attempts to do something harmful
    }
    catch()
    {
    // catch and handle attempts to do something harmful
    }
    }

    如果这些都不是 catches捕获,异常传播到 fxn 的调用者并可能是 fxn 的来电者的来电者, 直到出现 catch捕获异常或直到简单终止程序的默认异常处理程序。

    所以,你需要知道每个 try的代码区域。封面,你需要知道如何去下一个最近的 try (例如,在 fxn 的调用者中)如果直接 try/ catch没有捕获异常,它必须冒泡。
    try 的范围和 catch 的位置块很容易在可执行文件的特殊部分中编码,并且它们很容易使用(只需对这些范围内的违规指令地址进行二进制搜索)。但找出下一个外 try block 更难,因为您可能需要从函数中找出发生异常的返回地址。

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

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

    所以,后面的数据 _main.eh包含该附加信息。请注意,它明确地对 main() 的开头和大小进行了编码。引用 Leh_func_begin1Leh_func_end1-Leh_func_begin1 .这条信息允许异常处理代码识别 main()'s说明为 main()'s .

    看来 main()不是很独特,它的一些堆栈/异常信息与其他函数中的相同,在它们之间共享它是有意义的。所以有一个对 Leh_frame_common 的引用.

    我无法进一步评论 _main.eh 的结构以及这些常量的确切含义,如 14413因为我不知道这些数据的格式。但通常不需要知道这些细节,除非他们是编译器或调试器开发人员。

    我希望这能让您了解这些标签和常量的用途。

    关于c - Apple LLVM-gcc x86 程序集中发生了什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15293454/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com