This question already has answers here:
Using printf in assembly leads to empty output when piping, but works on the terminal
(2个答案)
Can ptrace tell if an x86 system call used the 64-bit or 32-bit ABI?
(1个答案)
What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?
(1个答案)
在8个月前关闭。
我正在尝试编写一个Python脚本,以针对预期的输出测试我在程序集中编写的各种代码的输出。但是我很难将输出重定向到文件中。
我写了以下内容:
然后,我继续在控制台中执行以下操作:
向屏幕输出10。
但是如果我输入
basic.txt显示为空文件。
我的总体目标是编写一个Shell脚本,该脚本循环遍历每个程序集文件以编译和运行该文件,然后将该脚本的输出重定向到一个文件中。但是,直到我可以使它与单个文件一起使用,我才能执行此操作。
我想知道这与我给printf的电话有关吗?尽管我被printf写到STDOUT的幻想中。
提前致谢!
您可以清楚地看到所需的输出,但也可以看到一些“杂散”的文字。那笔信从哪里来?
GDB解救:
好,这是预期的写请求。
这只是syscall的返回。写成功了吗? (我们知道它确实如此,因为我们在上面看到了它的输出,但是让我们确认一下。)
好。写写预期的3个字符。
这是我们没想到的写法。从哪里来?
因此,您的系统调用执行了
由于您未正确定义
从上面可以看出
NR_write呢?在64位模式下是否为1?
它的确是。因此,我们解决了“杂散写入来自何处?”的问题。难题。将
那还是不对。我们应该调用
解决这个问题(并删除伪造的
因此,现在我们可以看看上面的修复程序是否也解决了重定向问题。
在strace下重新运行,并重定向输出:
显然,我们对
好吧,
实际上,在退出前添加对
希望您今天学到了一些东西(我做过;-)
(2个答案)
Can ptrace tell if an x86 system call used the 64-bit or 32-bit ABI?
(1个答案)
What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?
(1个答案)
在8个月前关闭。
我正在尝试编写一个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的幻想中。
提前致谢!
最佳答案
您的重定向是正确的;问题必须出在您生成的装配中。
调试此类问题的工具是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
这只是syscall的返回。写成功了吗? (我们知道它确实如此,因为我们在上面看到了它的输出,但是让我们确认一下。)
(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,发现您的寄存器使用不正确:syscall号应该在%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) = ?
希望您今天学到了一些东西(我做过;-)