【发布时间】:2020-11-26 19:52:08
【问题描述】:
我有汇编代码,由 frameCount 调用,需要返回 frameCount,但不确定如何检索(然后导航)前一帧的指针引用。
getFP.s
.globl getFP
getFP:
movq %rbp, %rax
ret
frameCount.c
int frameCount() {
int count = 0;
uint64_t fp = getFP();
uint64_t *sp = &fp;
// how do I get the pointer/offset to pointer to the previous stack frame from here?
return count;
}
更新:
我已更新 frameCount 函数以包含一个遍历堆栈帧链表的循环,但在调用 frameCount 时出现分段错误。
main.c
#include <stdio.h>
#include <inttypes.h>
#include "framecount.c"
int main() {
printf("Number of Frames: %d\n", frameCount());
return(0);
}
frameCount.c
#include <stdio.h>
#include <inttypes.h>
uint64_t* getFP();
int frameCount() {
uint64_t* fp = getFP();
uint64_t registerValue1 = *fp;
while (registerValue1 != 0) {
printf("current register value %" PRIx64 "\n", registerValue1);
printf("next register value %" PRIx64 "\n", *(volatile uint64_t *)registerValue1);
count++;
registerValue1 = *(volatile uint64_t *)registerValue1;
}
printf("count=%d\n", count);
return count;
}
输出
current register value 7ffca7c147b0
next register value 401230
current register value 401230
next register value 8d4c5741fa1e0ff3
current register value 8d4c5741fa1e0ff3
Segmentation fault (core dumped)
但是,当我执行以下操作时,我没有遇到分段错误,但计数似乎不正确: (更新:删除虚假示例)
更新 2:
即使在使用选项 -O0 或 -fno-omit-frame-pointer 运行时仍会出现分段错误以下是初始第一次更新的程序集输出:
.file "lab7.c"
.text
.section .rodata
.LC0:
.string "%d"
.text
.globl frameCount
.type frameCount, @function
frameCount:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movl $0, -4(%rbp)
movl $0, %eax
call getFP
movq %rax, -24(%rbp)
movq -24(%rbp), %rax
movq (%rax), %rax
movq %rax, -16(%rbp)
jmp .L2
.L3:
addl $1, -4(%rbp)
movl -4(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
movq -16(%rbp), %rax
movq (%rax), %rax
movq %rax, -16(%rbp)
.L2:
cmpq $0, -16(%rbp)
jne .L3
movl -4(%rbp), %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size frameCount, .-frameCount
.section .rodata
.LC1:
.string "Number of Frames: %d\n"
.text
.globl main
.type main, @function
main:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $0, %eax
call frameCount
movl %eax, %esi
movl $.LC1, %edi
movl $0, %eax
call printf
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size main, .-main
.ident "GCC: (GNU) 10.2.1 20200723 (Red Hat 10.2.1-1)"
.section .note.GNU-stack,"",@progbits
【问题讨论】:
-
您的最后一个程序是伪造的,因为您正在从
NewFunc3开始的%rbp开始遍历堆栈,之后它已经返回。那时堆栈上的帧指针链可能已被覆盖。 -
您的第一个更新版本适用于我,
gcc -O0并按预期返回计数 1。请注意,frameCount并未将其自身计入调用链中。正如我所指出的,如果您使用优化进行编译,您应该期望它会因段错误或其他原因而失败。您使用了哪些编译器和选项? -
@NateEldredge 是的,感谢您指出这一点。
-
几个通用的 cmets,而我正在考虑它们:(1)不要将 .c 文件包含在彼此中。只写一个带有声明的标题,你包括在内;分别编译 .c 文件,并链接它们。 (2) C 中没有参数的函数声明为
int getFP(void);,而不是int getFP();。后者声明了一个带有 unspecified 参数的函数。特别是,编译器认为它可能是可变参数,因此它必须跳过一些额外的循环来调用它(如果你想知道为什么在所有函数调用之前%rax显然有不必要的归零,这就是原因。) -
无论如何,repl 中的代码似乎也在尝试循环,直到找到指向自身的帧指针。那不是正确的终止条件;正如我在回答中所说,在列表的末尾你会发现零。如果您不对此进行测试,您将取消对它的引用和段错误。
标签: c assembly x86-64 backtrace stack-frame