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

使用OpenWatcom编译链接的小模型DOS .exe程序崩溃排查

你的汇编代码中的关键错误分析

我来帮你拆解导致程序在DOSBox中无限循环的核心问题,尤其是当存在未调用函数中的字符串时的异常行为:

1. _printmsg_函数的致命指令顺序错误

你的_printmsg_函数开头第一行就是ret,这意味着任何对该函数的调用都会立即返回,完全跳过后续的函数逻辑。更严重的是,这个函数是供C代码调用的,遵循cdecl调用约定:

  • C代码调用_printmsg(msg)时,会先把msg的地址压入栈中,再执行call指令。
  • 但你的函数直接ret,不会弹出栈上的参数,导致栈平衡被彻底破坏。

即使other()函数未被显式调用,如果编译器没有优化掉该函数的代码(比如-Os可能保留未调用函数),一旦程序因某种意外跳转到other()(比如内存访问错误导致的指令流错乱),调用_printmsg_就会打乱栈结构,最终引发无限循环或崩溃。

另外,函数内部还有两个逻辑错误:

  • 硬编码使用hello_msg,完全忽略了C代码传入的msg参数,违背了函数声明的意图。
  • 没有妥善处理寄存器:xchg ax, dx修改了ax寄存器,虽然void函数影响较小,但不符合调用约定中寄存器保存的规范。

修正后的_printmsg_应该符合cdecl约定:

_printmsg_:
 push bp          ; 建立栈帧,遵循cdecl调用规范
 mov bp, sp
 push dx          ; 保存dx寄存器(DOS中断会修改它)
 mov dx, [bp+4]   ; 从栈中取出传入的msg参数(bp+4是第一个参数,bp+2是返回地址)
 mov ah, 9        ; DOS WRITE_STDOUT功能调用
 int 0x21
 pop dx           ; 恢复dx寄存器
 pop bp           ; 销毁栈帧
 ret              ; 返回,调用者会负责清理栈上的参数

2. 段命名与OpenWatcom的兼容性问题

OpenWatcom在DOS小模型(-mcmodel=s)下,编译C代码时默认使用的段名是:

  • 代码段:_TEXT
  • 数据段:_DATA

而你的汇编代码中使用的是segment code和segment data,这会导致链接器将汇编的段与C代码生成的段视为独立的段。当你在C代码中添加字符串字面量时,该字符串会被放入_DATA段,但你的程序初始化时只将ds指向了汇编的data段,这会导致:

  • 若程序意外访问到_DATA段中的内容(比如栈溢出覆盖到该段,或指令流错乱),会因为ds指向错误而读取到无效内存,进而触发异常行为(比如无限循环)。

修正方法是将汇编中的段名改为与OpenWatcom一致:

segment _TEXT class=CODE use16
segment _DATA class=DATA use16
segment STACK class=STACK use16

3. 入口点符号的冗余定义

你的汇编代码中同时定义了global _small_code_和..start:

global _small_code_
segment code
_small_code_:
..start:

..start是NASM的特殊符号,专门用于告知链接器这是程序的入口点,而global _small_code_是多余的,甚至可能导致链接器混淆入口点(虽然在你的案例中似乎没有直接引发问题,但会增加潜在风险)。

建议移除global _small_code_,只保留..start作为入口点标记:

segment _TEXT class=CODE use16
..start:
 mov ax, _DATA
 mov ds, ax
 mov ax, STACK
 mov ss, ax
 mov sp, stacktop
 ; ... 后续初始化代码

4. 栈段的简化处理

你的汇编代码中单独定义了STACK段,这在小模型下是允许的,但OpenWatcom默认会将栈放在_DATA段中。如果你想简化段管理,可以将栈合并到_DATA段中:

segment _DATA class=DATA use16
hello_msg: db 'Hello, World!', 13, 10, '$'
stack resb 1024
stacktop:

然后初始化栈时只需:

mov ax, _DATA
mov ds, ax
mov ss, ax
mov sp, stacktop

为什么移除字符串后程序正常?

当你移除other()函数中的字符串字面量时,C代码生成的_DATA段为空,链接器可能会忽略该段,此时程序的ds指向汇编的data段(或合并后的段),不会出现段不匹配的问题。同时,other()函数的代码可能被编译器优化掉,避免了潜在的栈平衡问题触发的异常。


内容的提问来源于stack exchange,提问作者pts

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 07:23:11