Assembly Source, Object Files, Linking & Debugging

Assembly source is not an executable: the assembler encodes sections and unresolved references into an object file, the linker lays out and relocates those objects, and debug metadata lets tools reconnect machine addresses to symbols and source.

Assembly source names instructions, labels, constants, sections, and external symbols. A CPU can execute none of those names. The toolchain must turn them into bytes at assigned addresses while retaining enough structure to resolve cross-file references and, when requested, explain those bytes to a debugger.

The artifact pipeline #

Source labels · directives symbolic operands Object file sections · symbols bytes · relocations Linked image layout · addresses entry · imports Process image mapped pages registers · stack assemble link load Debug correspondence source lines · types · variables · unwind rules addresses · symbols · build ID / debug link debugger joins metadata to loaded addresses
Each stage removes uncertainty: syntax becomes relocatable bytes, symbols receive addresses, and the loader creates a process image.
Artifact What is fixed What may remain unresolved
source (.asm, .s) symbolic intent, labels, directives instruction bytes, addresses, external definitions
object (.o, .obj) encoded bytes, section-relative offsets, local symbols final section addresses, external symbols, relocations
archive (.a, .lib) indexed collection of objects which members will be extracted
executable or shared object section/segment layout, resolved references allowed by the link model, entry metadata dynamic imports and load address when applicable
process image mapped pages, runtime relocations, initialized registers/stack future control flow and mutable state

The stages are not interchangeable. A successful assembly proves that each source file is syntactically encodable. It does not prove that every external symbol exists, that sections fit the target memory map, or that execution begins at the intended entry point.

What the assembler emits #

For a relocatable format, the assembler normally emits:

  • sections such as executable code, initialized data, and zero-initialized storage;
  • symbols with a name, binding, section, and section-relative value;
  • relocations describing fields whose final numeric value depends on link placement or another symbol;
  • debug information mapping instructions and variables back to source when enabled;
  • encoded instruction and data bytes whose values are already known.

GNU as describes an address as a section plus an offset. If an instruction refers to an external symbol, the assembler cannot invent its final address; it places a relocation record beside a provisional field. NASM similarly chooses output behavior from the requested format: a flat binary contains bytes only, while ELF, COFF, Mach-O, or OMF can carry sections, symbols, relocations, and supported debug formats.

What the linker decides #

The linker combines object files and selected archive members, resolves symbol references, assigns addresses to output sections, applies link-time relocations, and writes an executable, shared library, firmware image, or another relocatable object.

Order matters for archive extraction because a traditional linker pulls a member when it satisfies an unresolved reference already known at that point. A linker script or platform default also decides the memory layout, alignment, entry symbol, permissions, and which input sections are retained or discarded.

Undefined-symbol errors are therefore not assembler errors. Duplicate definitions, incompatible object formats, relocation overflow, a missing entry point, or a section that exceeds a firmware region belong to link layout and symbol resolution.

Flat binary versus relocatable object #

A flat binary is already a byte image for an assumed origin. It has no standard symbol table or relocation records for another linker to consume. It is useful when the consumer defines an exact layout: a boot sector, ROM region, DOS .COM image, or embedded payload.

A relocatable object preserves uncertainty explicitly. Sections can move, symbols can be supplied by another file, and debug data can survive into the final image. OMF was common in 16-bit DOS toolchains; ELF dominates many Unix-like systems; COFF/PE and Mach-O serve Windows and Apple platforms. The semantic stages remain the same even though container fields differ.

NASM still documents both 16-bit DOS .EXE/.COM production and current ELF, COFF, Mach-O, and OMF output. That makes old TASM/TLINK-style exercises historically recognizable without making those tools the durable topic.

A reproducible modern pass #

add.asmasm
global add_two
section .text
add_two:
    mov eax, edi
    add eax, esi
    ret
bash
nasm -f elf64 -g -F dwarf add.asm -o add.o
readelf -SWrs add.o
objdump -drwC -Mintel add.o
ld -r add.o -o combined.o

The useful inspection order is deliberate:

  1. readelf -S shows sections and their attributes.
  2. readelf -s shows which symbols are defined, local, global, or undefined.
  3. readelf -r or objdump -r shows unresolved address patches.
  4. objdump -d connects encoded bytes to disassembly.
  5. A linker map shows final placement and why an output section has its size.

ld -r performs a partial link and deliberately keeps a relocatable result. A normal final link also needs a valid platform entry and runtime environment; this isolated function is not a complete process by itself.

Debugging reconnects several views #

A debugger coordinates at least four representations:

  • machine registers and memory;
  • instruction addresses and disassembly;
  • symbols and stack-unwind rules;
  • source lines, variables, and types from debug information.

Stripping debug sections does not remove executable instructions, but it removes much of that correspondence. Separate debug files preserve it outside the deployed binary and can be matched by a debug link or build ID. Optimized code complicates the mapping even with perfect metadata: instructions may be reordered, variables may live briefly in registers or disappear, and one source line may map to several noncontiguous ranges.

Failure localization #

Evidence Likely failing stage
syntax error or invalid operand combination assembler/parser or ISA encoding
undefined or multiply defined symbol linker symbol resolution
relocation truncated or section overlaps region linker layout/model
correct file on disk, wrong bytes in memory loader, deployment, mapping, or self-modifying runtime
breakpoint moves or variable is unavailable optimization/debug mapping, not necessarily wrong execution
disassembly starts with nonsense wrong architecture/mode, wrong start address, or data decoded as code

The fastest diagnosis names the artifact and stage: “foo.o contains an unresolved PC-relative relocation” is actionable; “assembly failed” is not.

Sources

  1. docsNASM 3.02 documentation
  2. docsUsing as — the GNU Assembler
  3. docsThe GNU linker
  4. docsDebugging with GDB

Searches titles, aliases, headings, summaries, and full text.↑↓ move ↵ open esc close