我尝试从汇编代码中使用printf,这是一个最小的示例,应仅将hello打印到stdout:

.section  .rodata
hello:
    .ascii  "hello\n\0"
.section .text
    .globl _start
_start:
    movq $hello, %rdi #first parameter
    xorl %eax, %eax #0 - number of used vector registers
    call printf
#exit
    movq $60, %rax
    movq $0, %rdi
    syscall


我用

gcc -nostdlib try_printf.s -o try_printf -lc


当我运行它时,它似乎可以正常工作:打印出字符串hello,退出状态为0

XXX$ ./try_printf
hello
XXX$ echo $?
0
XXX$


但是,当我尝试捕获文本时,很明显,某些功能无法正常工作:

XXX$ output=$(./try_printf)
XXX$ echo $output

XXX$


变量output应具有值hello,但为空。

我使用printf有什么问题?

最佳答案

在使用诸如printf之类的stdio函数之后,使用call exit代替原始的_exit syscall。



正如Michael解释的那样,可以动态链接C库。这也是"Programming bottom up"书中介绍它的方式(请参见第8章)。

但是,从C库中调用exit以便结束程序而不是绕过程序很重要,这是我错误地通过调用exit-syscall所做的。正如Michael所暗示的,exit像冲洗流一样执行很多clean up

这就是发生的情况:如here所述,C库按以下方式缓冲标准流:


不缓冲标准错误。
如果标准输出/输入是终端,则它是行缓冲的。
如果标准输出/输入不是终端,则它是完全缓冲的,因此在写入结束时需要刷新。


哪种情况适用于何时首次为流调用printf的情况。

因此,如果直接在终端中调用printf_try,则可以看到程序的输出,因为hello在末尾有\n(这会在行缓冲模式下触发刷新),并且它也是一个终端。 2.案例。

通过printf_try调用$(./printf_try)意味着stdout不再是终端(实际上我不知道它是临时文件还是内存文件),因此3.情况有效-需要一个显式刷新,即调用C- exit

关于linux - 在汇编中使用printf导致输出为空,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56247270/

10-13 07:20