我有一个汇编程序/ 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的地址作为参数进行测试)或数据段?如何计算完整的逻辑地址?该函数无法知道给定地址偏移与哪个段相关..?!
答案 0 :(得分:3)
一般情况下,在分段平台上,您不能按照建议读取指针值“eax
”。在分段平台上,指针通常同时包含段值和偏移值,这意味着读取这样的指针意味着初始化至少两个寄存器 - 段和偏移 - 而不仅仅是eax
但在具体情况下,它取决于所谓的内存模型。分段平台上的编译器支持多种内存模型。
首先,由于显而易见的原因,只要段寄存器保存正确的值,哪个段寄存器无关紧要。例如,如果DS
和ES
寄存器内部保持相同的值,则DS:<offset>
将指向内存中与ES:<offset>
相同的位置。
在所谓的“微小”内存模型中,例如,所有段寄存器都保持相同的值,即所有内容 - 代码,数据,堆栈 - 都适合一个段(这就是为什么它被称为“微小的”)。在这个内存模型中,每个指针只是该段中的一个偏移量,当然,与该偏移量一起使用哪个段寄存器无关紧要。
在“较大”的内存模型中,您可以为代码(CS),堆栈(SS)和数据(DS)分别创建段。但是在这样的内存模型上,指针对象通常会将两者放在其中的地址的偏移量和段部分中。在您的示例中,指针p
实际上是一个由两部分组成的对象,同时包含段值和偏移值。为了取消引用这样的指针,编译器将生成将从p
读取段和偏移值的代码并使用它们。例如,段值将被读入ES
寄存器,而偏移值将被读入si
寄存器。然后,代码将访问ES:[di]
以便读取*p
值。
还有“中间”内存模型,当代码存储在一个段(CS)中时,数据和堆栈都将存储在另一个段中,因此DS
和SS
将保持不变相同的价值。显然,在该平台上,无需区分DS
和SS
。
在最大的内存模型中,您可以拥有多个数据段。在这种情况下,很明显,在分段模式下正确的数据寻址实际上并不是选择合适的段寄存器(正如您所认为的那样),而是需要花费很多任何段在执行访问之前,使用正确的值注册并初始化它。
答案 1 :(得分:1)
AndreyT描述的是在DOS时代发生的事情。现在,现代操作系统使用所谓的flat memory model(或更确切地说非常类似的东西),其中所有(保护模式)段都被设置为使得它们都可以访问整个地址空间(即:它们具有基础0和限制=整个地址空间。)
答案 2 :(得分:1)
在具有分段内存模型的计算机上,C实现必须执行以下某项操作才能符合:
longjmp
进行特殊处理!)。也许有其他方法可以做到,但这些是我能想到的唯一方法。分段记忆模型对C来说真的很不愉快,而且它们被遗弃了。
答案 3 :(得分:0)
分段是英特尔16位8086处理器的传统工件。实际上,您可能在virtual memory中操作,其中一切都只是一个线性地址。使用-S
标志进行编译并查看生成的程序集。
答案 4 :(得分:0)
由于您在解除引用之前将地址移动到eax,因此默认为ds段。但是,正如Nikolai所提到的,在用户级代码中,这些段可能都指向相同的地址。
答案 5 :(得分:0)
在x86下,直接使用堆栈将使用堆栈段,但间接使用将其视为数据段。如果您反汇编指针取消引用并写入堆栈节指针,则可以看到这一点。在x86 cs下,由于线性寻址,ss和ds的处理方式几乎相同(至少在非内核模式下)。英特尔参考手册还应该有一个关于段寻址的部分