【问题标题】:Why is there only one segment in .COM programs, both for the program code and for the stack?为什么 .COM 程序中只有一个段,用于程序代码和堆栈?
【发布时间】:2020-12-11 22:55:33
【问题描述】:

我正在研究 .EXE 程序和 .COM 程序之间的区别。 .EXE 对我来说是合乎逻辑的,堆栈相对于程序代码位于另一个段(实际上堆栈是强制的,并用 .STACK 指示此限制),因此,当我去插入值时堆栈(在 .EXE 中),使用与程序不同的段,我不会混淆这两者。

例子:

SP = 0400
SS = 3996 CS = 3995 IP = 0000

栈的容量为1024 Byte (400h),指的是栈段3996h,与代码段3995h不同。所以我确信数据不会混淆。

但我不明白的是,当我必须处理 .COM 程序时;因为我很清楚他们只使用一个片段,我发现自己遇到了类似的情况:

SP = FFEE
SS = 114A CS = 114A IP = 0100

我有与代码段匹配的堆栈段。那么如果我一直把值放在栈上,它们迟早会出现在我的代码中吗?

【问题讨论】:

  • 是的,栈最终会和你的代码段发生冲突。
  • 虽然我只能推测 COM 格式仅支持单个段,但我认为是 (a) 因为该模型允许您将 8086 视为 16 位机器,忽略所有段寄存器和 (b ) 因为早在 DOS 1 发布时,IBM 在任何常规配置中都没有配备超过 64k 的内存,而且这么多内存被认为是非常离谱的。
  • “那么如果我一直把值放在栈上,它们迟早会出现在我的代码中吗?”当然,考虑到 8086 段的工作方式,即使堆栈段与代码段不同,如果它们重叠,也可能发生这种情况。他们通常会这样做,因为谁需要一个完整的 64K 单独堆栈?
  • @Fuz:我相信唯一不支持的 DOS 版本是 IBM PC-DOS 1.0,它基本上是当时最新的 86-DOS 的更名版)。 86-DOS 不支持 EXE 文件格式(除了后来制造 86-DOS 的公司最终使用了 OEM MS-DOS 1.25)。 MSDOS 版本从支持 EXE 文件格式的 1.24 开始。 1.24 成为 IBM PC-DOS 1.1 的基础(因此 IBM PC-DOS 1.1+ 支持 EXE)。 MS-DOS 编号不是从 1.0 开始的,因为它们延续了 86-DOS 的编号方案。 CP/M-86也不支持EXE格式。

标签: assembly dos x86-16 memory-segmentation


【解决方案1】:

虽然 MS-DOS 确实在加载 COM 文件后在程序入口处将所有段寄存器设置为同一段,但没有什么能阻止您更改寄存器以使用不同的段。但是,您所设想的细分市场之间的硬分离并不真正存在。即使使用单独的堆栈段,如果堆栈溢出,您仍然会在不应该写入的地方写入内存,并且结果将是不可预测的。

使用 CS:IP 为 114A:0100 和 SS:SP 为 114A:FFFE 的第二个示例,您的 COM 程序可以将 SS:SP 更改为 210A:03FE。两个 SS:SP 值将指向相同的线性地址 (2149E) 和 MS-DOS 在进入时压入堆栈的相同返回值。您现在只有 1024 字节的堆栈,就像在 EXE 示例中一样,但您的堆栈段仍然是 64k。在实模式下,段始终为 64k。

现在猜猜当 SS:SP 是 210A:0000 并且您尝试将一个单词压入堆栈时会发生什么? CPU 不会产生异常,它不会以某种方式拒绝执行操作,它只是做它一直做的事情。它从 SP 中减去 2,然后将该字存储在新的 SS:SP 地址中。在这种情况下,这意味着 SP 将回绕到 FFFE,并且您推送的值将存储在 210A:FFFE 或线性地址 3109E。

现在的问题是线性地址 3109E 分配了什么?很有可能 MS-DOS 将这个内存分配给你的程序并且在那里写入值是无害的,因为你的程序没有将它用于任何事情。但是,有可能某些 TSR 或驱动程序已在此处分配内存并覆盖它会导致崩溃或其他不可预知的行为。

如果您在堆栈溢出时重写自己的代码实际上会更好,因为这意味着您更有可能注意到错误并修复它。

EXE 也存在同样的环绕问题。如果您想要一个真正隔离的堆栈,则需要将大小设置为 64K,但是溢出时发生的情况是堆栈会覆盖自身,因此并不能从根本上消除问题。最终,作为开发人员,您有责任确保您的程序为自己分配了足够的堆栈空间,并且您的代码永远不会超过该限制。


请注意,对于 COM 文件,您需要担心一个与堆栈相关的问题,那就是不能保证 MS-DOS 能够为您的 COM 文件分配完整的 64K。 MS-DOS 将分配您的 COM 文件将适合的最大空闲内存块。如果该内存块小于 64K,则 SP 不会设置为 FFFE,而是将 SP 设置为指向分配给程序的最后一个字。在最坏的情况下,这意味着堆栈将立即开始覆盖 COM 文件中的代码和数据。因此,最好在 COM 文件的末尾分配空间来为堆栈保留空间。

【讨论】:

    【解决方案2】:

    一个 *.com 文件可以使用函数 4ah 将 ram 的大小调整为可执行文件的最小值,最后通过函数 48h 从 DOS 获得空闲 ram。

    【讨论】:

      猜你喜欢
      • 2013-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-31
      • 2015-07-01
      • 2016-01-19
      • 1970-01-01
      • 2019-12-17
      相关资源
      最近更新 更多