Assembly 来自程序集的Printf调用不打印到标准输出
我正在x86_64 Linux上的NASM中构建并运行代码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
-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 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
有什么想法吗?在这种情况下,libcprintf
似乎正在打印到另一个文件描述符,而不是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
,然后将返回值传递给libcexit(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