ASM printf:如果字符串不包含换行符\ n,则没有输出

3

这段代码会在屏幕上输出“Hello”

.data
    hello: .string "Hello\n"
    format: .string "%s" 
.text
    .global _start 
    _start:

    push $hello
    push $format
    call printf

    movl $1, %eax   #exit
    movl $0, %ebx
    int $0x80

但是如果我从hello字符串中删除“\n”,就像这样:
.data
    hello: .string "Hello"
    format: .string "%s" 
.text
    .global _start 
    _start:

    push $hello
    push $format
    call printf

    movl $1, %eax   #exit
    movl $0, %ebx
    int $0x80

程序不能正常工作。有什么建议吗?

1
定义 "不工作"。 - Michael
1
它不会打印“Hello”。 - Dima
你确定它不会被你的shell提示符覆盖吗?如果你使用一个非常长的字符串(但仍然没有\n),会发生什么? - Michael
长字符串不起作用,我确信它什么也没有打印。我刚试过在室友的电脑上运行这两个变体,但都没有成功。 - Dima
@Michael。我正在使用gcc -nostartfiles 文件名命令编译代码。 - Dima
顺便说一下,我认为不需要格式化字符串... - Sam
2个回答

7
退出系统调用(相当于C语言中的_exit)不会刷新stdout缓冲区。
在行缓冲流上输出一个换行符会导致刷新,在指向终端的情况下,stdout将成为行缓冲。
如果你愿意在libc中调用printf,那么以同样的方式调用exit不应该让你感到不好。在程序中有一个int $0x80并不会使你成为裸机巨佬。
最少需要在退出之前push stdout并调用fflush。或者使用push $0;call fflush。(fflush(NULL)会刷新所有输出流)

或者使用 puts,或在你要打印的内容中包含一个换行符。虽然这只能解决终端而不是管道的问题,所以不要混合stdio和原始的_exit系统调用。在汇编中使用printf会导致管道输出为空,但在终端上可以正常工作 - Peter Cordes

3

您需要清理传递给printf的参数,然后刷新输出缓冲区,因为您的字符串中没有换行符:

.data
    hello: .string "Hello"
    format: .string "%s" 
.text
    .global _start 
    _start:

    push $hello
    push $format
    call printf
    addl $8, %esp
    pushl stdout
    call fflush
    addl $4, %esp
    movl $1, %eax   #exit
    movl $0, %ebx
    int $0x80

如果您最终要使用_exit(0)系统调用,则实际上不需要清除堆栈。 _start不是函数,因此您无处可返回。这里唯一重要的是在原始_exit系统调用之前使用fflush(NULL)fflush(stdout)(而不是call exit)。公平地说,如果想编写函数,了解堆栈的工作原理很重要。因此,请注意,您正在将堆栈参数保留为未弹出状态,而不是进行另一个推送,您可以使用movl $ 0,(%esp) / call fflush来重复使用为printf推送参数而保留的堆栈插槽之一。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接