汇编-如何在函数调用之间正确使用堆栈

时间:2019-12-05 17:25:36

标签: linux assembly gdb

我对如何在组装中使用堆栈感到困惑。我试图了解如何在函数之间传递和读取变量,为此,我编写了一个简短的程序,并使用gdb对其进行了调试(我在Linux上运行的vmware上使用sasm编写)。这是我的代码:

.text
.global main
main:
    movq $7, %rax
    movq $2, %r13
A:
    pushq %rax              # insert 7
    pushq %r13              # insert 2
    call B
    popq %r13
    popq %rax
    dec %rax
    inc %r13
    cmp $5, %r13
    je end
    call A
    addq $8, %rsp
    ret
B:
    movq 8(%rsp), %rcx        # pop 2
    movq 16(%rsp), %rdx       # pop 7
    xorq %rax, %rax
end:
    ret

它的工作原理与众不同,但是以退出代码04退出,而且当我查看堆栈时,它的行为也很奇怪。我有两件事我不确定- 1.我了解到,在每次调用后,我都需要以某种方式提高rsp(通过使用pop或add),以防止分段错误。即在行后

call A

由于ra,我必须根据调用之前插入的元素数+ 8弹出堆栈。如果我没有推送任何元素,我只需要将rsp增加8,因为ra仍然存在,在这种情况下我无法从程序中返回。但是当我运行正在程序末尾执行的命令时:

    addq $8, %rsp

堆栈看起来像这样:

#0  0x00000000004005d5 in A ()
#1  0x00007ffff7a05b97 in __libc_start_main (main=0x4005a7 <main>, argc=1, argv=0x7fffffffe028, init=<optimized out>,
    fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe018) at ../csu/libc-start.c:310
#2  0x00000000004004ea in _start ()

我认为它将是空的。为什么我仍然在#0中看到A,那里的标准库在做什么?据我了解,我不需要触碰这些信息,但这不只是退出堆栈中的某些信息吗?我的意思是,这会导致sigsegv吗?

我尝试过在线查找我认为某种程度上相关的退出代码4,但是我只发现了“打开文件问题”(显然不是这种情况)和“网络问题”,但我没有看到它与退出而不弹出堆栈上的所有元素的事实有什么关系。运行strace后,我得到以下输出:

execve("./stak", ["./stak"], 0x7ffe6ace7370 /* 51 vars */) = 0
brk(NULL)                               = 0x18ce000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=104376, ...}) = 0
mmap(NULL, 104376, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fd062dab000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\34\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd062da9000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fd0627ad000
mprotect(0x7fd062994000, 2097152, PROT_NONE) = 0
mmap(0x7fd062b94000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7fd062b94000
mmap(0x7fd062b9a000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fd062b9a000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7fd062daa4c0) = 0
mprotect(0x7fd062b94000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ)     = 0
mprotect(0x7fd062dc5000, 4096, PROT_READ) = 0
munmap(0x7fd062dab000, 104376)          = 0
exit_group(4)                           = ?
+++ exited with 4 +++

我听不懂。我可以看到由于ENOENT,打开某些文件失败,但是我不明白是什么文件。在手册上也找不到任何东西。

如果有人可以指导我我在做什么错,我将不胜感激-为什么我的堆栈仍保存着元素?我是否正确使用堆栈?也许有人知道退出代码4是什么意思?除了我已经指定的内容之外,我还尝试过在线查找,但没有发现任何相关内容。

感谢您的帮助,对于长期的问题,我们深表歉意,如果有问题,我可以尝试将其缩短,只想提供任何有用的信息。


编辑:使用命令addq删除行后,程序运行正常,并且在从程序返回之前,我的堆栈如下所示:

(gdb) bt
#0  0x00000000004005d1 in A ()
#1  0x00000000004005d1 in A ()
#2  0x00007ffff7a05b97 in __libc_start_main (main=0x4005a7 <main>, argc=1, argv=0x7fffffffe028, init=<optimized out>,
    fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe018) at ../csu/libc-start.c:310
#3  0x00000000004004ea in _start ()

我有2 RA的意思吗?

0 个答案:

没有答案