Assembly 对于.bss中的符号和.data中的符号,gdb的行为不同

Assembly 对于.bss中的符号和.data中的符号,gdb的行为不同,assembly,x86,gdb,x86-64,yasm,Assembly,X86,Gdb,X86 64,Yasm,我最近开始使用YASM学习英特尔x86-64体系结构的汇编语言。在解决一本书(Ray Seyfarth)中建议的一项任务时,我遇到了以下问题: 当我将一些字符放在.bss部分的缓冲区中时,在gdb中调试它时仍然看到一个空字符串。将字符放入.data节中的缓冲区将在gdb中按预期显示 segment .bss result resb 75 buf resw 100 usage resq 1 segment .data str_test db 0, 0, 0,

我最近开始使用YASM学习英特尔x86-64体系结构的汇编语言。在解决一本书(Ray Seyfarth)中建议的一项任务时,我遇到了以下问题:

当我将一些字符放在.bss部分的缓冲区中时,在gdb中调试它时仍然看到一个空字符串。将字符放入.data节中的缓冲区将在gdb中按预期显示

segment .bss
result  resb    75
buf resw    100
usage   resq    1

    segment .data
str_test    db 0, 0, 0, 0

    segment .text
    global main
main:
    mov rbx, 'A'
    mov [buf], rbx          ; LINE - 1 STILL GET EMPTY STRING AFTER THAT INSTRUCTION
    mov [str_test], rbx     ; LINE - 2 PLACES CHARACTER NICELY. 
    ret
在gdb中,我得到:

  • 在第1行之后:
    x/s&buf
    ,结果-
    0x7ffff7dd2740:“

  • 第2行之后:
    x/s&stru测试
    ,结果-
    0x601030:“A”

看起来
&buf
的计算结果不是正确的地址,所以它仍然看到所有的零。根据0x7ffff7dd2740的
/proc/PID/maps
,它不在正在调试的进程的BSS中,因此没有任何意义为什么
&buf
计算到错误的地址,而
&str_test
计算到正确的地址?两者都不是“全局”符号,但我们使用调试信息进行了构建

在x86-64 Ubuntu 15.10上使用GNU gdb(Ubuntu 7.10-1ubuntu2)7.10进行测试

我在和你一起建房子

yasm -felf64 -Worphan-labels -gdwarf2 buf-test.asm
gcc -g buf-test.o -o buf-test
可执行文件上的
nm
显示正确的符号地址:

$ nm -n  buf-test     # numeric sort, heavily edited to omit symbols from glibc
...
0000000000601028 D __data_start
0000000000601038 d str_test
... 
000000000060103c B __bss_start
0000000000601040 b result
000000000060108b b buf
0000000000601153 b usage

(编者按:我重写了很多问题,因为奇怪之处在于gdb的行为,而不是OP的asm!)。

glibc还包括一个名为
buf
的符号

(gdb) info variables ^buf$
All variables matching regular expression "^buf$":

File strerror.c:
static char *buf;

Non-debugging symbols:
0x000000000060108b  buf            <-- this is our buf
0x00007ffff7dd6400  buf            <-- this is glibc's buf

带有rel32编码的普通
调用
指令无法到达库函数,但它不需要到达,因为GNU/Linux共享库必须支持符号插入,因此对库函数的
调用实际上会跳到PLT,其中一个间接
jmp
(带有来自GOT的指针)转到最终目的地。

我基本上重写了问题,因为您的asm正在工作。问题是gdb中的
&buf
是堆栈上的某个奇怪地址<代码>0x7ffff…
地址是x86-64 Linux中的堆栈地址。bss和数据地址始终处于低2GB(因此它们适合有符号32位整数,因为许多x86-64指令使用符号扩展的
imm32
立即。例如
add rax,symbol
使用
add r/m64,imm64
编码)。无论如何,我在我自己的x86-64桌面上测试了自己,并重现了gdb的怪异之处,但没有找到在gdb表达式中获得
buf
的正确地址的方法。另一个错误地址的线索是它是16B对齐的(最后一个十六进制数字是0),但我们知道
buf
不应该是。它跟随一个
resb 75
,bss的起点通常为16B对齐(正如我们可以看到的,
result
在nm输出中。是的,这些符号地址是每次运行可执行文件时实际映射的位置,因为它不可重定位。Linux可执行文件的代码不必是位置独立的,不像动态库需要那样(使用
-fPIC
编译,或避免手写asm中的绝对地址。)谢谢,Peter,据我所知,这是一种gdb行为,是否可以处理它并以某种方式将字符放在.bss部分。堆栈中buf而不是.bss的位置不正确真的让我很恼火,我不明白为什么会这样。我应该以某种方式对齐buf吗?您的asm完全按照您的想法进行。唯一的问题是让gdb向您显示地址
0x060108b
中的内容,方法是键入与buf有关的内容,而不是复制/粘贴数字地址。我运行了
x/s 0x0060108b
,得到
x60108b:“A”
请注意,您可以在反汇编中看到数字地址。我的
~/.gdbinit
中有
设置反汇编风格的英特尔
layout reg
,因此我不必使用
disas
命令查看当前RIP周围的反汇编指令。现在很明显,这是一个老生常谈的名称冲突libc中的buf映射到堆栈附近的某个位置。因此值得为标签和变量选择适当的名称。谢谢,Peter。@BulatM.:如果没有安装glibc的调试符号,您可能不会有问题。它不会真正污染全局名称空间。(当然,您的
buf
也不是全局符号)。这就是为什么可以有两个地址不同的符号而不会出现链接错误。为什么gdb选择从libc而不是asm程序显示buf?以及如何故意查看libc的buf和程序的buf的内容?我的意思是如何在gdb命令中区分它们。@BulatM.:在程序开始运行之前,
buf
是您的程序的符号。但在它开始运行后,gdb会在动态加载时从glibc加载符号。我猜gdb会使用它最后看到的调试符号中的条目。OTOH,如果您使用
静态短buf[100]调试C
,我认为gdb会根据符号元数据和其他东西知道。特别是如果您使用
-g
编译它。但是在汇编中,您必须手动使用指令来生成调试信息。IDK如何消除符号名称的歧义。这可能是不可能的,或者可能有某种文件名前缀。您说过使用指令“您必须手动使用指令来生成调试信息。”请您建议使用YASM语法?这在其他程序中也可能有用。事实上,我是通过YASM选项生成调试符号的:YASM-f elf64-g dwarf2-m amd64 main.asm;clang-o main.o
7ffff7a0f000-7ffff7bcf000 r-xp 00000000 09:7f 17031175       /lib/x86_64-linux-gnu/libc-2.21.so
7ffff7bcf000-7ffff7dcf000 ---p 001c0000 09:7f 17031175       /lib/x86_64-linux-gnu/libc-2.21.so
7ffff7dcf000-7ffff7dd3000 r-xp 001c0000 09:7f 17031175       /lib/x86_64-linux-gnu/libc-2.21.so
7ffff7dd3000-7ffff7dd5000 rwxp 001c4000 09:7f 17031175       /lib/x86_64-linux-gnu/libc-2.21.so
7ffff7dd5000-7ffff7dd9000 rwxp 00000000 00:00 0        <--- &buf is in this mapping
...
7ffffffdd000-7ffffffff000 rwxp 00000000 00:00 0              [stack]     <---- more FFs before the first non-FF than in &buf.