在 nasm 中声明一个指针 [英] Declaring a pointer in nasm

查看:30
本文介绍了在 nasm 中声明一个指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用 nasm 编写一个简单的操作系统,而且我是汇编新手.从 c 开始,我习惯于能够声明指针而无需为它们保留内存并随心所欲地移动它们.我怎样才能在 nasm 中做到这一点?如果我声明一个变量,例如由

I am writing a simple os in nasm and I am new to assembly. From c I am used to be able to declare pointers without reserving memory for them and moving them around as I please. How can I do this in nasm? If I declare a variable e.g. by

var: resb 1

我知道我声明了一个指针并且可以访问变量的值,例如通过

I understand that I declare a pointer and can access the value of the variable e.g. by

mov eax, [var]

虽然,我不能将指针移动到另一个地址

Though, I cannot move the pointer to another address by

mov var, 0x1234

然后我得到操作码和操作数的无效组合".那么如何在内存中声明和移动指针呢?

then I get "invalid combination of opcode and operands". So how can I declare and move pointers in memory?

推荐答案

mov var, 0x1234 给你一个错误,因为 var 不是寻址模式,[var] 将是,但是,您仍然会收到错误,因为 NASM 无法确定操作的大小.

mov var, 0x1234 gives you an error because var is not an addressing mode, [var] would be but, nevertheless, you'd still get an error as NASM cannot tell the size of the operation.

mov DWORD [var], 0x1234 就可以了(假设您的内存模型使用 32 位近指针).

mov DWORD [var], 0x1234 would do (assuming your memory model uses 32-bit near pointers).

x86 没有间接地址移动,而在 C 中,您可以直接从内存中"使用指针(粗略地说),在汇编中,您必须首先将指针加载到寄存器中,然后将该寄存器用作地址.
亲自去 Godbolt 看看.

x86 doesn't have indirect address moves, while in C you can use pointers "directly from memory" (roughly speaking), in assembly you have to first load the pointer into a register and then use that register as an address.
See for your self at Godbolt.

int* bar;

int foo()
{
    return *bar;
}

------

foo():                                # @foo()
        mov     eax, dword ptr [bar]
        mov     eax, dword ptr [eax]
        ret
bar:
        .long   0

尊重操作需要一个额外的指令,这可能看起来像"使用两个尊重,但事实并非如此(实际上,在 NASM 中,可以将变量的名称视为指向它的指针,但让事情保持简单).

The deference operation takes an extra instruction, this may "looks like" as using two deferences but it is not (actually, in NASM, one could see the name of a variable as a pointer to it but lets keep things simple).

您可以将指针视为任何其他变量(包括复制它),只需将其复制到寄存器中并传递(上例中的第一个 mov 就是这样做的).
例如.

You can treat the pointer as any other variable (including copy it around) by simply copying it in a register and passing it around (the first mov in the example above does that).
E.g.

mov eax, DWORD [var]
call foo                ;Call foo with the pointer in EAX

mov DWORD [var], 0x1234 ;Change the pointer value

<小时>

关于保留内存:任何变量存储在内存中都会占用一些空间,这就是程序状态的原因.resb 用于未初始化的数据,这使用了 ELF在已编译的二进制文件中节省空间的功能在磁盘上.
如果您正在制作自己的操作系统,您可能根本不会使用 ELF,因此 resb(和类似的)可能会简单地回退为分配零初始化的 var(NASM 会对此发出警告).


Regarding reserving memory: any variable when stored in memory occupies some space, that's what makes the program state.
resb is used for uninitialised data, this makes uses of the ELF features to save space in the compiled binary when on disk.
If you are making your own OS you may not use ELF at all, thus resb (and similar) may simply fallback as allocating a zero-initialised var (NASM will warn about this).

如果变量的范围有限,您可以使用堆栈来临时存储它们;这将通过限制内存占用来重用相同的空间.
或者,您可以使用 %define 来定义汇编级符号,这些符号类似于(但完全不相同)C #defines.
例如:

You can use the stack to temporarily store your vars if they have a limited scope; this will reuse the same space there by limiting the memory footprint.
Alternatively you can use %define to define assembly-level symbols, these are like (but not identical at all to) C #defines.
For example:

%define MY_PTR_TO_SOMETHING 0x1234

mov DWORD [MY_PTR_TO_SOMETHING], 1       ;In C this is *MY_PTR_TO_SOMETHING = 1;

这不会为 MY_PTR_TO_SOMETHING 分配空间,因为它只是数字 0x1234 的别名.您可以将其视为 C #define MY_PTR_TO_SOMETHING ((int*)0x1234).或者像 static const int *MY_PTR_TO_SOMETHING = (int*)0x1234; 一样,编译器确实优化了指针对象本身的实际静态存储.

This won't allocate space for MY_PTR_TO_SOMETHING since it is just an alias for the number 0x1234. You can think of it as C #define MY_PTR_TO_SOMETHING ((int*)0x1234). Or like static const int *MY_PTR_TO_SOMETHING = (int*)0x1234; with a compiler that does optimize away the actual static storage for the pointer object itself.

但是请注意,由于指针值现在可用作已知常量,因此间接级别已经消失.
当然,你仍然可以传递它:

Note however that a level of indirection is gone thanks to the fact that the pointer value is now available as a known constant.
Of course, you can still pass it around:

mov eax, MY_PTR_TO_SOMETHING         ;EAX = Holds the value of MY_PTR_TO_SOMETHING
mov ebx, DWORD [eax]                 ;EBX = Load a DWORD from the address 0x1234
;Store or copy the EAX register to pass the pointer around

<小时>

如果不习惯 x86 寻址模式,使用指针可能会令人困惑.我的建议是阅读 mov 的说明手册,并确保了解 mov eax, 0x1234mov eax, DWORD [0x1234] 之间的区别>.


Working with pointers can be confusing if one isn't used to the x86 addressing modes. My advice is to read the instructions manual for mov and be sure to understand the difference between mov eax, 0x1234 and mov eax, DWORD [0x1234].

如果你需要一个可以在运行时修改的指针,你需要一些指针值的存储空间;它不能是汇编时间常数或标签地址.

If you need a pointer you can modify at runtime, you need some storage for the pointer value; it can't be an assemble-time constant or a label address.

在 C 中,您将使用 int *ptr; 它将在静态存储(在全局范围内)或在寄存器(或堆栈空间)中为指针大小的对象保留空间以用于本地范围.

In C, you'd use int *ptr; which would reserve space for a pointer-sized object either in static storage (at global scope) or in a register (or stack space) for local scope.

这篇关于在 nasm 中声明一个指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆