.cfi指令解读(二)

转自:http://sourceware.org/binutils/docs/as/CFI-directives.html

先看吧,时间太紧了没时间翻译。等什么时候有时间再说吧。

7.11 .cfi_sections section_list

.cfi_sections may be used to specify whether CFI directives should emit .eh_frame section and/or .debug_frame section. If section_list is .eh_frame, .eh_frame is emitted, if section_list is .debug_frame, .debug_frame is emitted. To emit both use .eh_frame, .debug_frame. The default if this directive is not used is .cfi_sections .eh_frame.

7.12 .cfi_startproc [simple]

.cfi_startproc is used at the beginning of each function that should have an entry in .eh_frame. It initializes some internal data structures. Don't forget to close the function by .cfi_endproc.

Unless .cfi_startproc is used along with parameter simple it also emits some architecture dependent initial CFI instructions.

7.13 .cfi_endproc

.cfi_endproc is used at the end of a function where it closes its unwind entry previously opened by .cfi_startproc, and emits it to .eh_frame.

7.14 .cfi_personality encoding [, exp]

.cfi_personality defines personality routine and its encoding. encoding must be a constant determining how the personality should be encoded. If it is 255 (DW_EH_PE_omit), second argument is not present, otherwise second argument should be a constant or a symbol name. When using indirect encodings, the symbol provided should be the location where personality can be loaded from, not the personality routine itself. The default after .cfi_startproc is .cfi_personality 0xff, no personality routine.

7.15 .cfi_lsda encoding [, exp]

.cfi_lsda defines LSDA and its encoding. encoding must be a constant determining how the LSDA should be encoded. If it is 255 (DW_EH_PE_omit), second argument is not present, otherwise second argument should be a constant or a symbol name. The default after .cfi_startproc is .cfi_lsda 0xff, no LSDA.

7.16 .cfi_def_cfa register, offset

.cfi_def_cfa defines a rule for computing CFA as: take address from register and add offset to it.

7.17 .cfi_def_cfa_register register

.cfi_def_cfa_register modifies a rule for computing CFA. From now on register will be used instead of the old one. Offset remains the same.

7.18 .cfi_def_cfa_offset offset

.cfi_def_cfa_offset modifies a rule for computing CFA. Register remains the same, but offset is new. Note that it is the absolute offset that will be added to a defined register to compute CFA address.

7.19 .cfi_adjust_cfa_offset offset

Same as .cfi_def_cfa_offset but offset is a relative value that is added/substracted from the previous offset.

7.20 .cfi_offset register, offset

Previous value of register is saved at offset offset from CFA.

7.21 .cfi_rel_offset register, offset

Previous value of register is saved at offset offset from the current CFA register. This is transformed to .cfi_offset using the known displacement of the CFA register from the CFA. This is often easier to use, because the number will match the code it's annotating.

7.22 .cfi_register register1, register2

Previous value of register1 is saved in register register2.

7.23 .cfi_restore register

.cfi_restore says that the rule for register is now the same as it was at the beginning of the function, after all initial instruction added by .cfi_startproc were executed.

7.24 .cfi_undefined register

From now on the previous value of register can't be restored anymore.

7.25 .cfi_same_value register

Current value of register is the same like in the previous frame, i.e. no restoration needed.

7.26 .cfi_remember_state,

First save all current rules for all registers by .cfi_remember_state, then totally screw them up by subsequent .cfi_* directives and when everything is hopelessly bad, use .cfi_restore_state to restore the previous saved state.

7.27 .cfi_return_column register

Change return column register, i.e. the return address is either directly in register or can be accessed by rules for register.

7.28 .cfi_signal_frame

Mark current function as signal trampoline.

7.29 .cfi_window_save

SPARC register window has been saved.

7.30 .cfi_escape expression[, ...]

Allows the user to add arbitrary bytes to the unwind info. One might use this to add OS-specific CFI opcodes, or generic CFI opcodes that GAS does not yet support.

7.31 .cfi_val_encoded_addr register, encoding, label

The current value of register is label. The value of label will be encoded in the output file according to encoding; see the description of .cfi_personality for details on this encoding.

The usefulness of equating a register to a fixed label is probably limited to the return address register. Here, it can be useful to mark a code segment that has only one return address which is reached by a direct branch and no copy of the return address exists in memory or another register.

<think>好的,用户想分析funcs.c文件中的func3和func2的汇编实现及其寄存器操作。首先,我需要回忆一下如何从C代码生成汇编代码,以及如何分析其中的函数和寄存器使用。 用户提到使用反汇编工具,比如objdump,这可能是一个关键点。根据引用[2],go tool objdump可以反汇编进制文件,类似于系统自带的objdump命令。所以,用户可能需要用objdump来查看func3和func2的汇编代码。 接下来,我需要考虑如何生成funcs.c的汇编输出。通常可以使用gcc的-S选项来生成汇编代码,例如gcc -S funcs.c -o funcs.s。这样生成的汇编代码会更易读,尤其是带有C代码注释的话。不过,如果用户已经有一个编译好的进制文件,可能需要用objdump -d来反汇编。 然后,分析函数定义。在汇编中,函数通常以.globl指令开始,后面跟着函数名,比如func3:。需要查看函数的入口点,以及使用的寄存器。根据引用[3],函数是由CALL指令跳转到的代码开始,所以需要找到func3和func2的起始标签。 寄存器操作方面,需要关注函数调用时的参数传递。在x86架构中,参数可能通过寄存器rdi、rsi等传递(System V AMD64 ABI)。而在ARM架构中,可能使用r0、r1等。需要确认目标平台的调用约定。 另外,用户提到递归调用,如引用[1]中的func_4,这可能涉及到栈帧的管理,比如push和pop操作,以及返回地址的处理。需要检查func3和func2是否有类似的递归结构,或者是否有调用其他函数。 还需要注意函数内部的指令流程,比如条件跳转(jne、je等)、循环结构,以及如何处理局部变量。栈指针(rsp)和基指针(rbp)的使用情况也很重要,尤其是在保存寄存器和分配局部变量空间时。 可能还需要参考引用[4]中的sync指令,不过这可能与内存同步相关,可能在多线程或特定架构中出现,但func3和func2可能不涉及这些,不过需要确认汇编代码中是否有相关指令。 最后,生成相关问题需要考虑用户可能的后续需求,比如如何优化汇编代码、理解调用约定、调试技巧等。确保问题与当前分析相关,并帮助用户深入理解汇编层面的实现。</think>### 分析 `funcs.c` 中 `func3` 和 `func2` 的汇编实现 #### 1. **生成汇编代码** 通过 `objdump` 或编译器生成的汇编文件(如 `gcc -S funcs.c`)获取汇编代码。假设使用 `objdump` 分析编译后的进制文件: ```bash objdump -d funcs.o | grep -A 20 "<func3>:" objdump -d funcs.o | grep -A 20 "<func2>:" ``` #### 2. **函数定义与寄存器操作** **假设 `func3` 为递归函数(参考引用[1]的递归模式)**: ```assembly <func3>: push %rbp ; 保存栈基址 mov %rsp, %rbp ; 新栈帧 sub $0x10, %rsp ; 分配局部变量空间 mov %edi, -0x4(%rbp) ; 参数存入栈 cmp $0x1, %edi ; 比较参数与1 jle 0x4005a0 ; 条件跳转(递归终止) lea -0x1(%rdi), %eax mov %eax, %edi call 0x400540 <func3> ; 递归调用 add %eax, %eax ; 结果累加 leave ret ``` **关键寄存器操作**: - `%edi`:存储函数参数(遵循System V AMD64调用约定[^3])。 - `%eax`:存储返回值,递归调用后累加结果。 - `%rbp/%rsp`:管理栈帧,保存调用前后状态。 **假设 `func2` 为循环结构**: ```assembly <func2>: xor %eax, %eax ; 清零返回值 test %edi, %edi jle 0x4005c0 ; 处理非正数输入 mov %edi, %ecx ; 循环计数器 .Loop: add %ecx, %eax ; 累加计数 loop .Loop ; ecx自减并循环 ret ``` **关键寄存器操作**: - `%edi`:输入参数(如循环上限)。 - `%ecx`:循环计数器,通过 `loop` 指令控制迭代。 - `%eax`:存储累加结果。 #### 3. **核心逻辑对比** | 函数 | 结构 | 寄存器用途 | 栈操作 | |-------|---------|----------------------------|-------------------| | func3 | 递归 | %edi传参,%eax返回结果 | push/pop栈帧管理 | | func2 | 循环 | %ecx计数器,%eax累加结果 | 无显式栈分配 | ### 相关问题 1. 如何通过汇编代码区分递归和循环结构? 2. System V AMD64调用约定中哪些寄存器用于参数传递? 3. 汇编中 `leave` 和 `ret` 指令的具体作用是什么? 4. 如何优化递归函数的汇编性能? --- [^1]: 递归函数通过 `call` 自身实现,需管理栈帧以保证状态隔离。 [^2]: 使用 `objdump` 可定位函数入口和指令流。 [^3]: 函数定义以栈帧管理指令(如 `push %rbp`)为起始标志。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值