如何将64位地址移动到寄存器
how to mov 64 bit address to register
我尝试在程序集中打开文件,将内存位置(地址)的值移动到 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
= 0x601058
(gdb) x/xg 0x601058
0x601058: 0x00007fffffffe16a
(gdb) x/s 0x7fffffffe16a
0x7fffffffe16a: "abc"
但它没有正确移动。我还读到 mov
有 32 位源操作数,但我需要将 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
开头(并且不以 d
或 b
结尾的寄存器) ) 您使用该寄存器中的所有 64 位。
指向内存的指针应该总是进入64位R
寄存器。
如果寄存器以 E
开头(或以 d
结尾),则它是一个 32 位寄存器(低 32 位)。
对 32 位寄存器的任何写操作都会将重叠的 64 位寄存器的前 32 位清零,例如mov eax,1
将 rax
设置为 1; mov eax,-1
将 rax
设置为 [=23=]00 0000 FFFF FFFF
;
mov eax,2
和 mov rax,2
做完全相同的事情。
除了 mov eax,2
短了两个字节,因此是首选。
如果你 read/write from/to 内存操作将遵循寄存器的大小,除非你另有指定。
如果将立即数写入内存,则必须指定大小。
您不能将 64 位立即数写入内存,您必须使用 2 条指令来执行此操作:mov R64,imm64 + mov [mem],R64
。
警告
对 16 位或 8 位寄存器的任何写入都不会将关联的 32/64 位寄存器的顶部清零!
结论
如果你想写汇编,你需要从头开始真正学习汇编。
因为你在 Linux 编程,所以我建议你买一本 Jeff Duntemann 的书:Assembly Language Step By Step, for Linux
杰夫是一个传奇人物,他比这个星球上的任何人都能更好地解释装配。
I determined length of the file but did not put seek position to beginning of the file
你读到最后找到长度了吗,把文件位置留在EOF?首先不要这样做,使用 fstat
查找您打开的文件的长度(如果有的话)。
如果是普通文件或块设备,它将有长度,如果是管道、套接字或 tty(或其他字符设备),则不会。但是如果另一个进程修改了它,那么在您读完之前长度可能会发生变化。
文件位置指向末尾的唯一其他方法是如果您在标志中使用 O_APPEND
打开。
看看系统调用 strace cat infloop.c
使用了什么(我已经 "commented out" 描述了那些与您使用固定大小的静态缓冲区无关的系统调用;如果你想要)
open("infloop.c", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0664, st_size=29, ...}) = 0 # get the size
# fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
# mmap(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff78a7eb000 # allocate a buffer
read(3, "\nint main()\n{\n while(1);\n}\n", 131072) = 29
write(1, "\nint main()\n{\n while(1);\n}\n", 29) = 29
read(3, "", 131072) = 0 # read returning 0 means EOF
# munmap(0x7ff78a7eb000, 139264) = 0 # free the buffer
close(3) = 0
我尝试在程序集中打开文件,将内存位置(地址)的值移动到 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
= 0x601058
(gdb) x/xg 0x601058
0x601058: 0x00007fffffffe16a
(gdb) x/s 0x7fffffffe16a
0x7fffffffe16a: "abc"
但它没有正确移动。我还读到 mov
有 32 位源操作数,但我需要将 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
开头(并且不以 d
或 b
结尾的寄存器) ) 您使用该寄存器中的所有 64 位。
指向内存的指针应该总是进入64位R
寄存器。
如果寄存器以 E
开头(或以 d
结尾),则它是一个 32 位寄存器(低 32 位)。
对 32 位寄存器的任何写操作都会将重叠的 64 位寄存器的前 32 位清零,例如mov eax,1
将 rax
设置为 1; mov eax,-1
将 rax
设置为 [=23=]00 0000 FFFF FFFF
;
mov eax,2
和 mov rax,2
做完全相同的事情。
除了 mov eax,2
短了两个字节,因此是首选。
如果你 read/write from/to 内存操作将遵循寄存器的大小,除非你另有指定。
如果将立即数写入内存,则必须指定大小。
您不能将 64 位立即数写入内存,您必须使用 2 条指令来执行此操作:mov R64,imm64 + mov [mem],R64
。
警告
对 16 位或 8 位寄存器的任何写入都不会将关联的 32/64 位寄存器的顶部清零!
结论
如果你想写汇编,你需要从头开始真正学习汇编。
因为你在 Linux 编程,所以我建议你买一本 Jeff Duntemann 的书:Assembly Language Step By Step, for Linux
杰夫是一个传奇人物,他比这个星球上的任何人都能更好地解释装配。
I determined length of the file but did not put seek position to beginning of the file
你读到最后找到长度了吗,把文件位置留在EOF?首先不要这样做,使用 fstat
查找您打开的文件的长度(如果有的话)。
如果是普通文件或块设备,它将有长度,如果是管道、套接字或 tty(或其他字符设备),则不会。但是如果另一个进程修改了它,那么在您读完之前长度可能会发生变化。
文件位置指向末尾的唯一其他方法是如果您在标志中使用 O_APPEND
打开。
看看系统调用 strace cat infloop.c
使用了什么(我已经 "commented out" 描述了那些与您使用固定大小的静态缓冲区无关的系统调用;如果你想要)
open("infloop.c", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0664, st_size=29, ...}) = 0 # get the size
# fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
# mmap(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ff78a7eb000 # allocate a buffer
read(3, "\nint main()\n{\n while(1);\n}\n", 131072) = 29
write(1, "\nint main()\n{\n while(1);\n}\n", 29) = 29
read(3, "", 131072) = 0 # read returning 0 means EOF
# munmap(0x7ff78a7eb000, 139264) = 0 # free the buffer
close(3) = 0