Declaring a pointer in nasm

2019-08-22 07:15发布

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?

1条回答
何必那么认真
2楼-- · 2019-08-22 08:07

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 would do (assuming your memory model uses 32-bit near pointers).

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

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).

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

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).

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;

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

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.

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.

查看更多
登录 后发表回答