在装配中使用printf会导致空输出

时间:2016-07-14 16:30:14

标签: linux assembly printf x86-64 glibc

我尝试使用汇编程序代码中的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的使用有什么问题?

2 个答案:

答案 0 :(得分:3)

使用像printf这样的stdio函数后,使用call exit代替原始_exit系统调用。

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

然而,重要的是从C库中调用exit以结束程序而不是绕过它,这是我通过调用exit-syscall错误地做到的。正如迈克尔所暗示的那样,退出很多clean up就像冲洗流一样。

发生了什么:正如here所解释的那样,C库按如下方式缓冲标准流:

  1. 没有缓冲标准错误。
  2. 如果标准输出/输入是终端,则它是行缓冲的。
  3. 如果标准输出/输入不是终端,则它是完全缓冲的,因此在写入结束时需要刷新。
  4. 当第一次为流调用printf时,确定适用的情况。

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

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

答案 1 :(得分:2)

C标准库通常包含标准I / O流的初始化代码 - 您通过定义自己的入口点而绕过的初始化代码。尝试定义main而不是_start

    .globl main
main:
    # _start code here.

然后使用gcc try_printf.s -o try_printf即。,不使用-nostdlib)进行构建。