问题描述
我尝试从我的汇编代码中使用 printf
,这是一个最小的示例,应该只将 hello
打印到stdout:
I try to use printf
from my assembler code, this is a minimal example which should just print hello
to 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
:
and when I run it, it seems to work: the string hello
is printed out and the exit status is 0
:
XXX$ ./try_printf
hello
XXX$ echo $?
0
XXX$
但是当我尝试捕获文本时,很明显,某些功能无法正常工作:
But when I try to capture the text, it is obvious, that something is not working properly:
XXX$ output=$(./try_printf)
XXX$ echo $output
XXX$
变量 output
的值应为 hello
,但为空.
The variable output
should have the value hello
, but is empty.
我使用 printf
有什么问题?
推荐答案
在使用诸如printf之类的stdio函数之后,使用 call exit
代替原始的 _exit
syscall.这会在进行 exit_group
系统调用之前刷新stdio缓冲区( write
系统调用).
Use call exit
instead of a raw _exit
syscall after using stdio functions like printf. This flushes stdio buffers (write
system call) before making an exit_group
system call).
(或者,如果您的程序定义了 main
而不是 _start
,则从 main
返回等同于调用 exit
.您不能从 _start
中 ret
.)调用 fflush(NULL)
也应该有效.
(Or if your program defines a main
instead of _start
, returning from main
is equivalent to calling exit
. You can't ret
from _start
.) Calling fflush(NULL)
should also work.
正如Michael所解释的,可以动态链接C库.这也是自下而上编程" 书(请参阅第8章).
As Michael explained, it is OK to link the C-library dynamically. This is also how it is introduced in the "Programming bottom up" book (see chapter 8).
但是从C库调用 exit
以便结束程序而不是绕过程序是很重要的,这是我错误地通过调用 exit-syscall .正如Michael所暗示的,exit会做很多清理像冲洗溪流.
However it is important to call
exit
from the C-library in order to end the program and not to bypass it, which was what I wrongly did by calling exit-syscall
. As hinted by Michael, exit does a lot of clean up like flushing streams.
发生的事情:如此处,C库将按以下方式缓冲标准流:
That is what happened: As explained here, the C-library buffers the the standard streams as follows:
不缓冲标准错误.
如果标准输出/输入是终端,则它是行缓冲的.
如果标准输出/输入不是终端,则它是完全缓冲的,因此需要在原始出口系统调用之前进行刷新.
哪种情况取决于第一次为流调用
printf
的情况.
Which case applies is decided when
printf
is called for the first time for a stream.
因此,如果直接在终端中调用
printf_try
,则可以看到程序的输出,因为 hello
的末尾有 \ n
(在行缓冲模式下触发刷新),它是一个终端,也是2. case.
So if
printf_try
is called directly in the terminal, the output of the program can be seen because hello
has \n
at the end (which triggers the flush in the line-buffered mode) and it is a terminal, also the 2. case.
通过
$(./printf_try)
调用 printf_try
意味着stdout不再是终端(实际上我不知道这是临时文件还是内存文件),因此3.情况有效-需要进行显式刷新,即调用C- exit
.
Calling
printf_try
via $(./printf_try)
means that the stdout is no longer a terminal (actually I don't know whether is is a temp file or a memory file) and thus the 3. case is in effect - there is need for an explicit flush i.e. call to C-exit
.
这篇关于在组装中使用printf会导致管道连接时输出为空,但可在终端上使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!