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