问题描述
我正在尝试编写一个Python脚本,以针对预期的输出测试我在汇编中编写的各种代码的输出.但是我很难将输出重定向到文件中.我写了以下内容:
I'm trying to write a Python script to test the output of some various code I've written in assembly against an expected output. However I am having difficulty redirecting the output into a file.I have written the following:
extern printf
LINUX equ 80H ; interupt number for entering Linux kernel
EXIT equ 1 ; Linux system call 1 i.e. 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 i.e. exit ()
mov rbx, 0 ; Error code 0 i.e. no errors
mov rcx, 5
int LINUX ; Interrupt Linux kernel
然后我要在控制台中执行以下操作:
I then procede to do the following in the console:
nasm -f elf64 basic.asm
gcc -m64 -o basic basic.o
./basic
哪个输出10到屏幕.但是,如果我输入
Which outputs 10 to the screen.However if I enter
./basic > basic.txt
cat basic.txt
basic.txt显示为空文件.我的总体目标是编写一个Shell脚本,该脚本循环遍历每个程序集文件以编译和运行该文件,然后将该脚本的输出重定向到一个文件中.但是,直到我可以使它与单个文件一起使用,我才能执行此操作.我想知道这与我对printf的调用有关吗?尽管我被printf写到STDOUT的幻想中.
basic.txt appears as an empty file.My overall goal is to write a shell script that loops over each assembly file to compiling and run the file and then redirect the output of this script into a file. However I cannot do this until I can get it to work with a single file.I was wondering it it was something to do with my call to printf? Although I was under the illusion that printf writes to STDOUT.
提前谢谢!
推荐答案
您的重定向是正确的;问题一定出在您生成的程序集中.
Your redirection is correct; the problem must be in the assembly you are generating.
调试此类问题的工具是strace
.在strace
下运行程序,显示:
The tool to debug such problems is strace
. Running your program under strace
, shows:
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>
您可以清楚地看到所需的输出,但也可以看到一些杂散"的文字.那笔信从哪里来?
You can clearly see your desired output, but also some "stray" write. Where is that write coming from?
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 ()
好,这是我们期望的写作要求.
Good, this is the expected call to write.
(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
这只是syscall的返回.写成功了吗? (我们知道是的,因为我们在上面看到了它的输出,但是让我们确认一下.)
This is just the return from syscall. Did write succeed? (We know it did, since we see its output above, but let's confirm.)
(gdb) p $rax
$1 = 3
好. Write写了预期的3个字符.
Good. Write wrote the expected 3 characters.
(gdb) c
Catchpoint 1 (call to syscall 'write'), 0x0000000000400577 in os_return ()
这是我们没想到的写法.从哪里来?
This is the write we didn't expect. Where from?
(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)
.为什么会这样?
So your syscall executed write(2)
instead of expected exit(2)
. Why did this happen?
由于您错误地定义了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.
From above, you can tell that EXIT
should be 1 in 32-bit mode, but 60 in 64-bit mode.
那NR_write呢?在64位模式下为1吗?
What about NR_write? Is it 1 in 64-bit mode?
grep 'define .*NR_write' /usr/include/asm/unistd_64.h
#define __NR_write 1
#define __NR_writev 20
的确如此.因此,我们解决了杂散写入来自何处?"的问题.谜.将EXIT
固定为60,然后在strace
下重新运行,我们现在看到:
Indeed it is. So we have solved the "where did stray write come from?" puzzle. Fixing EXIT
to be 60, and rerunning under strace
, we now see:
...
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 ,表明您的寄存器使用不正确:syscall号应该在%rax
中,但是参数在%rdi
,%rsi
,%rdx
等中.
That still isn't right. We should be calling _exit(0)
, not _exit(1)
. A look at the x86_64
ABI, reveals that your register use is incorrect: syscall number should be in %rax
, but the arguments in %rdi
, %rsi
, %rdx
, etc.
修复该问题(并删除伪造的mov rcx, 5
),我们终于从strace
获得了期望的输出:
Fixing that (and deleting bogus mov rcx, 5
), we finally get desired output from strace
:
...
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa5bb8da000
write(1, "10\n", 3) = 3
10
_exit(0) = ?
所以现在我们已经准备好看看上述修补程序是否也解决了重定向问题.
So now we are ready to see if above fixes also fixed the redirection problem.
在strace下重新运行,并重定向输出:
Re-running under strace, with output redirected:
strace ./basic > t
...
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f08161eb000
_exit(0) = ?
很显然,我们对write
的呼叫丢失了.去哪了?
Clearly our call to write
is missing. Where did it go?
好吧,stdout
输出在默认情况下是行缓冲的,并且在重定向到文件时会得到完全缓冲.也许我们错过了fflush
通话?
Well, stdout
output is line buffered by default, and gets fully buffered when redirected to a file. Perhaps we are missing an fflush
call?
确实,在退出前将呼叫添加到fflush(NULL)
即可解决问题:
Indeed, adding a call to fflush(NULL)
just before exiting solves the problem:
...
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8afd450000
write(1, "10\n", 3) = 3
_exit(0) = ?
我希望你今天学到了一些东西(我做过;-)
I hope you've learned something today (I did ;-)
这篇关于将在Linux中运行汇编代码的结果重定向到文本文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!