在装配中使用 printf 会导致管道输出为空,但在终端上有效

ead*_*ead 4 linux assembly printf glibc x86-64

我尝试printf从我的汇编代码中使用,这是一个应该打印hello到标准输出的最小示例:

.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
Run Code Online (Sandbox Code Playgroud)

我用

gcc -nostdlib try_printf.s -o try_printf -lc
Run Code Online (Sandbox Code Playgroud)

当我运行它时,它似乎工作:字符串hello被打印出来,退出状态是0

XXX$ ./try_printf
hello
XXX$ echo $?
0
XXX$
Run Code Online (Sandbox Code Playgroud)

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

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

XXX$ 
Run Code Online (Sandbox Code Playgroud)

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

我的用法有printf什么问题?

ead*_*ead 5

在使用 stdio 函数(如 printf)后使用call exit而不是原始_exit系统调用。 这会write在进行exit_group系统调用之前刷新 stdio 缓冲区(系统调用)。

(或者,如果您的程序定义了一个main而不是_start,则返回 frommain等效于调用exit。您不能ret_start。)调用fflush(NULL)也应该有效。


正如迈克尔解释的那样,动态链接 C 库是可以的。这也是“自下而上编程”一书中的介绍(见第 8 章)。

然而,重要的是exit从 C 库调用以结束程序而不是绕过它,这是我错误地调用exit-syscall. 正如迈克尔所暗示的那样,退出会像冲洗流一样进行大量清理

这就是发生的事情:正如这里所解释的,C 库按如下方式缓冲标准流:

  1. 没有缓冲标准错误。
  2. 如果标准输出/输入是终端,则它是行缓冲的。
  3. 如果标准输出/输入不是终端,则它是完全缓冲的,因此在原始退出系统调用之前需要刷新。

哪种情况适用取决于printf第一次为流调用时。

因此,如果printf_try在终端直接调用,该程序的输出可以看出,因为hello具有\n在端部(其触发在行缓冲模式冲洗),并且它是一个终端,也2.情况。

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