x86-64 assembly from the ground up

Sections and equ

What goes in each section and why, and how equ gives a number a name at assembly time so a program has no magic constants left in it.

Part 11 of 12

Advanced16 min readBack to Courses

Three sections, three jobs

section .text holds instructions. It is readable and executable and not writable, so a program cannot accidentally overwrite its own code. section .data holds bytes that have values before the program runs: text, tables, initialised counters. It is writable and it takes up room in the file.

section .bss holds bytes that start at zero and are not stored in the file at all — only their size is. A sixteen-byte buffer declared with resb 16 costs sixteen bytes at run time and nothing on disk, which is why every buffer in this course lives there.

The names are conventions the assembler and the linker agree on rather than deep truths, but they earn their keep: a reader can see at a glance what is stored, what is executed, and what is merely reserved.

equ names a number without storing one

equ creates a name for a value that exists only while the assembler is running. Nothing is placed in memory and nothing is loaded at run time: every later use of the name is replaced by the number before the file is written. SYS_WRITE equ 1 and SYS_EXIT equ 60 become the numbers one and sixty in the instructions that use them, and the names themselves cost nothing at all.

The right-hand side can be an expression, and two special symbols make lengths easy. $ is the address of the current line and $$ is the address where the current section began, so a line reading LEN equ $ - msg after a message has the length of that message, whatever it turns out to be.

That is why every program here measures rather than counts. Edit the text of a message and the length follows, because the subtraction is done at assembly time from two addresses the assembler already knows.

The whole layout with names

SYS_WRITE equ 1
SYS_EXIT  equ 60
STDOUT    equ 1

section .data
msg db "named numbers", 10
LEN equ $ - msg

section .bss
out: resb 8

section .text
_start:
  mov rax, SYS_WRITE
  mov rdi, STDOUT
  mov rsi, msg
  mov rdx, LEN
  syscall

  mov rax, SYS_EXIT
  xor rdi, rdi
  syscall

A constant, a message and a buffer

Run it, then give the reserved buffer a fourth byte and store a newline in it. The size on the `resb` line is the only thing that says how much room the buffer has, and nothing checks the stores against it.

Output
Click "Run" to execute your code

The second line of output is built entirely in the reserved bytes: three stores into out, then a write of that buffer. Nothing about it exists in the file, and it is still there when the program needs it.

The loader gives each section a job

A section is not just a place in the file. It is a promise about permissions when the program is loaded: .text is mapped readable and executable but not writable, .data is readable and writable, and .bss is readable and writable and starts at zero. Writing to .text is a fault, not a silent change to an instruction.

.bss is special because the file stores only its size. The loader reserves that many bytes, fills them with zero, and hands the program an address. A large buffer therefore costs almost nothing on disk, which is why it is the right home for scratch space.

The section names are conventions shared by the assembler and the linker rather than instructions to the processor. Their value is that every tool in the chain agrees on what each one means.

equ is a name, not a variable

An equ line is processed while the assembler is reading the file. Every later use of the name is replaced by the value, and the name itself disappears. There is no address, no storage and no load at run time, so an equ name cannot be written to or changed.

That is why LEN equ $ - msg can produce a length even though neither $ nor msg is a run-time value. The assembler knows both addresses while laying out the section and does the subtraction there and then.

The same mechanism makes call numbers readable. SYS_WRITE equ 1 does not add a variable called SYS_WRITE; it makes every instruction that mentions it carry the number one. A reader sees the name, and the processor sees the number.

Three section mistakes. Storing into .text faults because the page is not writable. Assuming .bss holds something other than zeros gives you whatever the loader put there, which is nothing. And treating an equ name as an address gives the assembler a number where it expected a place.

Try these next

  • Give the descriptor one a name with equ and use it in both write calls.
  • Reserve eight bytes in .bss, fill all eight with letters and a newline, then write all eight.
  • Change a message and confirm the length still follows without editing the mov.

Copy a word into reserved space

Run it, then change `COUNT` to four. The copy loop and the write both shrink together, which shows that one assembler-time name is feeding the size of the reserved area and the number of bytes sent.

Output
Click "Run" to execute your code

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

Which section holds bytes that take no room in the file?