gpt4 book ai didi

assembly - 在汇编代码中,.cfi 指令如何工作?

转载 作者:行者123 更新时间:2023-12-05 00:22:43 25 4
gpt4 key购买 nike

[汇编代码]

main:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $32, %esp
movl $5, 20(%esp)
movl $3, 24(%esp)
movl 24(%esp), %eax
movl %eax, 4(%esp)
movl 20(%esp), %eax
movl %eax, (%esp)
call add
movl %eax, 28(%esp)
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size main, .-main
.globl add
.type add, @function
add:
.LFB1:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $16, %esp
movl 12(%ebp), %eax
movl 8(%ebp), %edx
addl %edx, %eax
movl %eax, -4(%ebp)
movl -4(%ebp), %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc

[源代码]
int add(int k, int l);

int main(int argc, char **argv) {
int a, b, ret;
a = 5;
b = 3;
ret = add(a, b);
return 0;
}

int add(int k, int l) {
int x;
x = k + l;
return x;
}

我正在研究汇编语言级别的 c 函数的调用约定。

如您所知,.cfi 用于添加调试信息。我读过一些 cfi 文章并且知道每个指令的含义。

在上面的汇编代码中, .cfi_def_cfa_offset 8.cfi_offset 5 -8指令是连续出现的。这再次发生在“main”函数和“add”函数中。

但是,我不知道为什么会发生这种情况。我所知道的是 .cfi_def_cfa_offset.cfi_offset用于制作预留内存以存储调试信息。在此代码中,该偏移量首先设置为 +8,然后设置为 -8。结果是......没有剩余的内存空间来存储cfi。我对吗?

我认为堆栈段是这样工作的。
.cfi_startproc
|-------------|
| whatever | <- %esp = CFA ↑ increase address
|-------------|
| | ↓ stack grow
|_____________|



.pushl %ebp
|-------------|
| whatever |
|-------------|
| %ebp | <- %esp
|_____________|


.cfi_def_cfa_offset 8
|-------------|
| whatever | <- %esp
|-------------|
| whatever |
|-------------|
| %ebp |
|-------------|



.cfi_offset 5 -8
|-------------|
| whatever |
|-------------|
| whatever |
|-------------|
| %ebp | <- %esp
|-------------|



subl $32, %esp
|-------------|
| whatever |
|-------------|
| %ebp |
|-------------|
| |
|-------------|
| |
|-------------|
| |
|-------------|
| |
|-------------|
| |
|-------------|
| |
|-------------|
| |
|-------------|
| | <- %esp
|-------------|



movl $5, 20(%esp)
|-------------|
| whatever |
|-------------|
| %ebp |
|-------------|
| |
|-------------|
| |
|-------------|
| 5 |
|-------------|
| |
|-------------|
| |
|-------------|
| |
|-------------|
| |
|-------------|
| | <- %esp
|-------------|

等等...

问题2。

在程序 add ,来自调用者函数的参数被移动到被调用者函数寄存器。
    movl    12(%ebp), %eax
movl 8(%ebp), %edx

但是,在我的计算中, 8(%ebp) 没有指向调用者堆栈的顶部。因为,

1) 在 pushl %ebp , %esp 减去 4

2) 在 cfi_offset 5, -8 , %esp 是 sbracted 8 (这样,我忽略了 .cfi_def_cfa_offset 8 。我不确定)

所以,这样调用函数栈的顶部应该是12(%ebp),8(%ebp)指向的是调用函数存储的基指针。

我不知道我不知道的地方……我需要你的帮助。

-添加

What do the CFI directives mean? (and some more questions)

这个SO问题与我几乎相似。但是没有人清楚地回答这个问题。

最佳答案

.cfi directives不是 生成任何汇编代码。他们是 不是 执行并执行 不是 根本改变你的调用框架的布局。

相反,它们告诉需要展开堆栈的工具(调试器、异常展开器)有关帧的结构(以及如何展开它)。这些信息不与指令一起存储,而是存储在程序的另一部分中(见注 1)。

让我们看看这个片段:

main:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $32, %esp
movl $5, 20(%esp)
movl $3, 24(%esp)
movl 24(%esp), %eax
movl %eax, 4(%esp)
movl 20(%esp), %eax
movl %eax, (%esp)
call add
movl %eax, 28(%esp)
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc

汇编器将汇编 .text 中的指令分段并编译 .cfi另一部分中的指令( .eh_frame.debug_frame ):
$ gcc -m32 -g test.s -c -o a.out
$ objdump -d a.out
[...]
00000000 <main>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 e4 f0 and $0xfffffff0,%esp
6: 83 ec 20 sub $0x20,%esp
9: c7 44 24 14 05 00 00 movl $0x5,0x14(%esp)
10: 00
11: c7 44 24 18 03 00 00 movl $0x3,0x18(%esp)
18: 00
19: 8b 44 24 18 mov 0x18(%esp),%eax
1d: 89 44 24 04 mov %eax,0x4(%esp)
21: 8b 44 24 14 mov 0x14(%esp),%eax
25: 89 04 24 mov %eax,(%esp)
28: e8 fc ff ff ff call 29 <main+0x29>
2d: 89 44 24 1c mov %eax,0x1c(%esp)
31: b8 00 00 00 00 mov $0x0,%eax
36: c9 leave
37: c3 ret

注意如何在 main 的代码中只出现指令。功能。 CFI 在其他地方:
$ readelf -wF a.out 
Contents of the .eh_frame section:

00000000 00000014 00000000 CIE "zR" cf=1 df=-4 ra=8
LOC CFA ra
00000000 esp+4 c-4

00000018 0000001c 0000001c FDE cie=00000000 pc=00000000..00000038
LOC CFA ebp ra
00000000 esp+4 u c-4
00000001 esp+8 c-8 c-4
00000003 ebp+8 c-8 c-4
00000037 esp+4 u c-4

CFI 是描述帧布局的信息(不是 native CPU 指令)。

例子

例如,让我们以这个片段为例:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
.cfi_startproc cfi_startproc初始化函数的 CFI。此时,CFA(规范帧地址,即调用者帧中 %rsp 的地址)由 %esp + 4 给出。 (因为调用者在 call 指令中推送了返回地址):
whatever              <- CFA
return address (ra) <- %esp

CFI 指令在 .eh_frame 中“编译” :
   LOC   CFA      ebp   ra      
00000000 esp+4 u c-4
.cfi_def_cfa_offset.cfi_offset
pushl %ebp指令,这不再适用: cfa ≠ %esp + 4因为 %esp已经改变。在这条指令之后,我们有 cfa = %esp + 8 .调试器需要知道这一点和 .cfi_def_cfa_offset 8指令在 .eh_frame 中生成合适的信息调试器部分: .cfi_def_cfa_offset 8cfa = %esp + 8 中将偏移量设置为 8 .
whatever             <- CFA  = %esp + 8
return address (ra)
caller %ebp <- %esp (= CFA - 8)
pushl %ebp的目的是为了保存 %ebp 的值来自堆栈上的调用者。调试器需要知道这个值的保存位置,以便展开堆栈并恢复调用者帧。 .cfi_offset 5, -8指令指示调试器寄存器 5 ( %ebp ) 已被 cfa - 8 中的前一条指令保存.

此信息可在 .eh_frame 的下一个条目中找到。 table :
   LOC   CFA      ebp   ra
[...]
00000001 esp+8 c-8 c-4

笔记

注 1:在某些情况下,这些信息是调试信息的一部分,这意味着它可能在运行时不存在,并且如果文件没有使用调试信息编译,则它可能根本不存在于文件中。

关于assembly - 在汇编代码中,.cfi 指令如何工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29527623/

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