gpt4 book ai didi

linux - 换行字节 0Ah 被 x86_64 系统调用打印程序忽略

转载 作者:太空狗 更新时间:2023-10-29 12:22:41 25 4
gpt4 key购买 nike

我按照一个简单的教程创建了一个 NASM x86_64 程序,该程序使用定义的函数打印变量,并在末尾添加了一个新行。 sprintLF 调用 sprint ,它反过来打印 rax 中设置了适当系统调用的任何内容。返回时,sprintLF 用 0Ah 更新 rax 换行代码,然后将换行代码推送到堆栈,并在再次调用 sprint 之前将 rax 重新分配给 0Ah 的堆栈地址,并将换行代码写入标准输出。在我在 gdb 中调试 sprint 的所有代码下面,这表明所有正确的寄存器都存储了与系统调用 4 关联的值,我不知道为什么变量字符串被成功打印但换行符没有。

调用代码

 ;; 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.

在另一个程序中,我尝试在将参数放入堆栈后打印参数,所以我只能猜测系统调用不喜欢从堆栈中获取它的值,但我是汇编新手,这让我感到困惑.

最佳答案

您一直在尝试将使用 int0x80 的 32 位 Linux 代码转换为 64 位代码。虽然这适用于很多情况,但并不适用于所有情况。 int 0x80 是 32 位系统调用接口(interface),但由于 IA32 兼容性内置于 Linux 内核(大多数发行版的默认设置),您仍然可以使用 int 0x80。问题在于,当内核处理您的 int 0x80 请求时,寄存器的低 32 位被识别。

您第一个问题中的代码没有出现任何问题,但这段代码不起作用。原因是 RSP 中的堆栈指针通常是一个不能用 32 位值寻址的地址。当你执行 mov rax,rsp 时,RSP 的完整 64 位值被移动到 RAX,但是 sprintint 0x80 调用只会查看 RAX(EAX 寄存器)的底部 32 位。

解决这个问题的方法是使用 64 位 syscall 接口(interface)。不幸的是,传入的系统调用号和寄存器参数已经改变。 Ryan Chapman's blog有一个很好的 64 位 syscall 系统调用编号及其参数表。

表中的sys_write系统调用号和参数为:

enter image description here

根据此信息,您可以通过执行以下操作将 sprint 转换为使用 syscall 接口(interface):

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

这是相当低效的,可以清理。我以这种方式呈现,以便您可以从原始代码的角度理解更改。我已经评论了相关的行。

注意:您真的应该使用 Ryan Chapman 的表作为指南将所有 int 0x80 调用转换为 syscall。我将其留作 OP 的练习。

关于linux - 换行字节 0Ah 被 x86_64 系统调用打印程序忽略,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55128338/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com