在 64 位 Linux 上为 x86_64 编写 putchar?

问题描述 投票:0回答:1

我正在尝试使用 write

syscall
来重现打印单个字符的
putchar
函数行为。我的代码如下,

asm_putchar:
  push    rbp
  mov     rbp, rsp

  mov     r8, rdi

call:
  mov     rax, 1
  mov     rdi, 1
  mov     rsi, r8
  mov     rdx, 1
  syscall

return:
  mov     rsp, rbp
  pop     rbp
  ret
linux assembly nasm x86-64 system-calls
1个回答
6
投票

man 2 write
,可以看到
write
的签名是,

ssize_t write(int fd, const void *buf, size_t count);

它需要一个指向内存中缓冲区的指针(

const void *buf
)。您无法按值传递
char
,因此您必须将其存储到内存中并传递指针。

(不要一次打印一个字符,除非你只有一个字符要打印,这真的效率很低,这就是为什么 C stdio 正常缓冲 I/O 的原因。在内存中构造一个缓冲区并打印它:例如这个 x86-64 Linux NASM 函数:

如何在汇编级编程中打印整数而不使用 c 库中的 printf?(itoa,整数到十进制 ASCII 字符串)

GCC 的 NASM 版本:在内联汇编中放置char(char),针对代码大小/效率进行了一些调整。 write

如果您
do

在RSI中传递无效指针,例如; x86-64 System V calling convention: input = byte in DIL ; clobbers: RDI, RSI, RDX, RCX, R11 (last 2 by syscall itself) ; returns: RAX = write return value: 1 for success, -1..-4095 for error writechar: lea rsi, [rsp-4] ; RSI = buf in the red zone (below RSP) mov [rsi], edi ; store the char from RDI into it mov eax, 1 ; __NR_write syscall number from unistd_64.h mov edi, 1 ; EDI = fd=1 = stdout ; RSI set earlier, before overwriting EDI mov edx, eax ; RDX = len = 1 happens to be the same as fd and call # syscall ; rax = write(1, buf, 1) ret (整数

'2'
),系统调用将在RAX中返回
50
-EFAULT
)。 (内核会在系统调用的错误指针上返回错误代码,而不是像在用户空间中取消引用时那样传递 SIGSEGV)。
另请参阅

Assembly 中系统调用的返回值是什么?

在玩具程序/实验中,您应该在

-14

下运行它们,而不是编写代码来检查返回值。如果您在不使用 libc 的情况下编写自己的

strace ./a.out
,则在启动期间不会有任何其他系统调用,因此很容易读取输出,否则会进行一堆启动系统调用在你的代码之前由 libc 编写。
strace应该如何使用?

© www.soinside.com 2019 - 2024. All rights reserved.