1

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?

hellwraiz
  • 359
  • 2
  • 13
  • 2
    `.equ` doesn't make a "variable" in memory, it makes an assemble-time constant. `.skip` is like a static array in C, static storage class not dynamic (heap), Nothing about `.quad` makes it get executed; only if you put it in `.text` and jump to i. – Peter Cordes Sep 10 '22 at 15:07
  • 2
    *Why was I able to change `bob` at runtime?* - You weren't. Storing to RAX to absolute address 128 is a valid instruction (encodeable into machine code), but will segfault if you run it in a normal user-space process. Regardless, that doesn't change the symbol "value" (which you use as an address). – Peter Cordes Sep 10 '22 at 16:02
  • 1
    Note also that to load the value of `bob` into a register, you need to use `mov $bob, %rax`, not `mov bob, rax` as with variables. This is because for constants, the name refers to the value of the constant, not its address; constants do not have addresses. – fuz Sep 10 '22 at 16:03
  • @PeterCordes do you mean that because of the absent $ sign I only changed the address of `bob` and not the contents? – hellwraiz Sep 10 '22 at 16:10
  • 1
    @hellwraiz: `bob` has no "contents", it's an assemble-time constant. Syntactically, it works like a symbol defined by a label, but whose "address" is `128`, because that's how GAS and AT&T syntax work. Thus `mov bob, %eax` is a load from that address (which will fault because your program didn't do anything to reserve memory there), and `mov $bob, %eax` is `mov $128, %eax`. There is one minor ambiguity in AT&T syntax between a `.equ` constant and a symbol defined another way (like a label such a `bob:`), see the bottom of [this answer](https://stackoverflow.com/a/39360636/224132) – Peter Cordes Sep 10 '22 at 16:16
  • I am thankful for the fact that you are trying to help me, but I just don't understand what that answer that you linked is saying. Regardless, I am not trying to `mov $bob, %eax`, but `mov %eax, $bob`. And after I added the $ sign to `bob` `buf` and `bar` it started saying `operand type mismatch for `mov` for all of them. – hellwraiz Sep 10 '22 at 16:43
  • 1
    @hellwraiz You cannot assign to the address of a symbol. Symbols do not exist at runtime. If you want something you can assign to, use a variable; do not define a symbol with `.equ`. – fuz Sep 12 '22 at 20:22

1 Answers1

2

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
fuz
  • 88,405
  • 25
  • 200
  • 352
  • so is there any use for `foo: .int 42`, if I can't even use it as a variable? I mean, sure, I can change the value during runtime, but I still can't use it. And is it possible to assign anything to a buffer? I did `.skip 128` on it, but now it is just taking memory space, and I can't seem to be able to assign any value to it. – hellwraiz Sep 10 '22 at 15:33
  • 1
    @hellwraiz What do you mean by “can't use it as a variable?” If you define `foo` as `foo: .int 42`, that's exactly what a variable is. You can e.g. use `movl $23, foo` to set `foo` to 23. Works the same with buffers. Note that in contrast to constants defined with `.equ`, symbols for variables refer to the *address* of the variable, not its value. – fuz Sep 10 '22 at 15:36
  • 1
    If you have a particular code example you tried that failed, [edit] your question and add it in. – fuz Sep 10 '22 at 15:37
  • Just added the code that I made, the expected and actual behaviour, and a question. – hellwraiz Sep 10 '22 at 15:59
  • 1
    @hellwraiz The number of bytes you reserve with `.skip` must be a constant as the number must be known at assembly time. You cannot change the number of bytes in a buffer at run time. – fuz Sep 10 '22 at 16:00
  • Maybe add a note that EQU is basically the assembly equivalent of C/C++ #define preprocessor directive. – SoronelHaetir Sep 10 '22 at 20:34
  • @SoronelHaetir It is not, that would be a macroo. C does not have anything like this. – fuz Sep 10 '22 at 21:41
  • What's `res` instruction for? – CrazyMan Oct 30 '22 at 01:06
  • @CrazyMan Where do you see this instruction? – fuz Oct 30 '22 at 01:43