Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Linux 32位模式下的NASM x86_64汇编:为什么此指令会生成RIP相对寻址代码?_Linux_Assembly_X86 64_Nasm_Relative Addressing - Fatal编程技术网

Linux 32位模式下的NASM x86_64汇编:为什么此指令会生成RIP相对寻址代码?

Linux 32位模式下的NASM x86_64汇编:为什么此指令会生成RIP相对寻址代码?,linux,assembly,x86-64,nasm,relative-addressing,Linux,Assembly,X86 64,Nasm,Relative Addressing,这里最基本的事情是,我需要有hello字符串的长度来传递给linux的sys_write系统调用。现在,我很清楚我可以使用EQU,它会很好地工作,但我真的很想了解这里发生了什么 所以,基本上,当我使用eq时,它会加载值,这很好 [bits 32] global _start section .data str_hello db "HelloWorld", 0xa str_hello_length db $-str_hello

这里最基本的事情是,我需要有hello字符串的长度来传递给linux的sys_write系统调用。现在,我很清楚我可以使用EQU,它会很好地工作,但我真的很想了解这里发生了什么

所以,基本上,当我使用eq时,它会加载值,这很好

[bits 32]
    global _start

    section .data
    str_hello       db  "HelloWorld", 0xa
    str_hello_length    db      $-str_hello

    section .text

    _start:

        mov ebx, 1              ; stdout file descriptor
        mov ecx, str_hello      ; pointer to string of characters that will be displayed        
        mov edx, [str_hello_length] ; count outputs Relative addressing
        mov eax, 4              ; sys_write
        int 0x80                ; linux kernel system call

        mov ebx, 0  ; exit status zero
        mov eax, 1  ; sys_exit
        int 0x80    ; linux kernel system call
但是,如果我将此行与DB一起使用

str_hello_length equ $-str_hello
...
...
mov edx, str_hello_length
汇编器没有像我期望的那样在该地址加载值,而是输出RIP相对寻址,如gdb调试器中所示,我只是想知道为什么

str_hello_length db $-str_hello
...
...
mov edx, [str_hello_length]     ; of course, without the brackets it'll load the address, which I don't want. I want the value stored at that address
现在,我尝试改用eax寄存器(然后将eax移到edx),但后来我遇到了一个不同的问题。我最终得到了一个分段错误,如gdb中所述:

mov    0x6000e5(%rip),%edx        # 0xa001a5
显然,不同的寄存器产生不同的代码。我想我需要以某种方式截断上面的32位,但我不知道怎么做

虽然确实找到了一个“解决方案”,它是这样的: 用str_hello_length的地址加载eax,然后加载eax指向的地址的内容,所有内容都是hunky dory

movabs 0x4b8c289006000e5,%eax
显然,试图从mem地址间接加载值会产生不同的代码?我真的不知道


我只需要帮助理解这些指令的语法和操作,以便更好地理解为什么要加载有效地址。是的,我想我本可以切换到EQU并开始我的快乐之旅,但我真的觉得我不能继续下去,直到我理解DB声明和从其地址加载的情况。

问题可能是
str\u hello\u length
的偏移量大于32位。IA-32不支持大于32位的位移。解决方法是使用RIP相对寻址,前提是(通常是正确的)RIP和您试图达到的地址之间的距离符合32位。在这种情况下,基是
RIP
,索引是指令长度,因此如果指令已经有基或索引,则不能使用RIP Relative

让我们检查一下您的各种尝试:

mov eax, str_hello_length       
mov edx, [eax]  ; count


; gdb disassembly
mov    $0x6000e5,%eax
mov    (%rax),%edx
这里没有内存访问,只有一个简单的即时移动,所以根本没有寻址

下一步:

现在,第一条指令是带有立即数的移动,它仍然不是内存访问。第二条指令具有内存访问,但它使用
eax
作为基,并且没有位移。只有当存在位移时,相对撕裂才相关,所以这里没有相对撕裂

最后:

mov eax, str_hello_length       
mov edx, [eax]  ; count

这里您使用的是
str\u hello\u length
作为置换。如上所述,这将导致RIP相对寻址。

答案是不是。x86-64在32位仿真模式下没有RIP相对寻址(这应该是显而易见的,因为RIP在32位中不存在)。发生的事情是,nasm正在编译一些可爱的32位操作码,您正试图以64位运行这些操作码。GDB将您的32位操作码分解为64位,并告诉您,在64位中,这些字节表示RIP相对mov。x86-64上的64位和32位操作码重叠很多,以利用硅芯片中的通用解码逻辑,您会感到困惑,因为GDB正在分解的代码看起来与您编写的32位代码相似,但实际上您只是在向处理器扔垃圾字节


这与nasm无关。您对所处的流程使用了错误的体系结构。在32位进程中使用32位nasm或编译[位64]的汇编代码。

您要求汇编程序以32位模式为目标(使用
位32
),但您将32位机器代码放入64位对象文件中,然后查看将其作为x86-64机器代码反汇编时会发生什么

因此,您看到了x86-32和x86-64中指令编码之间的差异。i、 e.这是将32位机器代码解码为64位时发生的情况


mov 0x6000e5(%rip),%edx#0xa001a5

在这种情况下,关键的一点是32位x86有两种冗余方式来编码32位绝对地址(不带寄存器):带或不带SIB字节。32位模式没有RIP相对(或EIP相对)寻址

x86-64将较短的(
ModR/M+disp32
)形式重新用作RIP相对寻址模式,而较长的
ModR/M+SIB+disp32
编码仍然可以使用32位绝对寻址。(当然,SIB字节不编码基址寄存器和索引寄存器。有趣的事实:在本例中,scale字段未使用,但AMD64选择不使用这些位)

详细说明ModRM编码中的“转义码”,它可以对特殊情况(如SIB present或no base reg)进行编码

请注意,从RIP解码的偏移量实际上是放置数据的绝对静态地址(64位代码),
0x6000e5

注释是显示有效绝对地址的反汇编程序;RIP相对寻址计数来自指令后的字节,即下一条指令的开始


movabs 0x4b8c289006000e5,%eax

当目标寄存器为EAX时,汇编器(在32位模式下)选择较短的
mov
编码,该编码从32位绝对地址加载
EAX
,该地址不包含ModR/M字节,只包含
A1 disp32
。(内存偏移量)而不是有效地址

在x86-64模式下,该操作码采用64位绝对地址。(独特之处在于能够从64位绝对(非RIP相对)地址加载/存储,而无需先将地址放入寄存器)。因此,解码消耗下一条指令的一部分作为64位地址的一部分,这就是地址中一些高字节的来源。
0x6000e5mov eax, str_hello_length       
mov edx, [eax]  ; count
str_hello_length db $-str_hello
...
...
mov edx, [str_hello_length]     ; of course, without the brackets it'll load the address, which I don't want. I want the value stored at that address