问题
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
Which outputs 10 to the screen. However if I enter
./basic > basic.txt
cat basic.txt
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.
Thanks in advance!
回答1:
Your redirection is correct; the problem must be in the assembly you are generating.
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 to the rescue:
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
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
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
So your syscall executed write(2)
instead of expected exit(2)
. Why did this happen?
Because you've defined EXIT
incorrectly:
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
From above, you can tell that EXIT
should be 1 in 32-bit mode, but 60 in 64-bit mode.
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
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) = ?
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.
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.
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) = ?
Clearly our call to write
is missing. Where did it go?
Well, stdout
output is line buffered by default, and gets fully buffered when redirected to a file. Perhaps we are missing an fflush
call?
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 ;-)
回答2:
Try exiting from main by
mov rax, 0; exit code
ret
来源:https://stackoverflow.com/questions/8105804/redirecting-result-of-running-assembly-code-in-linux-to-text-file