【问题标题】:Accessing a *next pointer to a struct using GNU Assembly使用 GNU 汇编访问指向结构的 *next 指针
【发布时间】:2012-10-07 04:44:56
【问题描述】:

我正在使用 GNU Assembly 尝试遍历 C 结构 Linked Listed 并从其中一个结构值中找到某个值。我想知道如何到达结构的 *next 指针以移动到下一个节点并检查值。以下是我编写的一些示例 C 代码以尝试学习。

struct node{
   struct node *next;
   int id;
  };

struct node *root;

void functionToBuildLinkList(){
   //Code to malloc and link 4 nodes
 }

int main(int argc, char *argv[]){

   functionToBuildLinkList();

   int valueOne;

   rootPtr = rootPtr->next;
   valueOne = rootPtr->id;
   printf("The valueOne is: %i\n", valueOne);

   return 0;
}

为了帮助自己弄清楚,我查看了 main 的 objdump。

mov    0x804a024,%eax   //Moving the rootPtr->next address into eax
mov    0x4(%eax),%eax   //Offset 4 bytes for id
mov    %eax,0x804a024   //Compiler nonsense?
mov    0x804a024,%eax   //Compiler nonsense?
mov    (%eax),%eax      //Moving the contents of EAX into EAX or more nonsense?
mov    %eax,0x1c(%esp)  //Moving id into the stack
mov    $0x804861c,%eax  //What address is being moved into EAX?
mov    0x1c(%esp),%edx  //Moving id into edx
mov    %edx,0x4(%esp)   //Moving edx (id) into the stack
mov    %eax,(%esp)      //What's the address in EAX? The top of stack - ESP?
call   8048340 <printf@plt>
mov    $0x0,%eax        //Returning O

我有点被甩了,因为看起来编译器正在提供硬编码的内存地址。我怎么知道下一个内存地址或某些节点在哪里?当我通过在堆上分配内存来创建列表时,它是按顺序排列的,所以我可以尝试以这种方式计算它吗?我将如何通过列表工作?同样,当我将内存地址获取到结构的开头时,*next 指针是第一个成员变量,它不会有偏移(访问它)吗?任何帮助表示赞赏。

感谢大家的帮助!因此,使用示例代码中的全局指针根迭代列表将是:

movl root, %eax   //Move root into EAX
movl (%eax), %eax //eax = eax->next

【问题讨论】:

  • root 和 rootPtr 似乎是具有两个不同名称的同一个变量。
  • root 是一个全局变量。链接器为所有全局变量建立特定的内存位置。
  • 如果你让 GCC 用-S 编译你的源代码,你会得到一个带注释的汇编输出,其中包含更多信息,例如符号名称,这将解释$0x804861c 是什么,以及那些“编译器”废话”行是(提示:不是废话)。

标签: c assembly struct gnu gnu-assembler


【解决方案1】:

使用了两种不同的寻址模式:

mov    0x804a024,%eax   //Moving the rootPtr (content) into eax
mov    [%eax], %ebx     //access tmp=rootPtr->next

add    0x4, %ebx        // increment tmp by 4 to get the address of &tmp->id

mov    $0x804861c,%eax  // Moving an address to eax

第一行通常写成

mov.l  [0x804a024], %eax

第一个“废话”对应左边的赋值

rootPtr = rootPtr->next;

虽然可以优化下一行。

【讨论】:

  • 感谢我有机会运行我的代码,您可以移动到下一个节点,它是 movl (%eax), %ebx。
【解决方案2】:

让我们正确地注释这个......

mov    0x804a024,%eax   // eax = rootPtr (global variable)
mov    0x4(%eax),%eax   // eax = eax->next (offset 4)
mov    %eax,0x804a024   // rootPtr = eax

mov    0x804a024,%eax   // eax = rootPtr
mov    (%eax),%eax      // eax = eax->id (offset 0)
mov    %eax,0x1c(%esp)  // valueOne = eax (local variable, on stack)
mov    $0x804861c,%eax  // eax = "The valueOne is: %i\n" (static string)
mov    0x1c(%esp),%edx  // edx = valueOne
mov    %edx,0x4(%esp)   // put edx on argument stack (position 1, offset 4)
mov    %eax,(%esp)      // put eax on argument stack (position 0, offset 0)
call   8048340 <printf@plt> // call printf(eax, edx) (= printf(string, valueOne))
mov    $0x0,%eax        // return 0

这个例子中有很多无用的动作。如果您在轻量级优化模式下编译(例如-O),您通常可以获得更简单的代码。在像 -O3 这样的高级别上,由于棘手的优化,代码可能会变得非常难以理解。

请注意,您编写的汇编代码不会使用“硬编码”地址;如果需要引用全局,就在上面打个标签,通过标签来引用。请注意,例如,访问-&gt;next 只是访问0x4(%eax) 的问题(假设节点指针在eax 中);这是因为next 指针距离结构体开头有 4 个字节。

【讨论】:

  • 感谢您解决所有问题我更新了我的问题以帮助解决我的几个问题。
猜你喜欢
  • 2017-01-04
  • 2017-07-18
  • 2023-03-25
  • 2023-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多