将linux中汇编代码的运行结果重定向到文本文件

Sar*_*all 2 linux assembly printf gcc io-redirection

我正在尝试编写一个Python脚本来测试我在汇编中针对预期输出编写的各种代码的输出.但是我很难将输出重定向到文件中.我写了以下内容:

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
Run Code Online (Sandbox Code Playgroud)

然后我继续在控制台中执行以下操作:

nasm -f elf64 basic.asm
gcc -m64 -o basic basic.o
./basic
Run Code Online (Sandbox Code Playgroud)

其中10输出到屏幕.但是,如果我进入

./basic > basic.txt
cat basic.txt
Run Code Online (Sandbox Code Playgroud)

basic.txt显示为空文件.我的总体目标是编写一个shell脚本,循环遍历每个程序集文件以编译和运行该文件,然后将该脚本的输出重定向到一个文件中.但是,在我能够使用单个文件之前,我无法做到这一点.我想知道这与我对printf的调用有什么关系?虽然我认为printf写入STDOUT的错觉.

提前致谢!

Emp*_*ian 7

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

调试此类问题工具是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>
Run Code Online (Sandbox Code Playgroud)

你可以清楚地看到你想要的输出,但也有一些"流浪"写.写的来自哪里?

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 ()
Run Code Online (Sandbox Code Playgroud)

好,这是预期的写作电话.

(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
Run Code Online (Sandbox Code Playgroud)

这只是系统调用的回报.写的成功了吗?(我们知道它确实如此,因为我们看到它的输出在上面,但让我们确认.)

(gdb) p $rax
$1 = 3
Run Code Online (Sandbox Code Playgroud)

好.Write写了预期的3个字符.

(gdb) c

Catchpoint 1 (call to syscall 'write'), 0x0000000000400577 in os_return ()
Run Code Online (Sandbox Code Playgroud)

这是我们没想到的写作.来自哪里?

(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
Run Code Online (Sandbox Code Playgroud)

所以你的系统调用执行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
Run Code Online (Sandbox Code Playgroud)

从上面可以看出,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
Run Code Online (Sandbox Code Playgroud)

的确是.所以我们已经解决了"杂散写入来自哪里?" 难题.修复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)                                = ?
Run Code Online (Sandbox Code Playgroud)

那仍然是不对的.我们应该打电话_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)                                = ?
Run Code Online (Sandbox Code Playgroud)

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

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

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

很明显,我们的号召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)                                = ?
Run Code Online (Sandbox Code Playgroud)

我希望你今天学到的东西(我做过;-)


归档时间:

查看次数:

656 次

最近记录:

14 年,3 月 前