Memory GDB中的x86标签和LEA

Memory GDB中的x86标签和LEA,memory,assembly,gdb,x86,nasm,Memory,Assembly,Gdb,X86,Nasm,我正在学习用x86汇编(目前为32位)编写代码,我正在努力完全理解内存模型。尤其令人困惑的是标签的语义、LEA指令和可执行文件的布局。我编写了这个示例程序,以便检查它在gdb中的运行情况 ; mem.s SECTION .data msg: db "labeled string\n" db "unlabeled-string\n" nls: db 10,10,10,10,10,10,10,10 SECTION .text global _start _start:

我正在学习用x86汇编(目前为32位)编写代码,我正在努力完全理解内存模型。尤其令人困惑的是标签的语义、LEA指令和可执行文件的布局。我编写了这个示例程序,以便检查它在gdb中的运行情况

; mem.s
SECTION .data
    msg: db "labeled string\n"
    db "unlabeled-string\n"
    nls: db 10,10,10,10,10,10,10,10
SECTION .text
global  _start
_start:
    ; inspect msg label, LEA instruction
    mov eax, msg
    mov eax, &msg
    mov eax, [msg]
    ; lea eax, msg (invalid instruction)
    lea eax, &msg
    lea eax, [msg]

    ; populate array in BSS section
    mov [arr], DWORD 1
    mov [arr+4], DWORD 2
    mov [arr+8], DWORD 3
    mov [arr+12], DWORD 4

    ; trying to print the unlabeled string
    mov eax, 4
    mov ebx, 1
    lea ecx, [msg+15]
    int 80H

    mov eax, 1      ; exit syscall
    mov ebx, 0      ; return value
    int 80H
SECTION .bss
    arr: resw 16
我收集并链接了:

nasm -f elf -g -F stabs mem.s
ld -m elf_i386 -o mem mem.o
GDB会议:

(gdb) disas *_start
Dump of assembler code for function _start:
   0x08048080 <+0>: mov    $0x80490e4,%eax
   0x08048085 <+5>: mov    0x80490e4,%eax
   0x0804808a <+10>:    mov    0x80490e4,%eax
   0x0804808f <+15>:    lea    0x80490e4,%eax
   0x08048095 <+21>:    lea    0x80490e4,%eax
   0x0804809b <+27>:    movl   $0x1,0x8049110
   0x080480a5 <+37>:    movl   $0x2,0x8049114
   0x080480af <+47>:    movl   $0x3,0x8049118
   0x080480b9 <+57>:    movl   $0x4,0x804911c
   0x080480c3 <+67>:    mov    $0x4,%eax
   0x080480c8 <+72>:    mov    $0x1,%ebx
   0x080480cd <+77>:    lea    0x80490f3,%ecx
   0x080480d3 <+83>:    int    $0x80
   0x080480d5 <+85>:    mov    $0x1,%eax
   0x080480da <+90>:    mov    $0x0,%ebx
   0x080480df <+95>:    int    $0x80
数组按预期填充了值1,2,3,4:

# before program execution:
(gdb) x/16w &arr
0x8049104 <arr>:    0   0   0   0
0x8049114:  0   0   0   0
0x8049124:  0   0   0   0
0x8049134:  0   0   0   0
# after program execution
(gdb) x/16w &arr
0x8049104 <arr>:    1   2   3   4
0x8049114:  0   0   0   0
0x8049124:  0   0   0   0
0x8049134:  0   0   0   0
程序执行前: (gdb)x/16w和arr 0x8049104:0 0x8049114:0 0x8049124:0 0x8049134:0 #程序执行后 (gdb)x/16w和arr 0x8049104:1 2 3 4 0x8049114:0 0x8049124:0 0x8049134:0 我不明白为什么在gdb中打印标签会导致这两个值。另外,如何打印未标记的字符串。
提前感谢

< p>它有些混淆,因为GDB不理解标签的概念,实际上它是用来调试高级语言(C或C++)编写的程序,一般来说。因此,它试图将二进制中看到的内容映射到高级语言概念——变量和类型——基于对正在发生的事情的最佳猜测(在没有编译器的调试信息告诉它正在发生什么的情况下)

nasm做什么

对于汇编器来说,标签是尚未设置的值——它实际上在链接器运行时获得其最终值。通常,标签用于引用内存段中的地址——实际地址将在链接器布局最终可执行映像时定义。汇编器生成重定位记录,以便使用链接器可以正确设置标签

所以当汇编程序看到

mov eax, msg
它知道
msg
是与数据段中的地址相对应的标签,因此它生成一条指令将该地址加载到eax中

mov eax, [msg]
它生成一条指令,从
msg
地址的内存中加载32位(寄存器eax的大小)。在这两种情况下,都会生成重新定位,以便链接器可以插入最终地址
msg

(撇开它不谈——我不知道
&
对nasm意味着什么——它没有出现在我能看到的文档中的任何地方,所以我很惊讶它没有给出错误。但它看起来像是将它作为
[]
的别名对待)

现在LEA是一个有趣的指令——它的格式基本上与从内存中移出的指令相同,但它不读取内存,而是将本来读取的地址存储到目标寄存器中

lea eax, msg
毫无意义——源是标签(地址)
msg
,它是一个(链接时间)常量,不在内存中的任何位置

lea eax, [msg]
有效,因为源在内存中,所以它会将源地址粘贴到eax中。这与
mov eax,msg
的效果相同。最常见的情况是,您只会看到
lea
与更复杂的寻址模式一起使用,这样您就可以利用x86 AGU进行有用的工作,而不仅仅是计算地址。例如:

lea eax, [ebx+4*ecx+32]
它在AGU中执行一个移位和两个加法,并将结果放入eax,而不是从该地址加载

gdb做什么

在gdb中,当您键入
p
时,它会尝试对
进行评估,以尽可能理解C/C++编译器对该表达式的意义

(gdb) p msg
它查看
msg
并说“这看起来像一个变量,所以让我们获取该变量的当前值并打印它。”“。现在它知道编译器喜欢将全局变量放入.data段,并为这些变量创建与变量同名的符号。由于它将符号表中的
msg
视为
.data
段中的一个符号,因此它假定这就是正在发生的事情,并获取该符号处的内存并打印它。现在它不知道这个变量是什么类型(没有调试信息),所以它猜测它是一个32位int,并以此打印它

因此,输出

$1 = 1700946284
是msg的前4个字节,被视为整数

对于
p&msg
,它理解您想要获取变量
msg
的地址,因此它直接从符号中给出地址。当打印地址时,gdb打印关于这些地址的类型信息,从而输出“数据变量,无调试信息”

如果需要,您可以使用强制转换来指定gdb的类型,它将使用该类型而不是猜测的类型:

(gdb) p (char)msg
$6 = 108 'l'
(gdb) p (char [10])msg
$7 = "labeled st"
(gdb) p (char *)&msg
$8 = 0x80490e4 "labeled string\\nunlabeled-string\\n\n\n\n\n\n\n\n\n" <Address 0x804910e out of bounds>
克里斯·多德经济特区


(旁白——我不知道&对nasm意味着什么——它没有出现在我能看到的文档中的任何地方,所以我很惊讶它没有给出一个错误。但它看起来像是把它当作[]的别名。) 哦,哦!你已经发现了秘密语法!很久以前,每个用户请求都向Nasm添加了“&”(作为“[]”的别名)。它从未被记录在案。也从未被移除过。我会坚持使用“[]”。由于“无文件记录”,它可能会消失。请注意,其含义几乎与它对gdb的含义“相反”

可以尝试使用“-F矮人”而不是“-F刺”。它应该是gdb使用的“本机”调试信息格式。(我自己从未注意到有多大的不同)

最好的

坦率的


我可以通过以下方式获得另一个值:
(gdb)p(int[])&msg
$1={134516964}
有没有办法用sys\u write系统调用打印未标记的字符串?我可以像这样打印未标记的字符串:
(gdb)p(char*)(&msg)+16
$1=0x80490f4“未标记的字符串\\n\n\n\n\n\n\n
另一个值是msg的十进制地址
(gdb)p/d&msg
$10=134516964
注意:sys\u write需要edx处字符串的长度,因此:
mov edx,17
int 80H
$1 = 1700946284
(gdb) p (char)msg
$6 = 108 'l'
(gdb) p (char [10])msg
$7 = "labeled st"
(gdb) p (char *)&msg
$8 = 0x80490e4 "labeled string\\nunlabeled-string\\n\n\n\n\n\n\n\n\n" <Address 0x804910e out of bounds>
    mov ebx, 1           ; fd 1 (stdout)
    lea ecx, [msg+15]    ; address
    mov edx, 17          ; length
write_more:
    mov eax, 4           ; sys_write
    int 80H              ; write(1, &msg[15], 17)
    test eax, eax        ; check for error
    js error             ; error, eax = -ERRNO
    add ecx, eax
    sub edx, eax
    jg write_more        ; only part of the string was written