linux 在汇编中使用printf会导致管道化时输出为空,但在终端上有效

vsdwdz23  于 2023-08-03  发布在  Linux
关注(0)|答案(2)|浏览(99)

No output即使在输出不包括换行符的终端上也有同样的原因。
我试着在我的汇编代码中使用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有什么问题?

nimxete2

nimxete21#

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

(Or如果您的程序定义了main而不是_start,则从main返回等效于调用exit。您不能从_start调用ret。)调用fflush(NULL)应该也可以。
正如Michael所解释的,动态链接C库是可以的。这也是"Programming bottom up"一书中介绍它的方式(请参见第8章)。
然而,从C库调用exit是很重要的,这样才能结束程序,而不是绕过它,这是我调用exit-syscall的错误之处。正如Michael所暗示的那样,exit执行了很多clean up类似于刷新流的操作。
事情的经过就是这样:如here所述,C库缓冲标准流,如下所示:
1.没有标准错误的缓冲区。
1.如果标准输出/输入是终端,则它是线路缓冲的。
1.如果标准输出/输入不是终端,则它是完全缓冲的,因此在原始出口系统调用之前需要刷新。
当第一次为数据流呼叫printf时,便会决定适用哪种情况。
因此,如果在终端中直接调用printf_try,则可以看到程序的输出,因为hello的末尾有\n(在行缓冲模式下触发刷新),并且它是一个终端,也是2。的情况下。
通过$(./printf_try)调用printf_try意味着stdout不再是终端(实际上我不知道它是临时文件还是内存文件),因此3. case生效-需要显式刷新,即调用C-exit

pzfprimi

pzfprimi2#

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

.globl main
main:
    # _start code here.

字符串
然后使用gcc try_printf.s -o try_printf构建(* 即 *,不使用-nostdlib)。

相关问题