Minix 3.3.0 i386汇编程序中write(2)系统调用失效问题排查
Minix 3.3.0 i386汇编Hello-World程序write调用失效问题修复
我正在编写Minix 3.3.0 i386汇编版Hello-World程序,exit(2)调用可正常传递退出码,但write(2)调用无法输出内容。
程序源码
; Compile with: nasm -o prog prog.nasm && chmod +x prog cpu 386 bits 32 org 0x08048000 Ehdr_OSABI equ 0 ; SYSV. PT: .LOAD equ 1 .NOTE equ 4 .GNU_STACK equ 0x6474e551 Elf32_Ehdr: ; ELF-32 i386 header. db 0x7f, 'ELF', 1, 1, 1, Ehdr_OSABI db 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 3, 0 dd 1, _start, Elf32_Phdr0-$$, 0, 0 dw Elf32_Phdr0-$$, 0x20, (Elf32_Phdr_end-Elf32_Phdr0)>>5 dw 0x28, 0, 0 Elf32_Phdr0: dd PT.LOAD, 0, $$, $$, text_end-$$, text_end-$$, 5, 0x1000 Elf32_Phdr1: dd PT.LOAD, text_end-$$, text_end+0x1000, text_end+0x1000 dd file_end-text_end, mem_end-text_end, 6, 0x1000 Elf32_Phdr2: dd PT.NOTE, Elf32_Note-$$, Elf32_Note, Elf32_Note, dd Elf32_Note_end-Elf32_Note, Elf32_Note_end-Elf32_Note, 4, 4 Elf32_Phdr3: dd PT.GNU_STACK, 0, 0, 0, 0, 0, 7, 4 Elf32_Phdr_end: Elf32_Note: ; Same PT_NOTE as in /bin/cat in Minix 3.3.0. db 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00 db 0x00, 0x4d, 0x69, 0x6e, 0x69, 0x78, 0x00, 0x00, 0x00, 0xe0, 0x36 db 0xe6, 0x11, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03 db 0x00, 0x00, 0x00, 0x50, 0x61, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00 Elf32_Note_end: _start: cld sub esp, 16*4 ; Minix message_t. mov edi, esp xor eax, eax mov ecx, 16 rep stosd ; Clear the Minix message_t. mov ebx, esp mov dword [ebx+1*4], 0x101 ; SYS_MINIX330.VFS_WRITE. mov dword [ebx+2*4], 1 ; STDOUT_FILENO. mov dword [ebx+3*4], msg_hello mov dword [ebx+4*4], msg_hello.end-msg_hello mov eax, 1 ; MINIX_WHO.FS ;mov ebx, ebx ; Already points to the Minix message_t. mov ecx, 3 ; SENDREC. int 0x21 ; Minix i386 syscall. It ruins EAX, EBX, ECX and EDX. ; Don't check the result. mov ebx, esp mov edi, esp xor eax, eax mov ecx, 16 rep stosd ; Clear the Minix message_t. mov dword [ebx+1*4], 1 ; SYS_MINIX330.PM_EXIT. mov dword [ebx+2*4], 42 mov eax, 0 ; MINIX_WHO.MM. ;mov ebx, ebx ; Already points to the Minix message_t. mov ecx, 3 ; SENDREC. int 0x21 ; Minix i386 syscall. It ruins EAX, EBX, ECX and EDX. ; Not reached. It works, exit code is propagated. msg_hello: ; We put it to .rodata, which is the same PT_LOAD as .text. db 'Hello, World!', 10 .end: text_end: db 0 ; Minix 3.3.0 crashes with a missing .data. Nonempty is safest. file_end: mem_end:
运行结果
# ./prog; echo $? 42 # _
排查信息
- .rodata段(与.text同属一个PT_LOAD)的全局变量可正常访问,Minix未设置内存保护,可修改.rodata变量;
- .data段全局变量也可正常使用,需按PT_LOAD设置在虚拟地址上加0x1000;
- 首次write(2)的
int 0x21调用返回EAX=0(表示成功),且[esp+4]=1,但实际无字节写入; - 将Minix消息缓冲区移至.data段后,write(2)仍失效,返回值变为-38(ENOTSOCK,无意义);
- 将.data段按页对齐后,问题仍存在。
等价C程序
#include <unistd.h> void _start(void) { (void)!write(1, "Hello, World!\n", 14); _exit(42); }
问题分析与修复
问题出在Minix消息结构的参数顺序错误。Minix 3.3.0中,VFS_WRITE请求的message_t结构里,参数顺序应为:
m_type: 系统调用号(0x101)m1_i1: 文件描述符(STDOUT_FILENO=1)m1_i2: 要写入的字节数m1_p1: 缓冲区地址m1_i3: 文件偏移(字符设备可忽略)
原代码中错误地将缓冲区地址放在了m1_i2的位置,字节数放在了m1_i3的位置,导致VFS接收到错误的参数:尝试从无效地址(字节数的值)读取大量字节(缓冲区地址的数值),最终只写入了无意义的少量数据,无法输出预期内容。
修复方法:交换缓冲区地址和字节数的赋值顺序,修改_start中的write调用部分:
mov ebx, esp mov dword [ebx+1*4], 0x101 ; SYS_MINIX330.VFS_WRITE. mov dword [ebx+2*4], 1 ; STDOUT_FILENO (m1_i1). mov dword [ebx+3*4], msg_hello.end-msg_hello ; 写入字节数 (m1_i2). mov dword [ebx+4*4], msg_hello ; 缓冲区地址 (m1_p1).
修改后重新编译运行,即可正常输出Hello, World!。
内容的提问来源于stack exchange,提问作者pts
相关产品推荐
相关产品推荐

