c和汇编的指针算术

我想使用 Assembly 和 C 访问内存上的特定位置。

我创建了以下结构:

struct node{
    uint64_t x[5];
    uint64_t y;
    struct node * next;
};

后来,我创建了一个该类型的对象。

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

struct node{
    uint64_t x[5];
    uint64_t y;
    struct node * next;
};

void foo();

struct node * ptr;

int main(){
    
    struct node* ptr = (struct node *) malloc(sizeof(struct node));
    ptr->next = NULL;

    foo();

    printf("%lun", ptr->y);

    return 0;
}

现在,在 Assembly 上,我想更改 y 的值。

    .section .data
    
    .text
    .globl  foo
    .extern ptr

foo:

    //access ptr->y
    leaq (ptr + 40), %r12
    movq $42, %r12
    
    ret

我希望 %r12 具有 ptr->y 的地址。我想象它会得到正确的地址,因为 ptr.x 将是内存中的第一个,它的权重为 8*5 = 40 字节,但事实并非如此。

回答

首先,从 中选择一个不同的寄存器%r12。在 Linux 和大多数其他类 Unix 系统(我假设您正在使用,因为这是 GNU 汇编程序的主要目标)上使用的 x86-64 ABI 中,%r12是一个“被调用者保存的”寄存器,这意味着当编译器调用您的函数foo,它希望%r12保持其价值。您可以%r12在函数的开始和结束处推送和弹出,但选择一个不同的寄存器更简单,该寄存器是“调用者保存的”,编译器假定其值可能会改变。%rax下面我会用。

您需要从内存中加载才能将 的值ptr放入寄存器中。你leaq不会那样做;它以指针地址加 40、 in 结束%r12,并且不从内存中读取。相反,您需要mov带有内存源操作数的 a 。

然后,您需要做一个存储来实际写入ptr指向(加 40)的地址。您当前movq只将数字 42 放入%r12寄存器中,根本不修改内存。

尝试

    .text
    .globl  foo
    .extern ptr

foo:

    movq ptr, %rax  # no $ means this is a load from memory
    movq $42, 40(%rax)
    ret

如果尝试构建与位置无关的可执行文件(64 位 Linux 的默认值),则需要相对于 rip 寻址来访问全局变量。在这种情况下,替换movq ptr, %raxmovq ptr(%rip), %rax


您有一个单独的问题,因为您声明了一个名为ptrin的局部变量main,它隐藏了具有相同名称的全局变量。的结果malloc分配给局部变量,而全局变量保持等于NULL。该函数foo将访问全局函数,因此它将取消引用 NULL 并崩溃。你应该改变

    struct node* ptr = (struct node *) malloc(sizeof(struct node));

简单地

    ptr = (struct node *) malloc(sizeof(struct node));

它没有声明一个新变量。


以上是c和汇编的指针算术的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>