在创建NASM x86_64程序时,我遵循了一个简单的教程,该程序使用定义的函数来打印变量,并在末尾添加新行。 sprintLF调用sprint,依次设置适当的系统调用,然后打印rax中的所有内容。在返回时,sprintLF用0Ah更新rax的换行代码,然后将其推入堆栈,并将rax重新分配给0Ah的堆栈地址,然后再次调用sprint并将换行代码写入标准输出。在所有代码下面,我已在gdb中调试了sprint,该操作表明所有正确的寄存器都存储了与系统调用4相关的值,而我困惑于为什么成功打印变量字符串而不能换行的原因。 / p>
呼叫代码
;; Hello World Program (Externam file include)
;; Compile with: nasm -f elf64 helloworld-if.asm
;; Link with ld helloworld-if.o -o helloworld-if
;; Run with ./helloworld-inc
%include 'function.asm' ; include our external file
SECTION .data
msg1 db 'Hello, brave new world!', 0h ;our first message string add null terminating byte
msg2 db 'This is how we recycle in NASM.', 0h ; our second message string add null terminating byte
SECTION .text
global _start
_start:
mov rax, msg1 ; mov the address of our first message string into RAX
call sprintLF ; call our string printing function
mov rax, msg2 ; move the address of our second message string into RAX
call sprintLF ; call our string printing function
call quit ; call our quit function
实用功能
; -------------------------------------------------------------------------------------------------------------------
; int slen(String message)
; String length calculation function
slen: ; this is our first function declaration
push rbx ; push the value in RBX onto the stack to preserve it while we use RBX in this function
mov rbx, rax ; move this address in RAX into RBX ( Both point to the same segment in memory)
nextchar:
cmp byte [rax], 0 ; this is the same as lesson 3
jz finished
inc rax
jmp nextchar
finished:
sub rax, rbx
pop rbx ; pop the value on the stack back into RBX
ret ; return to where the function was called
;; ---------------------------------------------------------------------------------------------------------
;; void sprint(String message)
;; String printing function
sprint:
push rdx
push rcx
push rbx
push rax
call slen
mov rdx, rax
pop rax
mov rcx, rax
mov rbx, 1
mov rax, 4
int 80h
pop rbx
pop rcx
pop rdx
ret
;; ----------------------------------------------------------------------------------------------------------
;; void sprintLF(String message)
;; String printing with line feed function
sprintLF:
call sprint
push rax ; push rax onto the stack to preserve it while we use the rax register in this function
mov rax, 0Ah ; push 0Ah into rax, 0Ah is the ascii character for a linefeed
push rax ; push the linefeede onto the stack so we can get the address
mov rax, rsp ; move the address of the current stack pointer into rax for sprint -> because write requires a memory address
call sprint ; call our sprint function
pop rax ; restore out linefeed character from the stack
pop rax ; return to our program
ret
;; -----------------------------------------------------------------------------------------------------------
;; void exit()
;; Exit program restore resources
quit:
mov rbx, 0
mov rax, 1
int 80h
ret
用于执行代码和输出的命令如下:
nasm -f elf64 helloworld-if.asm
ld helloworld-if.o -o hellworld-if
./hellworld-if
Hello, brave new world!This is how we recycle in NASM.
在另一个我尝试将参数放入堆栈后打印参数的程序中,会发生相同的情况,因此我只能猜测系统调用不喜欢从堆栈中获取其值,但是我是汇编新手,这使我感到困惑
答案 0 :(得分:1)
您一直在尝试将使用int0x80
的32位Linux代码转换为64位代码。尽管这在很多情况下都可以使用,但并不能在所有情况下都适用。 int 0x80
是32位系统调用接口,但由于Linux内核内置了IA32兼容性(大多数发行版的默认设置),您仍然可以使用int 0x80
。问题在于,内核处理您的int 0x80
请求时,只能仅识别寄存器的低32位。
第一个问题中的代码没有任何问题,但是此代码不起作用。原因是RSP中的堆栈指针通常是一个不能用32位值寻址的地址。当您执行mov rax,rsp
时,RSP的完整64位值将移至RAX,但是sprint
的{{1}}调用将仅看到RAX的低32位(EAX寄存器)
解决方法是使用64位int 0x80
接口。不幸的是,传递的系统调用号和寄存器参数已更改。 Ryan Chapman's blog有一个很好的表格,其中包含64位syscall
的系统调用号及其参数。
表中的syscall
系统调用号码和参数是:
根据此信息,您可以通过执行以下操作将sys_write
转换为使用sprint
界面:
syscall
这效率很低,可以清除。我以这种方式呈现它,以便您可以从原始代码的角度理解更改。我已经评论了相关内容。
注意:您应该真正使用Ryan Chapman的表格将所有sprint:
push r11 ; R11 and RCX are clobbered by syscall as well
push rcx
push rdx
push rsi
push rdi
push rax
call slen
mov rdx, rax ; RDX = number of characters to print
pop rax
mov rsi, rax ; RSI = address of characters to print
mov rdi, 1 ; RDI = file descriptor (1=STDOUT)
mov rax, 1 ; System call number 1 = sys_write
syscall ; 64-bit system call (rather than int 0x80)
pop rdi
pop rsi
pop rdx
pop rcx
pop r11
ret
调用转换为int 0x80
。我将其保留为OP的练习。