将在linux中运行汇编代码的结果redirect到文本文件

我正在尝试编写一个Python脚本来testing我编写的一些代码的输出与预期的输出。 但是,我很难将输出redirect到一个文件。 我写了以下内容:

extern printf LINUX equ 80H ; interupt number for entering Linux kernel EXIT equ 1 ; Linux system call 1 ie exit () section .data intfmt: db "%ld", 10, 0 segment .text global main main: push rax push rsi push rdi mov rsi, 10 mov rdi, intfmt xor rax, rax call printf pop rdi pop rsi pop rax call os_return ; return to operating system os_return: mov rax, EXIT ; Linux system call 1 ie exit () mov rbx, 0 ; Error code 0 ie no errors mov rcx, 5 int LINUX ; Interrupt Linux kernel 

然后我开始在控制台中执行以下操作:

 nasm -f elf64 basic.asm gcc -m64 -o basic basic.o ./basic 

其中10个输出到屏幕上。 但是,如果我input

 ./basic > basic.txt cat basic.txt 

basic.txt显示为空文件。 我的总体目标是编写一个shell脚本,它循环遍历每个程序集文件以编译和运行该文件,然后将此脚本的输出redirect到一个文件中。 但是,我不能这样做,直到我可以得到它与一个单一的文件。 我想知道这是我打电话给printf的事情吗? 虽然我是在printf写入STDOUT的幻想之下的。

提前致谢!

你的重定向是正确的。 问题必须出现在您生成的程序集中。

调试这些问题工具是strace 。 在strace下运行你的程序,显示:

 strace ./basic ... mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa5bb8da000 write(1, "10\n", 3) = 3 10 write(1, "z\377n\f\377\177\0\0\0\0\0\0\0\0\0\0\202\377n\f\377\177\0\0\362\377n\f\377\177\0\0"..., 139905561665008 <unfinished ... exit status 0> 

你可以清楚地看到你想要的输出,但也有一些“流浪”的写。 写的是从哪里来的?

GDB来拯救:

 gdb -q ./basic Reading symbols from /tmp/basic...done. (gdb) catch syscall write Catchpoint 1 (syscall 'write' [1]) (gdb) r Catchpoint 1 (call to syscall 'write'), 0x00007ffff7b32500 in __write_nocancel () (gdb) bt #0 0x00007ffff7b32500 in __write_nocancel () at ../sysdeps/unix/syscall-template.S:82 #1 0x00007ffff7acd133 in _IO_new_file_write (f=0x7ffff7dd7780, data=0x7ffff7ff8000, n=3) at fileops.c:1276 #2 0x00007ffff7ace785 in new_do_write (fp=0x7ffff7dd7780, data=0x7ffff7ff8000 "10\n", to_do=3) at fileops.c:530 #3 _IO_new_do_write (fp=0x7ffff7dd7780, data=0x7ffff7ff8000 "10\n", to_do=3) at fileops.c:503 #4 0x00007ffff7accd9e in _IO_new_file_xsputn (f=0x7ffff7dd7780, data=0x601023, n=1) at fileops.c:1358 #5 0x00007ffff7a9f9c8 in _IO_vfprintf_internal (s=0x7ffff7dd7780, format=<value optimized out>, ap=0x7fffffffda20) at vfprintf.c:1644 #6 0x00007ffff7aaa53a in __printf (format=0x7ffff7ff8000 "10\n") at printf.c:35 #7 0x000000000040054f in main () 

好,这是预期的写作要求。

 (gdb) c 10 Catchpoint 1 (returned from syscall 'write'), 0x00007ffff7b32500 in __write_nocancel () at ../sysdeps/unix/syscall-template.S:82 82 in ../sysdeps/unix/syscall-template.S 

这只是系统调用的返回。 写成功了吗? (我们知道它确实,因为我们看到它的输出,但是让我们来确认一下。)

 (gdb) p $rax $1 = 3 

好。 写写了预期的3个字符。

 (gdb) c Catchpoint 1 (call to syscall 'write'), 0x0000000000400577 in os_return () 

这是我们没有想到的写作。 从哪里来?

 (gdb) bt #0 0x0000000000400577 in os_return () #1 0x0000000000400557 in main () (gdb) disas Dump of assembler code for function os_return: 0x0000000000400557 <+0>: movabs $0x1,%rax 0x0000000000400561 <+10>: movabs $0x0,%rbx 0x000000000040056b <+20>: movabs $0x5,%rcx 0x0000000000400575 <+30>: int $0x80 => 0x0000000000400577 <+32>: nop 0x0000000000400578 <+33>: nop 0x0000000000400579 <+34>: nop 0x000000000040057a <+35>: nop 0x000000000040057b <+36>: nop 0x000000000040057c <+37>: nop 0x000000000040057d <+38>: nop 0x000000000040057e <+39>: nop 0x000000000040057f <+40>: nop End of assembler dump. (gdb) quit 

所以你的系统调用执行write(2)而不是预期的exit(2) 。 为什么会发生?

因为您已经错误地定义了EXIT

 grep 'define .*NR_exit' /usr/include/asm/unistd*.h /usr/include/asm/unistd_32.h:#define __NR_exit 1 /usr/include/asm/unistd_32.h:#define __NR_exit_group 252 /usr/include/asm/unistd_64.h:#define __NR_exit 60 /usr/include/asm/unistd_64.h:#define __NR_exit_group 231 

从上面可以看出, EXIT在32位模式下应该是1,而在64位模式下应该是60。

NR_write呢? 在64位模式下是1吗?

 grep 'define .*NR_write' /usr/include/asm/unistd_64.h #define __NR_write 1 #define __NR_writev 20 

它的确是。 所以我们解决了“流浪写了哪里?” 难题。 将EXIT固定为60,然后在strace下重新运行,现在我们看到:

 ... mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa5bb8da000 write(1, "10\n", 3) = 3 10 _exit(1) = ? 

那还是不对的。 我们应该叫_exit(0) ,而不是_exit(1) 。 看一下x86_64 ABI ,发现你的注册使用是不正确的:系统调用数应该在%rax ,但是%rdi%rsi%rdx等参数

修复(并删除伪mov rcx, 5 ),我们终于得到期望的strace输出:

 ... mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa5bb8da000 write(1, "10\n", 3) = 3 10 _exit(0) = ? 

所以现在我们准备看看上面的修复是否也解决了重定向问题。

在strace下重新运行,输出重定向:

 strace ./basic > t ... mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f08161eb000 _exit(0) = ? 

显然,我们的write缺失。 它去了哪里?

那么, stdout输出是默认行缓冲,并重定向到一个文件时得到完全缓冲。 也许我们错过了一个fflush调用?

事实上,在退出之前向fflush(NULL)添加一个调用可以解决这个问题:

 ... mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8afd450000 write(1, "10\n", 3) = 3 _exit(0) = ? 

我希望你今天学到了一些东西(我做了;-)