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

发布于 2024-12-15 04:14:34 字数 1101 浏览 1 评论 0原文

我正在尝试编写一个 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

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

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

将 10 输出到屏幕。 但是,如果我输入

./basic > basic.txt
cat basic.txt

basic.txt 将显示为空文件。 我的总体目标是编写一个 shell 脚本,该脚本循环遍历每个程序集文件以编译并运行该文件,然后将该脚本的输出重定向到一个文件中。但是,除非我可以让它与单个文件一起工作,否则我无法执行此操作。 我想知道这与我对 printf 的调用有关吗?尽管我有一种幻觉,认为 printf 会写入 STDOUT。

提前致谢!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

宫墨修音 2024-12-22 04:14:34

您的重定向是正确的;问题一定出在您正在生成的程序集中。

调试此类问题的工具是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

很好。 Write 写入了预期的 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)                                = ?

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

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 ;-)

清晨说晚安 2024-12-22 04:14:34

尝试从 main 退出

mov rax, 0; exit code
ret

Try exiting from main by

mov rax, 0; exit code
ret
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文