【问题标题】:Can't call `putchar` in assembly [duplicate]无法在程序集中调用“putchar”[重复]
【发布时间】:2019-03-24 18:31:12
【问题描述】:

我正在尝试使用 C 标准库中的 putchar 函数。看起来很简单——你传入一个 int,它将字符写入 stdout。但是,当我编译并运行这个程序时,什么都没有发生。

    # This doesn't work:
    .section .text
    .globl _start

_start:
    mov $0x41, %dil
    call putchar

    mov $60, %rax
    syscall

【问题讨论】:

  • 这是因为您的程序既没有正确初始化 libc 也没有正确取消初始化它,因此没有在退出时刷新缓冲区。要解决此问题,请从main(而不是_start)启动您的程序,通过C 编译器链接并通过从main 返回或调用exit 来终止您的程序。
  • @fuz 啊,好的。因此,将系统调用中的现有行为更改为call exit 似乎使它起作用了。为什么会发生这种情况?
  • 这是因为 libc 确实缓冲了 I/O。退出时,输出缓冲区由函数exit 刷新。如果您不调用exit 或手动刷新输出缓冲区,它们将不会被刷新。也就是说,仅仅调用exit 是不够的!除非您还正确初始化了 libc(通过让您的程序从 main 开始并让 C 编译器提供 _start 来初始化 libc),否则如果您使用任何 libc 函数,可能会发生奇怪的事情。如果你使用 libc,你应该尽量不要直接调用任何系统调用。请改用 libc 函数!
  • 另一方面,如果你确实放弃使用putchar,你可以使用write,它不需要任何初始化
  • @AnttiHaapala wait is write 只是 sys_write 系统调用的包装器?

标签: c assembly x86


【解决方案1】:

您看不到任何输出,因为 libc 的 IO 包执行缓冲 IO,并且您在终止程序之前没有刷新 stdout 的缓冲区。这个问题是一个更大的潜在问题的症状,那就是您没有正确初始化和取消初始化 libc。在每个使用 libc 的程序中,您应该:

  1. 让 libc 在启动时自行初始化,使其初始化代码为 _start 并调用您的 main 函数
  2. 不通过syscall指令执行原始系统调用,而是使用libc提供的包装器
  3. 通过调用exit或从main返回正确终止程序
  4. 使用 C 编译器驱动程序cc 链接您的程序,而不是直接调用链接编辑器,从而正确链接所有 libc 代码

一旦您更有经验,您可能会偏离这些建议,但这样做可能会产生您可能没有意识到的微妙后果,因此现在最好遵循这些规则。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-06
    • 1970-01-01
    • 2011-07-10
    • 2017-12-31
    • 2012-11-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多