【问题标题】:Can't print a linefeed that was PUSHed on the stack in NASM [duplicate]无法打印在 NASM 中被推送到堆栈上的换行符 [重复]
【发布时间】: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


【解决方案1】:

@Neverlands 我通过删除sprint_linefeed 对您的functions.asm 做了一个小改动,并将以下内容放在文件顶部:

%define newline 0Ah

SECTION .data
    lineEnding db newline, 0h

sprint_linefeed:
    call sprint
    mov  rcx, lineEnding
    call sprint
    ret

它可能不是你想要的,但它确实提供了:

Hello World!
Another string.

作为输出。玩得开心!

【讨论】:

  • 遗憾,但这不是我想要的。我想准确处理内部函数中的换行符,而不是在字符串声明中定义换行符。您提供的方式已经在我的问题内容末尾提到(只是没有%define)。
  • 但是再仔细观察一下,我想你会明白的。注意 lineEnding 和你的 msgFirst 和 msgSecond 以 NUL 结尾
  • @KenSchumack mov rcx,0Ah push rcx 确实在堆栈内存中设置了 8 个字节 0A 00 00 00 00 00 00 00,因此它已经 以 nul 终止了。 OP 的问题是,对于他在堆栈中的换行符字符串,您必须非常横向地查看,例如超出 32b 内存边界,而int 0x80 将永远无法做到这一点。您在.data 中的字符串是可访问的,这就是为什么即使使用错误的系统调用它也能正常工作的原因。
猜你喜欢
  • 1970-01-01
  • 2013-08-13
  • 2020-03-29
  • 1970-01-01
  • 2021-02-06
  • 2013-10-12
  • 2017-10-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多