【发布时间】:2017-10-30 20:23:23
【问题描述】:
我正在学习 NASM 汇编器,目前我一直在处理换行符。
如何打印换行符?我会给你看。但是,在我展示它之前,重要的是要告诉你我的平台:
$ uname -a
Linux 4.4.0-97-generic #120-Ubuntu SMP Tue Sep 19 17:28:18 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
$ lsb_release -a
No LSB modules are available.
Distributor ID : Ubuntu
Description : Ubuntu 16.04.3 LTS
Release : 16.04
Codename : xenial
我为学习准备的功能:
sprint:
push rdx
push rbx
push rax
push rcx
mov rax, rcx
pop rcx
jmp next_char
ret
sprint_syscall:
mov rbx, 1
mov rax, 4
int 80h
pop rax
pop rbx
pop rdx
ret
sprint_linefeed:
call sprint
push rcx
mov rcx, 0Ah
push rcx
mov rcx, rsp
call sprint
pop rcx
pop rcx
ret
next_char:
cmp byte [rax], 0
jz count_length
inc rax
jmp next_char
count_length:
sub rax, rcx
mov rdx, rax
jmp sprint_syscall
quit:
push rbx
push rax
mov rbx, 0
mov rax, 1
int 80h
pop rax
pop rbx
ret
主应用程序,可能使用给定功能之一:
%include 'functions.asm'
SECTION .data
msgFirst db "Hello World!", 0h
msgSecond db "Another string.", 0h
SECTION .text
global _start
_start:
mov rcx, msgFirst
call sprint_linefeed
mov rcx, msgSecond
call sprint_linefeed
call quit
所有的编译都很好,也很好用。如果要编译/链接:
nasm -g -f elf64 -l main.lst main.asm -o main.o && ld main.o -o main
然后执行编译好的app,我们可以看到:
Hello World!Another string
如您所见,尽管调用了 sprint_linefeed 函数,但没有任何换行符,该函数使用下一个指令来打印换行符:
sprint_linefeed:
call sprint
push rcx ; push rcx onto the stack to preserve it
mov rcx, 0Ah ; move 0Ah into rcx, 0Ah is the ASCII char for a linefeed
push rcx ; push the linefeed char onto the stack
mov rcx, rsp ; move the address of the pointer into rcx for the 'sys_write' syscall
call sprint
pop rcx ; remove the linefeed char from the stack
pop rcx ; restore the original value of rcx
ret
所以,我的行为的解释是指sprint() 调用给定的字符串,然后将0Ah 字符压入堆栈。稍后,我从 RSP 寄存器中获取指针以再次调用 sprint() 函数。
已经完成,因为SYS_WRITE 系统调用需要指针来打印给定字符串 (http://fresh.flatassembler.net/lscr/data/004.html)。
如果要将OAh 字符添加到字符串声明中:
SECTION .data
msgFirst db "Hello World!", 0Ah, 0h
调用相同的函数,但使用不同的字符串值,会给我们等待的结果,但这没有意义,因为 sprint_linefeed() 被定义为传递冗余 ASCII 字符的声明。
我应该在我的源代码中修复什么以使sprint_linefeed() 正常工作?
【问题讨论】:
-
不要在64位代码中使用
int 80h,即32位兼容系统调用接口。见What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code? -
@Jester 谢谢你的建议,我会记住的。
-
rsp很可能超出 32b 边界,因此int 0x80将无法从堆栈内存中读取0A字符串。我建议你使用strace来查看系统调用返回什么,但实际上64b 二进制文件中的int 0x80甚至会混淆,所以输出没有那么有价值。要么将你的 asm 重写为 32b 并编译为 elf32,要么修复系统调用。 -
使用
strace和/或gdb进行调试,您会看到来自sys_write的-EFAULT返回值。 -
@PeterCordes 是的,我想准确地准备初学者教程。所以,稍后,我将在
CodeReview上创建一个帖子,并附上我的解释,并在准备好时给你一个链接。我会等待你的批评。之后,我将以友好的方式准备初学者教程并提供您提供的问题的答案(stackoverflow.com/a/46087731/224132)。
标签: linux assembly x86 nasm linefeed