Assembly 来自程序集的Printf调用不打印到标准输出

Assembly 来自程序集的Printf调用不打印到标准输出,assembly,Assembly,我正在x86_64 Linux上的NASM中构建并运行代码 -rw-rw-r-- 1 me me 0 output.txt 程序从我的程序调用GNU libcprintf函数 程序只需将一个句子打印到标准输出 ; comment section .data fmt: db "Hello %s %c", 0 name: db "Jane Doe", 0 section .text global _start extern printf func: lea rdi, [fmt] lea

我正在x86_64 Linux上的NASM中构建并运行代码

-rw-rw-r-- 1 me me    0 output.txt
程序从我的程序调用GNU libc
printf
函数

程序只需将一个句子打印到标准输出

; comment
section .data
fmt: db "Hello %s %c", 0
name: db "Jane Doe", 0

section .text
global _start
extern printf

func:
lea rdi, [fmt]
lea rsi, [name]
mov rcx, 0x0A
xor rax, rax
call printf
ret

_start:
call func
; exit
mov rax, 1
mov rbx, 0
int 80h
ret
以下是我编译它的方法:

nasm -f elf64 Program.s -o Program.o -Werror 
ld -m elf_x86_64 Program.o -o Program -lc -dynamic-linker /lib64/ld-linux-x86-64.so.2
当我运行程序时,它将输出到终端
Hello Jane Doe
。好的,这就是我所期待的

但当我重定向出以下内容时:

./Program > output.txt
文件
output.txt

-rw-rw-r-- 1 me me    0 output.txt
有什么想法吗?在这种情况下,libc
printf
似乎正在打印到另一个文件描述符,而不是
stdout
,但可能我错了

解决方案 用户已在注释中找到解决方案

转换

; exit
mov rax, 1
mov rbx, 0
int 80h

通过
调用exit
成功了。

如果你想使用C库函数,你不应该使用
\u start
,而应该使用好的
main
。如果从libc偷取
\u start
,它将无法执行多个设置和清理操作,例如刷新打开的C文件

这种情况下,只有在写入文件时才可见,因为写入tty标准输出时,默认情况下是行缓冲的,所以会立即刷新。相反,当重定向到文件时,数据只是复制到一个临时缓冲区中,当数据足够满时或在终止时刷新数据-但在您的情况下,从未调用过C运行时清理函数,因此数据从未真正传递到操作系统


另一种可能是使用
exit
libc函数退出(
extern exit
在执行开始时,调用exit在执行结束时),该函数应处理清理,但是我不确定libc中的东西是否需要正常工作,如果它没有得到初始化的机会,那么在Linux上应该是安全的,请参阅@PeterCordes comment并感谢@Kirill_Zaitsev的尝试。

如果你想使用C库函数,你不应该使用
\u start
,而应该使用好的
main
。如果从libc偷取
\u start
,它将无法执行多个设置和清理操作,例如刷新打开的C文件

这种情况下,只有在写入文件时才可见,因为写入tty标准输出时,默认情况下是行缓冲的,所以会立即刷新。相反,当重定向到文件时,数据只是复制到一个临时缓冲区中,当数据足够满时或在终止时刷新数据-但在您的情况下,从未调用过C运行时清理函数,因此数据从未真正传递到操作系统


另一种可能是使用
exit
libc函数退出(
extern exit
在执行开始时,调用exit在执行结束时),该函数应处理清理,但我不确定libc中的东西是否需要正常工作,如果它没有机会初始化,那么它在Linux上应该是安全的,请参阅@PeterCordes comment并感谢@Kirill_Zaitsev的尝试。

如果printf检测到数据正在写入文件,可能是数据没有被printf刷新的问题吗?尝试调用exit而不是
int80h
(它不调用由
atexit
注册的东西)半相关(这里不是问题):32位
int0x80
ABI不适用于需要64位指针的系统调用。使用64位
syscall
ABI。对于
sys\u exit
来说,它是完全安全的,除非您使用的内核没有配置IA32\u仿真或Windows Subsystem for Linux。如果printf检测到数据正在写入文件,可能是数据没有被printf刷新的问题吗?尝试调用exit而不是
int80h
(它不调用由
atexit
注册的东西)半相关(这里不是问题):32位
int0x80
ABI不适用于需要64位指针的系统调用。使用64位
syscall
ABI。
sys\u exit
非常安全,除非您使用的内核没有配置IA32\u仿真,或者Windows Subsystem for Linux.glibc启动代码在Linux上动态链接的可执行文件中运行(使用
.init
挂钩);这不是问题所在。(通常不建议从一开始就使用libc,但据我所知,它在动态Linux可执行文件中实际上是完全安全的。它在Cygwin上不起作用,这可能是一些人反对它的原因?)。只有在stdio被完全缓冲时才退出而不刷新它才是问题所在。@PeterCordes@MatteoItalia有没有一种方法可以使用好的旧main而不使用
gcc
编译,而只使用
ld
?@Alrick:是的,如果你将所有相同的选项传递给
ld
,gcc会这样做,包括定义
\u start
.o
文件。或者编写自己的
\u start
调用
main
,然后将返回值传递给libc
exit(3)
。(例如,Matteo发布了一个真正最小的
\u start
,它使用sys\u exit,您可以将其修改为
调用exit
)但是IDK为什么您会这样做,只需使用gcc的前端在CRT启动代码中运行
as
ld
和链接。@Alrick不可能以可移植的方式运行。尽量避免直接使用
ld
,除非您确切知道自己在做什么。glibc启动代码在Linux上动态链接的可执行文件中运行(使用
.init
钩子);这不是问题所在。(通常不建议从一开始就使用libc,但据我所知,它在动态Linux可执行文件中实际上是完全安全的。它在Cygwin上不起作用,这可能是一些人反对它的原因?)。只有在stdio缓冲区已满时才退出而不刷新它才是问题所在。@PeterCordes@MatteoItalia有没有一种方法可以使用好的旧main而不必使用
gcc
编译,而只使用
ld
?@Alr