不同的细分可能会相互崩溃

时间:2019-06-17 13:01:20

标签: assembly x86-16

当我们编码时,我们给不同的段寄存器提供相同的值,在这种情况下,两个不同的段可能会相互混淆,从而可能会出现问题。 例如在以下给定条件下。

MOV AX, CS
MOV SS, AX
ASSUME SS: DOSGROUP 
如以上各行所述,在第1行中,我们将CS移至AX。之后我们立即将AX转换为SS。这意味着此时CS和SS寄存器包含相同的值或相同的存储位置,这不是真的,当我们进一步编码时,CS的内容将与内容SS一起折叠。

2 个答案:

答案 0 :(得分:3)

设置段寄存器不会更改内存的内容(实际上在实模式下根本无法访问内存),只会更改 CPU的内存视图(逻辑上线性翻译)。

具有两个具有相同值的段寄存器仅表示相同的偏移量 X 可以用于访问相同的数据。
如果我们不想指导汇编器如何计算不同段的偏移量,这将非常方便。
如果我们天真地编写汇编程序,我们将缺少汇编程序的段信息,该段信息会将程序视为单个代码段。因此,汇编器将为数据和代码使用相同的偏移量计数器,但是在运行时这两个区域是具有不同段(即DSCS)的访问,除非您覆盖它每次加载/存储。
如果上述属性不为true,则在加载程序加载程序的情况下,汇编程序生成的数据偏移量将不匹配。

当通过指针获取函数指针或通过指针处理堆栈上的数据时(即与bp没有固定偏移量),此方法也很有用,它实际上将每个指针转换为近指针。
因此,变量的偏移量实际上成为变量的地址,而无需进一步考虑该段。这使得地址/偏移更易于推论,因为现在,如果变量具有偏移X,这是我们唯一需要关注的偏移,X与我们使用CS一样, DSSS
因此X是1-1映射到其var的,如果我们必须考虑分段,则每个var可能有多达64Ki / 16的偏移量,并且该集合与偏移量不相交另一个变量集(即偏移量X可以表示两个不同的变量,当与两个不同的段一起使用时),因此指针必须很远。
由于我们只有少数有效的空闲段寄存器,因此,远的指针(除了指针大小加倍之外)给寄存器分配策略带来了很大压力。

当然,由于段的大小相对较小(64KiB),将所有代码,数据和堆栈放入一个段可能是具有挑战性的。
程序员通常不设置代码和数据(一旦构建了二进制文件,它们的布局就固定了),因此仅设置了堆栈,必须注意不要将堆栈指针设置得过于靠近数据和数据的末尾。代码。
在最大调用链深度上进行餐巾纸计算常常会忽略对堆栈的正确处理,但是如果我们无法绑定调用链,或者我们有太多的代码/数据,则堆栈大小在百字节内我们可能需要重构。
由于缺少功能,因此很难在实模式下正确管理堆栈,我想不到一种比检查每个函数入口处的堆栈指针更好的方法。

除非每种数据类型有多个段(数据/堆栈,代码),否则使用单独的段免费增加可寻址内存,但在初始化时增加几行(当然,我们不使用函数指针)。
将指针传递到堆栈上的结构时,使堆栈和数据具有相同的段很有用。

DOS .com可执行文件是其中CSDSSS都共享相同值的程序的示例。 DOS .exe可执行文件通常是为小型内存模型组装/编译的,在小型内存模型中,我们只有代码(CS和数据(DSSS)段。
但是它们可以处理其他内存模型,包括巨大的内存模型,在该模型中,我们可以为数据类型(代码,堆栈,数据)提供多个段。

使用多段编程容易出错,这就是为什么32位OS停止使用它,而64位OS无法使用它的原因(但以有限的方式)。
但是段也很有用,它们可以在内存中创建方便的视图,尤其是在访问MMIO和ISA区域时。

如果您对段没有足够的经验(痛苦吗?),很难理解这堵文字墙,我的建议是从一个有效的程序开始,并通过将数据移入(不必要地)分离的段来有意伤害自己。
只要记住如何将逻辑地址(段:偏移量)转换为线性地址(段* 16 +偏移量)即可。

答案 1 :(得分:0)

我想将答案分为两部分。

第1部分

  

这意味着此时CS和SS寄存器包含相同的值或相同的存储位置...

在16位x86程序中,段不描述内存位置,但是它们描述内存位置的范围

“理想化”的16位CPU只能使用64 KiB内存。为了能够使用超过64 KiB的内存,兼容8086的CPU有以下段:

段寄存器的值描述了CPU应该在其中工作的64 KiB大小的内存范围。因此,一个16位程序可以(仅)访问64 KiB的内存,但是通过更改段寄存器的值,它可以选择应访问1 MiB内存中的哪个64 KiB。这意味着程序可以通过更改段寄存器的值来有效访问1 MiB的内存(而不仅仅是64 KiB)。

当然,代码和不同类型的数据可能位于相同的64 KiB内存范围内。 (对于只有64 KiB内存的计算机(例如1982 IBM PCjr),这甚至很明显。)

在这种情况下,CSSS具有相同的值,因为代码(CS寄存器)和堆栈(SS寄存器)位于相同的64 KiB内存范围

但是“相同的内存范围”(其中包含许多不同的内存位置)和“相同的内存位置”之间是有区别的。

第2部分

  

我们如何使用相同的内存位置执行两项操作...

让我们更改示例,并使用成对的寄存器SS:SPES:DI,而不是寄存器CSSS。这些对实际上描述的是内存位置,而不是内存范围。

如果SS:SPES:DI的值相同会怎样?

答案:没事。

CPU使用SS:SP计算堆栈操作(例如pushpop)的内存位置,使用ES:DI进行字符串写操作(例如stosmovs)。

因为CPU不会一次执行两次操作,所以如果为两种操作指定了相同的内存位置,就没有问题。

但是,您必须注意不会发生任何愚蠢的事情。示例:

push bx
; Let's say SS:SP and ES:DI contain the same value here
stosw
pop bx

使用stosw指令将破坏您使用push指令push编辑的数据,因为SS:SPES:DI指向相同的内存位置。

CPU对此不关心。如果指示CPU覆盖堆栈,则CPU将覆盖堆栈。

避免这种情况是您作为程序员的职责。

但实际上,每种编程语言都存在完全相同的问题。在C,C ++,C#或Java中,您可能会遇到以下错误:

for(i = 0; i < 5; i++)
{
    for(i = 0; i < 10; i++)
    {
        ...
    }
}

...而且不明白为什么内部循环执行10次而不是50次。