You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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结构里,参数顺序应为:

  1. m_type: 系统调用号(0x101)
  2. m1_i1: 文件描述符(STDOUT_FILENO=1)
  3. m1_i2: 要写入的字节数
  4. m1_p1: 缓冲区地址
  5. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 03:09:54