微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

NASM - 将一个变量的地址存储在另一个变量中

如何解决NASM - 将一个变量的地址存储在另一个变量中

我目前正在学习汇编。 我想编写一个程序,它以三个相互指向的变量结束:

0x804a000: 0x804a001  ; 0 -> 1
0x804a001: 0x804a002  ; 1 -> 2
0x804a002: 0x804a000  ; 2 -> 0

According to some other posts,我可以检索(例如对于mov):

  • 使用 x 的变量 [x]内容
  • 使用 x 的变量 x 的地址

这是我目前想到的:

section .bss
    head resd 3         ; reserve three dwords

section .text
  global _start
    
_start:

  xor eax,eax
  xor ebx,ebx
  xor ecx,ecx          ; counter = 0

  mov eax,head         ; move the address of head into eax: eax -> 0
  mov ebx,eax          ; move the address of head from eax into ebx: ebx -> 0
  add ebx,2            ; ebx -> 2
  mov [ebx],eax        ; move the value of eax ( = the address of 0 ) into the address in ebx ( = the address of 2)
  
  loop:                 ; first run     second run
    inc ecx             ; eax -> 0      eax -> 1
    mov ebx,eax        ; ebx -> 0      ebx -> 1
    add eax,1          ; eax -> 1      eax -> 2
    mov [ebx],eax      ; 0 -> 1            1 -> 2
    cmp ecx,2          ; ecx = 1 < 2   ecx = 2 == 2
  jl loop

  mov eax,head         ; eax points to the first element
    
  mov   eax,1           ; system call number (sys_exit)
  int   0x80            ; call kernel

这应该基本上 0. 保留三个双字,第一个head

中的地址
  1. 将0的地址加载到eax中,将2的地址加载到ebx
  2. mov [ebx],eax 将 0 的地址写入 2 (2 -> 0)
  3. 对字段 0 和 1 重复相同的操作:0 -> 1,1 -> 2
  4. 将head地址存入eax

现在我使用

组装和运行整个东西
nasm -f elf -g -F dwarf test.asm
ld -m elf_i386 -o test.out test.o

但是,02 中的值都是错误的,因为我可以使用 gdb 进行检查:

gdb test.out
(gdb) b 27 // break after mov eax,head
(gdb) r
(gdb) i r eax
eax     0x804a000    134520832 // eax points to head
(gdb) print *134520832
$1 = 77595137                  // cell 0 does not point to cell 1
(gdb) print *134520833
$2 = 134520834                 // cell 1 does point to cell 2
(gdb) print *134520834
$3 = 134743200                 // cell 2 does not point to cell 1

这些错误值从何而来?

这可能是因为我尝试将整个 32 位 eax 写入 16 位双字? 我尝试将行更改为 mov [ebx],ax,但结果相同。

我能想到的另一个原因是内存地址比 dword 大,所以我尝试使用 qword 代替,但最终得到另一个错误结果。

我还尝试使用 lea assembly instruction 中所建议的 lea 指令,这会导致相同的结果。

有人可以帮我解决这个问题吗?提前致谢

解决方法

一个双字在 x86 中是 32 位的,所以是 4 个字节。 “双字”,其中“字”是 16 位(因为 x86 是从 16 位 8086 演变而来的)。是的,正如您所发现的,x86 是字节可寻址的,就像所有现代主流 ISA 一样。

此外,例如,标题问题的答案是 mov dword [head],head+4head+4 在 assemble+link 时被评估并变成一个 32 位立即数操作数来保存那个地址,而 head 变成一个 32 位位移保存另一个地址。

或者你可以像你正在做的那样使用循环,但简化为 mov [eax-4],eax 以将当前元素的地址存储到前一个元素中,使用 add eax,4 来推进指针。无需复制到 EBX,只需对内存操作数使用寻址模式进行常量偏移即可。

如果您想要整个循环/函数的完整示例,请用 C 语言编写并查看编译器输出。 How to remove "noise" from GCC/clang assembly output?

,

好吧,在阅读更多关于此的内容后,解决方案非常明显。 每个地址对应一个字节。如果我所有的单元格都是 qwords,我需要将每个循环中的地址增加 4,而不是 1:

section .bss
    head resq 4         ; three qwords

section .text
  global _start
    
_start:

    xor eax,eax
    xor ebx,ebx
    xor ecx,ecx        ; counter = 0

    mov eax,head       ; move the address of head into eax: eax -> 0
    mov ebx,eax        ; eax -> 0
    add ebx,12         ; ebx -> 3
    mov [ebx],eax      ; 3 -> 0

    loop:               ; first run     second run
    inc ecx             ; counter = 0 counter = 1
    mov ebx,eax        ; ebx -> 0      ebx -> 1
    add eax,4          ; eax -> 1      eax -> 2
    mov [ebx],eax      ; 0 -> 1            1 -> 2
    cmp ecx,3          ; ecx = 1 < 2   ecx = 2 == 2
    jl loop

    mov eax,head       ; eax points to the first element
    
  mov   eax,1           ; system call number (sys_exit)
  int   0x80            ; call kernel

地址(至少对于我的设置)最多 32 位 = 4 字节大,所以我的第一次尝试没有成功,因为我用下一个写入的地址覆盖了每个地址。这就是为什么只有第二个值是正确的 - 它是最后一个写入的值。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。