2016-07-14 100 views
2

我尝试使用printf从我的汇编代码,这是一个小例子,这应该只是打印hello到stdout:装配用printf会导致一个空的输出中

.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

使用系统调用退出不会刷新_C_运行时使用的输出缓冲区。使用'call exit'替换exit syscall(退出也是_C_库的一部分) –

+0

如果您查看[手册页](http://man7.org/linux/man-pages/man3/exit.3 .html)退出(退出(3)),你会发现这在描述中_所有打开的stdio(3)流被刷新并关闭。由tmpfile(3)创建的文件被删除._。当使用'movq $ 60,%rax movq $ 0,%rdi 系统调用' –

回答

3

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

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

这是发生了什么事:作为解释here,在C-库缓冲标准流如下:

  1. 标准错误无缓冲。
  2. 如果标准out/in是一个终端,它是行缓冲的。
  3. 如果标准输出/输入不是终端,则它是完全缓冲的,因此在写入结束时需要刷新。

哪一个情况适用是在首次调用流的时候决定的printf

所以如果printf_try在终端直接调用,该程序的输出可以看出,因为hello在端部\n(这触发了行缓冲模式冲洗),并且它是一个终端,还2- 。 案件。

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

+0

感谢您为退出与_exit和stdio缓冲问题写了一个很好的规范答案。添加到[x86标签维基FAQ部分](http://stackoverflow.com/tags/x86/info),所以我们可以很容易地找到它,并关闭未来的问题作为重复的。 –

2

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

.globl main 
main: 
    # _start code here. 

,然后用gcc try_printf.s -o try_printf建立(即,而不-nostdlib)。

+0

时,这是不能保证的。谢谢你的建议,但是我想在没有C运行库的情况下使用'printf',并认为它应该是可能的。但是你是对的,我的代码似乎错过了一些通常由C运行时完成的初始化/终结。 – ead

+0

@ead不幸的是,这是一种给定的。无论如何,您正在与C标准库链接,这是运行时大小的主要部分。拍一下,看看它是怎么回事。 (你可以直接使用'write'系统调用!) – refi64

+0

你不能使用'printf'(或者其他C运行时函数)而不链接C运行时。即使你可以,这显然是一个坏主意。解决这个问题的唯一方法就是调用操作系统API函数(它们只是将OS运行时功能封装到OS中)。 –

相关问题