2

我有一个汇编程序/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
}

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

4

6 回答 6

3

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

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

对于初学者,出于显而易见的原因,只要段寄存器保持正确的值,使用哪个段寄存器都没有关系。例如,如果DSES寄存器在内部保存相同的值,DS:<offset>则将指向与ES:<offset>.

例如,在所谓的“微型”内存模型中,所有段寄存器都保存相同的值,即所有内容——代码、数据、堆栈——都适合一个段(这就是它被称为“微型”的原因)。在这个内存模型中,每个指针只是这个段中的一个偏移量,当然,使用哪个段寄存器来处理这个偏移量并不重要。

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

还有“中间”内存模型,代码将存储在一个段(CS)中,而数据和堆栈都将存储在另一个段中,因此DSSS将保持相同的值。在那个平台上,显然没有必要区分DSSS

在最大的内存模型中,您可以有多个数据段。在这种情况下,很明显,分段模式下正确的数据寻址实际上并不是选择正确的段寄存器(您似乎相信),而是获取几乎任何段寄存器并用正确的初始化它的问题执行访问之前的值。

于 2011-02-01T18:56:18.430 回答
1

AndreyT 描述的是 DOS 时代发生的事情。如今,现代操作系统使用所谓的平面内存模型(或者说是非常相似的东西),其中设置了所有(保护模式)段,以便它们都可以访问整个地址空间(即:它们的基数为 0和一个限制=整个地址空间)。

于 2011-02-01T19:28:42.003 回答
1

On a machine with a segmented memory model, the C implementation must do one of the following things to be conformant:

  • Store the full address (with segment) in each pointer, OR
  • Ensure that all stack addresses that will be used for variables whose addresses are taken can be accessed via the data segment, either at the same relative address or via some magic offset the compiler can apply when taking the address of local variables, OR
  • Not use the stack for local variables whose addresses are taken, and perform a hidden malloc/free on every function entry/return (with special handling for longjmp!).

Perhaps there are other ways of doing it, but these are the only ones I can think of. Segmented memory models were really pretty disagreeable with C, and they were abandoned for good reason.

于 2011-02-01T20:09:35.777 回答
0

分段是 Intel 16 位 8086 处理器的遗留产物。实际上,您可能在虚拟内存中操作,其中一切都只是一个线性地址。-S使用标志编译并查看生成的程序集。

于 2011-02-01T18:46:38.233 回答
0

由于您在取消引用之前将地址移动到 eax,因此它默认为 ds 段。然而,正如 Nikolai 所提到的,在用户级代码中,这些段可能都指向同一个地址。

于 2011-02-01T18:53:34.720 回答
0

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

于 2011-02-01T18:58:13.200 回答