linux - 如何将64位地址传送到寄存器

标签 linux assembly x86-64

我尝试在程序集中打开文件,将内存位置(地址)的值移动到 rdi:

mov r13, [rsi+8*1] ; rsi - where addresses of cmdline parameters are.
mov [file_1], r13
; some calls...
mov rax, 2
mov rdi, [file_1] ; 
xor esi, esi ; automatically zero-extended to rsi
syscall

已解决的问题: 我确定了文件的长度,但没有将查找位置放在文件的开头,例如

mov rax, 8
mov rdi, [rsp+.fd_1]
xor esi, esi
mov edx, edx
syscall

调试:

(gdb) p/x &file_1
$1 = 0x601058
(gdb) x/xg 0x601058
0x601058: 0x00007fffffffe16a
(gdb) x/s 0x7fffffffe16a
0x7fffffffe16a: "abc"

但它无法正确移动。我还读到 mov32 位源操作数,但我需要将 64 位从内存移动到寄存器。尝试在YASM中使用movq,但它给出了关于操作码和操作数的无效组合的语法错误。

最佳答案

系统调用 2 打开一个文件。

参数为:

rax: syscall #2
rdi: pointer: zero terminated filename
rsi: int: flags
rdx: int: mode

您使用以下代码:

...
mov rax, 2          //syscall_2 = open file
...
syscall

但是,根据文档,到目前为止一切顺利:

Given a pathname for a file, open() returns a file descriptor, a small, nonnegative integer for use in subsequent system calls.

所以您只完成了第一部分,您希望获得一个文件描述符,通过它您可以从文件中读取数据,您需要将该部分添加到代码中。
最后,完成后您需要自行清理并关闭文件

让我为您完成代码。

//********* get a file_descriptor.
push r15             //save non-volatile registers
push r14
mov eax,2            //syscall_2 = open file
mov rdi,[file_1]     //filename
xor esi,esi          //flags = O_RDONLY
xor edx,edx          //mode = 0, good habits: don't leave parameters undefined. 
syscall              //get me a file descriptor (fd).
test eax,eax         //is the fd positive?
mov edi,eax          //param1=fd
mov eax,1            //set failure code, just in case.
js failure           //no,it's negative,report failure.

//*********** read some data
//Step 2, we have a fd, lets read the data in the file.

mov r15,rax          //save the fd in a non-volatile register for later.
xor eax,eax          //syscall_0 = read data from file
mov rsi,[buffer]     //an array to place the data into
                     //`buffer` is the pointer to the buffer. 
mov edx,[len]        //the max_len of the buffer, must be >= 2.
dec edx              //keep an extra byte to put a zero terminator into.
//put 2 zero's if you're reading unicode_16 data.
syscall              //Read that data.
xor r14,r14          //assume a length of zero in case of failure.
                     //we can't jump to failure, we still need to clean up!
test eax,eax         //do we have a fail?
cmovs rax,r14        //if failure, then set zero length result, else do nothing.
mov [len],eax        //set len to the length of data read.
mov byte ptr [buffer+eax],0  //add a zero byte to terminate the data.

//************ clean up
//we are done, got the data. Let's close the file.

mov r14,rax          //if rax=0 then we had an error, store for later use
mov rdi,r15          //param1 = fd
mov eax,3            //syscall_3: close file
syscall              //close that file.
test eax,eax         //did close go ok?
mov eax,3            //set failure code just in case.
js failure           //negative = failure
//************ report back
//if we get here, all went ok, report success and return.
xor eax,eax          //return 0 for success.
//we still need to check if `read()` failed.   
sub r14,1            //carry = 1 only if r14 = 0 = failure else carry = 0
//sub eax,(0+carry)  //if success then no-op else eax=eax-1
sbc eax,eax          //eax = -1 if we had a failure when reading.
failure:
pop r14              //restore non-volatile registers
pop r15
ret                  //all done

关于 64 位与 32 位

Also I read that mov has 32 bit source operand [...]

这完全是胡言乱语。

X64 是一款成熟的 64 位处理器,它的功能正如其包装上所言。
如果您使用 64 位寄存器(任何以 R 开头(且不以 db 结尾)的寄存器),您将使用该寄存器中的所有 64 位。
指向内存的指针应始终进入 64 位 R 寄存器。
如果寄存器以 E 开头(或以 d 结尾),则它是 32 位寄存器(低 32 位)。
对 32 位寄存器的任何写操作都会将重叠的 64 位寄存器的前 32 位清零,例如mov eax,1rax 设置为 1; mov eax,-1rax 设置为 $0000 0000 FFFF FFFF;

mov eax,2mov rax,2 执行完全相同的操作。
除了 mov eax,2 短了两个字节,因此是首选。

如果您从内存读取/写入,操作将遵循寄存器的大小,除非您另外指定。
如果将立即数写入内存,则必须指定大小。
您无法将 64 位立即数写入内存,必须使用 2 条指令来执行此操作:mov R64,imm64 + mov [mem],R64。

警告
对 16 位或 8 位寄存器的任何写入不会将关联的 32/64 位寄存器的顶部部分清零!

结论
如果你想编写汇编,你需要真正从头开始学习汇编。
因为您正在 Linux 中编程,所以我建议您获取 Jeff Duntemann 的书的副本:Assembly Language Step By Step, for Linux
杰夫是一位传奇人物,他比这个星球上的任何人都能更好地解释组装。

关于linux - 如何将64位地址传送到寄存器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39354326/

相关文章:

mysql - 如何查询 1 个表中的 2 列和第二个表中的另一列

linux - 如何使用 File::stat 模块在 Perl 中查找文件夹大小?

gcc - 在 64 位 (x86-64) 机器上构建 32 位 (x86) gcc-10.2.0 时出错

linux - 一个用于计算按键排序的字段的平均值的单行?

命令输出(stdout、stderr)未重定向到管道

gcc - x86-64 汇编语言中的 ELF 共享对象

assembly - 哪个 x86 寄存器表示 movsb 指令中的源位置?

c++ - 将 .asm 文件编译并链接到 C++ 代码

assembly - 如何简化引用自身作为参数的函数? (这是什么意思)

c - 线程局部变量和 fs 段