我正在尝试编写一个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) = ?
我希望你今天学到了一些东西(我做了;-)