[英]Newline byte 0Ah being ignored by x86_64 system call print program
I followed a simple tutorial in the creation of a NASM x86_64 program which uses the defined functions to print variables with a new line added to the end.我在创建 NASM x86_64 程序时遵循了一个简单的教程,该程序使用定义的函数来打印变量,并在末尾添加新行。 sprintLF calls sprint which in turn prints whatever is in rax having set up the appropriate system call.
sprintLF 调用 sprint,它依次打印 rax 中设置了适当系统调用的任何内容。 On return sprintLF updates rax with 0Ah the newline code which it then pushes to the stack and reassigns rax to the stack address of 0Ah before calling sprint again with the newline code to be written to stdout.
返回时 sprintLF 用 0Ah 更新 rax 换行代码,然后将其压入堆栈并将 rax 重新分配给 0Ah 的堆栈地址,然后再次调用 sprint 并将换行代码写入 stdout。 Below all of the code I have debugged sprint in gdb which shows that all the correct registers have the values associated with system call 4 stored and I am at a loss as to why the variable strings are successfully being printed but the newlines are not.
在我在 gdb 中调试 sprint 的所有代码下面,这表明所有正确的寄存器都存储了与系统调用 4 关联的值,我不知道为什么成功打印了变量字符串但换行符却没有。
THE CALLING CODE呼叫代码
;; 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
UTILITY FUNCTIONS实用功能
; -------------------------------------------------------------------------------------------------------------------
; 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
The commands used to execute the code and the output follow:用于执行代码和输出的命令如下:
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.
In another program where I attempt to print arguments after putting them into the stack the same occurs so I can only guess the system call doesn't like taking its value from the stack but I am new to assembly and this is confusing me.在另一个程序中,我尝试在将参数放入堆栈后打印参数,同样发生了同样的情况,所以我只能猜测系统调用不喜欢从堆栈中获取它的值,但我是汇编的新手,这让我感到困惑。
You have been trying to convert 32-bit Linux code that uses int0x80
to 64-bit code.您一直在尝试将使用
int0x80
32 位 Linux 代码转换为 64 位代码。 Although this can work for a lot of cases it doesn't work for everything.虽然这可以在很多情况下工作,但它并不适用于所有情况。
int 0x80
is the 32-bit system call interface, but with IA32 compatibility built into the Linux kernel (the default for most distros) you are allowed to still use int 0x80
. int 0x80
是 32 位系统调用接口,但是由于 Linux 内核中内置的 IA32 兼容性(大多数发行版的默认设置),您仍然可以使用int 0x80
。 The catch is that only the lower 32-bits of the registers are recognized when the kernel processes your int 0x80
request.问题在于,当内核处理您的
int 0x80
请求时,只能识别寄存器的低 32 位。
The code in your first question didn't exhibit any problems, but this code doesn't work.您第一个问题中的代码没有出现任何问题,但此代码不起作用。 The reason is that the stack pointer in RSP is usually an address that can't be addressed with with a 32-bit value.
原因是 RSP 中的堆栈指针通常是无法用 32 位值寻址的地址。 When you do
mov rax,rsp
the full 64-bit value of RSP is moved to RAX, but sprint
's int 0x80
call will only see the bottom 32-bits of RAX (the EAX register).当您执行
mov rax,rsp
,RSP 的完整 64 位值将移至 RAX,但sprint
的int 0x80
调用只会看到 RAX 的底部 32 位(EAX 寄存器)。
The way around this is to use the 64-bit syscall
interface.解决这个问题的方法是使用 64 位
syscall
接口。 Unfortunately, the system call numbers and the registers parameters are passed in have changed.不幸的是,传入的系统调用号和寄存器参数发生了变化。 Ryan Chapman's blog has a nice table of the 64-bit
syscall
system call numbers and their parameters. Ryan Chapman 的博客有一个很好的 64 位
syscall
系统调用号及其参数表。
The sys_write
system call number and parameters from the table are:表中的
sys_write
系统调用号和参数是:
Based on this information you can transform sprint
to use syscall
interface by doing this:根据此信息,您可以通过执行以下操作将
sprint
转换为使用syscall
接口:
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
This is rather inefficient and it can be cleaned up.这是相当低效的,它可以被清理。 I present it this way so you you can understand the changes from the perspective of your original code.
我以这种方式呈现它,以便您可以从原始代码的角度理解更改。 I have commented the lines of relevance.
我已经评论了相关的行。
Note : You should really convert all the int 0x80
calls to syscall
using Ryan Chapman's table as a guide.注意:您真的应该使用 Ryan Chapman 的表作为指南将所有
int 0x80
调用转换为syscall
。 I leave that as an exercise for the OP.我把它作为 OP 的练习。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.