Home > Software design >  Confusion about the many ways of saving variables in assembly, aka .skip .equ and .quad
Confusion about the many ways of saving variables in assembly, aka .skip .equ and .quad

Time:09-11

Dear stack overflow users!

Trying to learn assembly here, and I have stumbled upon the fact that thus far, I have found three ways to create variables using assembly(AT&T syntax):

.equ makes a variable

.skip reserves a specific amount of storage for a variable

.quad/long/word/byte creates a small variable.

The only difference I have gathered thus far, is that the .quad method could lead to the variable getting executed under specific conditions(and that .skip kind of works like heap memory? I think?).

So at the very least, I wanted to know, are there any reasons to use any specific method? And which one do you think is the best to use to store things like strings, integers, and maybe some input from the user?


Edit: following fuz's explanation, I have constructed the following code, to test the differences:

.data

bar: .int 128

.equ bob, 128

.bss

buf: .skip 128

buf1: .skip bar

buf2: .skip bob

.text

.global main

main:
    mov $256, %rax
    mov %rax, bar
    mov %rax, bob
    mov %rax, buf
    ret

Expected behaviour:
All works except for buf1: .skip bar, and mov %rax, bob.

Actual behaviour:
All works except for buf1: .skip bar. The error test.s:11: Error: .space specifies non-absolute value has appeared.

Questions:
Why was I able to change bob at runtime?

CodePudding user response:

The first directive, .equ, is for defining symbolic constants. A directive of the form

        .equ foo, 42

can also be spelled as

foo=    42

and sets the symbol foo to value 42. Whenever you mention the symbol foo, the assembler or linker will substitute the value 42. Crucially, this is not a variable. You cannot change the value of foo at run time and foo is not stored anywhere in your program. On the other hand, being a symbolic constant, you can use foo wherever you could use a number. For example, you can skip foo bytes with

        .skip foo

which you couldn't do if foo was not a symbolic constant.

As for the others, these are fundamentally just different ways to reserve regions of memory. .byte reserves 1 byte, .word reserves 2 bytes, .long reserves 4 bytes and .quad reserves 8 bytes. You need to give the initial value the memory is going to get after the directive:

        .int 23

reserves 4 bytes of memory and writes the number 23 into that memory. If you want to have a name for that region of memory, put a label in front:

bar:    .int 23

The .skip directive is similar, but reserves however many bytes you tell it to. You cannot set their initial value though, they'll always be zero. For example, to reserve a buffer of 100 bytes, do

buf:    .skip 100

Note that for all these, it matter where you place the directives. The memory is reserved in the current section. So if you put these directives into the path of execution, the data will be executed as code. If you want to be able to write to the memory you reserved, you'll need to place the directives into a writable section such as .data.

        .data
foo:    .int 42
bar:    .byte 23

For buffers reserved with .skip it might be useful to put them into the .bss section as that reduces the size of the executable file on disk. You can only reserve storage with .skip or similar in .bss (i.e. the initial memory contents are all zeroes). Otherwise it behaves identical to .data.

        .bss
buf:    .skip 100
  • Related