如何解决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
- 将0的地址加载到
eax
中,将2的地址加载到ebx
中 -
mov [ebx],eax
将 0 的地址写入 2 (2 -> 0
) - 对字段 0 和 1 重复相同的操作:
0 -> 1
,1 -> 2
- 将head地址存入eax
现在我使用
组装和运行整个东西nasm -f elf -g -F dwarf test.asm
ld -m elf_i386 -o test.out test.o
但是,0
和 2
中的值都是错误的,因为我可以使用 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+4
。 head+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 举报,一经查实,本站将立刻删除。