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, %rax为movq 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));
它没有声明一个新变量。