使用指针作为函数参数时的段前缀

时间:2011-02-01 18:30:46

标签: c assembly x86 segment

我有一个汇编程序/ c问题。我刚刚阅读了有关段前缀的内容,例如ds:varX等。前缀对于计算逻辑地址很重要。我也读过,默认为“ds”,只要你使用ebp寄存器计算地址,就会使用“ss”。代码“cs”是默认值。这一切都有道理。 现在我在c中有以下内容:

int x; // some static var in ds

void test(int *p){
...
*p =5;

}

... main(){

test(&x);
//now x is 5
}

如果你现在考虑测试函数的实现......你会在堆栈上得到指向x的指针。如果要取消引用指针,首先从堆栈中获取指针值(x的地址)并将其保存在eax中。然后你可以取消引用eax来改变x的值。但是c编译器如何知道给定的指针(地址)是否引用堆栈上的内存(例如,如果我从另一个函数调用test并将localvariable的地址作为参数进行测试)或数据段?如何计算完整的逻辑地址?该函数无法知道给定地址偏移与哪个段相关..?!

6 个答案:

答案 0 :(得分:3)

一般情况下,在分段平台上,您不能按照建议读取指针值“eax”。在分段平台上,指针通常同时包含段值和偏移值,这意味着读取这样的指针意味着初始化至少两个寄存器 - 段和偏移 - 而不仅仅是eax

但在具体情况下,它取决于所谓的内存模型。分段平台上的编译器支持多种内存模型。

首先,由于显而易见的原因,只要段寄存器保存正确的值,哪个段寄存器无关紧要。例如,如果DSES寄存器内部保持相同的值,则DS:<offset>将指向内存中与ES:<offset>相同的位置。

在所谓的“微小”内存模型中,例如,所有段寄存器都保持相同的值,即所有内容 - 代码,数据,堆栈 - 都适合一个段(这就是为什么它被称为“微小的”)。在这个内存模型中,每个指针只是该段中的一个偏移量,当然,与该偏移量一起使用哪个段寄存器无关紧要。

在“较大”的内存模型中,您可以为代码(CS),堆栈(SS)和数据(DS)分别创建段。但是在这样的内存模型上,指针对象通常会将两者放在其中的地址的偏移量和段部分中。在您的示例中,指针p实际上是一个由两部分组成的对象,同时包含段值和偏移值。为了取消引用这样的指针,编译器将生成将从p读取段和偏移值的代码并使用它们。例如,段值将被读入ES寄存器,而偏移值将被读入si寄存器。然后,代码将访问ES:[di]以便读取*p值。

还有“中间”内存模型,当代码存储在一个段(CS)中时,数据和堆栈都将存储在另一个段中,因此DSSS将保持不变相同的价值。显然,在该平台上,无需区分DSSS

在最大的内存模型中,您可以拥有多个数据段。在这种情况下,很明显,在分段模式下正确的数据寻址实际上并不是选择合适的段寄存器(正如您所认为的那样),而是需要花费很多任何段在执行访问之前,使用正确的值注册并初始化它。

答案 1 :(得分:1)

AndreyT描述的是在DOS时代发生的事情。现在,现代操作系统使用所谓的flat memory model(或更确切地说非常类似的东西),其中所有(保护模式)段都被设置为使得它们都可以访问整个地址空间(即:它们具有基础0和限制=整个地址空间。)

答案 2 :(得分:1)

在具有分段内存模型的计算机上,C实现必须执行以下某项操作才能符合:

  • 在每个指针中存储完整地址(带段),或
  • 确保所有用于变量地址的堆栈地址都可以通过数据段访问,可以在相同的相对地址访问,也可以通过编译器在获取局部变量地址时应用的一些魔法偏移来访问,或者
  • 不使用堆栈来获取其地址的局部变量,并在每个函数入口/返回时执行隐藏的malloc / free(对longjmp进行特殊处理!)。

也许有其他方法可以做到,但这些是我能想到的唯一方法。分段记忆模型对C来说真的很不愉快,而且它们被遗弃了。

答案 3 :(得分:0)

分段是英特尔16位8086处理器的传统工件。实际上,您可能在virtual memory中操作,其中一切都只是一个线性地址。使用-S标志进行编译并查看生成的程序集。

答案 4 :(得分:0)

由于您在解除引用之前将地址移动到eax,因此默认为ds段。但是,正如Nikolai所提到的,在用户级代码中,这些段可能都指向相同的地址。

答案 5 :(得分:0)

在x86下,直接使用堆栈将使用堆栈段,但间接使用将其视为数据段。如果您反汇编指针​​取消引用并写入堆栈节指针,则可以看到这一点。在x86 cs下,由于线性寻址,ss和ds的处理方式几乎相同(至少在非内核模式下)。英特尔参考手册还应该有一个关于段寻址的部分