Repository navigation
COFF and Sections
Normative source: SPRU018D, chapter 2 (Introduction to Common Object File
Format) for the model, and SPRU018D Appendix A (COFF file, section, symbol and
relocation structures, with the relocation-type table A-9) for the byte-level
layout that lib/c5xcoff/ implements. The object files that the c5xtools
assembler and linker produce and consume are in common object file format
(COFF).
COFF organizes a program as a set of named blocks called sections. Thinking in terms of sections encourages modular code: each section is a distinct block of code or data that will finally occupy a contiguous span of the target memory map. This page describes what a section is, how the assembler builds sections, how the linker combines and places them, how relocation works, and how symbols are recorded in the file.
A section is the smallest unit of an object file. Every section is separate and distinct, and ends up as one contiguous region in memory. A COFF object file always has three default sections:
| Section | Usual contents |
|---|---|
.text |
executable code |
.data |
initialized data |
.bss |
reserved space for uninitialized variables |
Beyond these three, the assembler and linker let you create your own named sections and use them the same way as the defaults.
There are two basic kinds of section:
- Initialized sections hold actual data or code that is stored in the object
file.
.textand.dataare initialized, and so is any named section made with the.sectdirective. - Uninitialized sections only reserve space in the memory map; they have no
contents in the object file.
.bssis uninitialized, and so is any named section made with the.usectdirective.
One of the linker's jobs is to relocate sections into the target memory map, which is called allocation. Because a target usually has several kinds of memory, sections let you use that memory efficiently: every section is independently relocatable, so any section can be placed into any allocated block of memory (for example, an initialization routine can be placed into a region backed by EPROM).
The assembler decides which parts of the source belong to which section. Six directives drive this:
| Directive | Creates | Kind |
|---|---|---|
.text |
the .text section |
initialized |
.data |
the .data section |
initialized |
.sect |
a named section with relocatable addresses | initialized |
.asect |
a named section with absolute addresses | initialized |
.bss |
reserved space in the .bss section |
uninitialized |
.usect |
reserved space in a named section | uninitialized |
If the source uses none of these, the assembler assembles everything into
.text.
.text, .data, .sect, and .asect tell the assembler to stop assembling
into the current section and start assembling into the named one, which acts as
an implied end of the current section. The assembler keeps placing code or data
there until it meets the next such directive. Sections are built iteratively:
the first .data starts with an empty section, and each later .data appends
to what is already there, so the whole .data section can later be allocated
as one contiguous unit. Each initialized section is separately relocatable and
may reference symbols defined in other sections; the linker resolves those
references.
.bss and .usect do not contain data in the object file; they only reserve
memory, normally allocated into RAM, that the program uses at run time. Unlike
the initialized directives, .bss and .usect do not end the current section
and begin a new one; they temporarily escape from the current section and can
appear anywhere inside an initialized section without changing its contents.
Each .bss invocation reserves more space in .bss; each .usect invocation
reserves more space in the named section it points at.
Syntax:
.bss symbol, size in words [, blocking flag]
symbol .usect "section name", size in words [, blocking flag]
The symbol points to the first word reserved by this invocation and may be
referenced from any section (and made global with .global). The size is an
absolute word count. The optional blocking flag, when greater than 0, asks the
assembler to keep the reserved words from crossing a page boundary unless the
size itself exceeds a page.
Named sections are ones you create and use like the defaults, but they are
assembled separately so you can allocate them separately (for instance, an
initialization routine kept apart from .text). .usect makes named sections
used like .bss; .sect and .asect make named sections that can hold code or
data, with relocatable and absolute addresses respectively. Section names are
significant to 8 characters, and up to 32,767 named sections can exist. Using a
new name creates a new section; reusing a name continues the existing one. The
same name cannot be used with two different directives.
The assembler keeps a separate program counter, the SPC, for each section. The SPC is the current address within its section. Each SPC starts at 0, and the assembler advances it as it fills the section. When assembly resumes in a section, the assembler restores that section's previous SPC value and keeps counting from there. The assembler treats every section as if it began at address 0; the linker later relocates each section to its real place in memory.
Swapping among .data, .bss, .text, and a named section newvars within
one source file produces four sections: a .text section of 7 words of code, a
.data section of 5 words of data, a .bss section reserving 11 words, and the
.usect named section newvars reserving 8 words. The listing shows, per line,
the source line counter, the section program counter, the object code, and the
original source statement.
The linker has two main jobs with sections. First, it uses the input sections of the COFF object files as building blocks: when more than one file is linked, it combines input sections into the output sections of one executable COFF module. Second, it chooses the memory addresses of those output sections.
Two directives control this:
-
MEMORYdescribes the target's memory map. It names ranges and gives each a starting address (origin) and a length. -
SECTIONStells the linker how to combine the input sections and where to place the output sections.
Both directives are optional. Without them the linker falls back to the target processor's default allocation algorithm; with them, you supply the directives in a linker command file. The linker refers to program memory as PAGE 0 and to data memory as PAGE 1. Note that the linker assigns addresses to sections so that a loader can later place the code; it does not itself place object code into memory.
Under default allocation, the linker merges all input sections that share a name
into one output section of that name. Suppose file1.obj and file2.obj each
contain the .text, .data, and .bss defaults, an initialized named section
table_1, and an uninitialized named section u_vars, and file2.obj also has
an initialized named section fft:
In this arrangement the linker:
- Combines
file1 .textwithfile2 .textinto one.textoutput section, allocated first in program memory. - Combines the two
.datasections into one.dataoutput section, placed in program memory after.text. - Combines the two
table_1sections; becausetable_1is the first initialized named section it meets, it is allocated beforefft, following.data. - Allocates
fftfromfile2aftertable_1. - Combines the two
.bsssections into one.bssoutput section, allocated in data memory. - Combines the two
u_varssections into oneu_varsoutput section, placed in data memory after.bss.
With no MEMORY or SECTIONS directive, the linker reads the object file to
find the target processor and then allocates with a default model. On a
TMS320C25, for example, it places .text into program memory, .data into
program memory immediately after .text, then any initialized named sections
(in the order they are encountered), then .bss into data memory, and finally
any uninitialized named sections after .bss. Allocating a section leaves any
remaining space in a configured range configured but unused.
You need not accept the defaults. With MEMORY and SECTIONS you can split
sections apart, choose which section lands at a given address, and steer a
section into a particular kind of memory. Only ranges named in a MEMORY
directive can hold code or data; anything outside them is unconfigured, and the
linker treats unconfigured memory as nonexistent.
Because the assembler treats every section as starting at address 0, all relocatable symbols (labels) are relative to 0 within their section. The sections cannot all really start at 0, so the linker relocates them by:
- allocating each section so it begins at its proper address;
- adjusting symbol values to match the new section addresses;
- adjusting references to relocated symbols to reflect the adjusted values.
To patch references, the linker uses relocation entries. The assembler creates
one relocation entry each time a relocatable symbol is referenced; the linker
applies these entries after the symbols have been relocated. A reference may be
to a symbol defined in another module (an external reference, shown with ! in
the listing) or to a relocatable symbol defined in the current module (shown
with '). An undefined external symbol is assumed to have value 0 at assembly
time.
Each section in a COFF object file carries its own table of relocation entries,
one entry per relocatable reference in the section. The linker normally removes
the entries after applying them, which keeps the output from being relocated a
second time (on relink or on load). A file with no relocation entries is an
absolute file, where every address is absolute. The linker's -r option keeps
the relocation entries instead of discarding them.
The SECTIONS directive can allocate a section twice: once for a load address
and once for a run address, using the load and run keywords. The load
address is where a loader puts the raw data; references to the section use the
run address. Copying the section from its load address to its run address is the
application's responsibility and does not happen automatically. If only one
allocation is given, the section loads and runs at the same address. Because
uninitialized sections such as .bss are never loaded, only their run address
matters; if both are specified for one, the linker warns and ignores the load
address.
The linker produces an executable COFF module in the same format as its input object files, except that its sections are already combined and relocated into target memory. To run, the module must be loaded into target memory. Common loading methods are the debugging tools' built-in loaders (invoked with a LOAD command), conversion to another object format with a hex conversion utility so the program can be burned into EPROM, and a custom loader written for a target running an operating system or monitor.
A COFF file holds a symbol table that records information about the program's symbols. The linker reads it during relocation, and debugging tools can read it for symbolic debugging.
External symbols are defined in one module and referenced in another. Three directives mark a symbol as external:
| Directive | Meaning |
|---|---|
.def |
defined in this module, used by another module |
.ref |
referenced in this module, defined in another module |
.global |
either of the above |
A .global definition of a symbol that is defined in this module makes it
available to other modules; a .global mention of a symbol that is not defined
here marks it as supplied by another module. The assembler puts both kinds into
the symbol table. At link time, a definition entry resolves unresolved
references from other files, and a reference entry drives the linker to search
other files' symbol tables for the definition. The linker must match every
reference with a definition; an unresolved reference is an error that prevents
an executable module from being produced.
The assembler always creates a symbol table entry for an external symbol,
whether a definition or a reference. It also creates special symbols that mark
the start of each section, which the linker uses to relocate references into
that section. Other symbols are normally left out, because the linker does not
need them; for example, a plain label is not in the symbol table unless declared
.global. To record every symbol (useful for symbolic debugging), assemble with
the -s option so labels and assembly-time constants are also placed in the
table.
The c5xtools line reads and writes this same COFF format. c5xasm emits COFF
object files: it builds the .text, .data, .bss, and named sections from
the directives above, maintains a section program counter per section, and
writes the per-section relocation entries and the symbol and string tables.
c5xlnk reads those object files, combines the like-named input sections into
output sections, allocates them, resolves external symbols against the symbol
tables, applies and then removes the relocation entries, and writes an
executable COFF module. c5xhex reads a COFF module and converts it to an
object format suitable for a device programmer. The implementation targets the
COFF v1 and v2 structures documented alongside this format.
MIT - (c) 2026 Grzegorz Worona grzegorz@worona.pl. See LICENSE.
Start here
The TMS320C5x processor
Writing assembly
The instruction set
A: ABS ADCB ADD ADDB ADDC ADDH ADDI ADDK ADDS ADDT ADLK ADRK AND ANDB ANDI ANDK APAC APL APLI APLK
B: B BACC BACCD BANZ BANZD BBNZ BBZ BC BCND BCNDD BD BDDD BDSD BGEZ BGZ BIOZ BIT BITT BKDK BLDD BLDP BLEZ BLKD BLKP BLPD BLZ BNC BNV BNZ BPSD BSAR BV BZ
C: CALA CALAD CALL CALLD CC CCD CLRC CMPL CMPR CNFD CNFP CONF CPL CPLI CPLK CRGT CRLT
E: EHWBS EHWBT EINT EMPSD EMRET ESTOP ESTPD ETRAP EXAR
F: FORT
L: LAC LACB LACC LACI LACK LACL LACT LALK LAMM LAR LARK LARP LDP LDPI LDPK LMMR LPH LRLK LST LST0 LST1 LT LTA LTD LTP LTS
M: MAC MACD MADD MADS MAR MPY MPYA MPYI MPYK MPYS MPYU MRKL
O: OPL OPLI OPLK OR ORB ORI ORK OUT
R: RC RET RETC RETCD RETD RETE RETI RFSM RHM ROL ROLB ROR RORB ROVM RPT RPTB RPTI RPTK RPTR RPTZ RSXM RTC RTXM RXF
S: SACB SACH SACL SAMM SAR SATH SATL SBB SBBB SBLK SBRK SC SETC SFL SFLB SFR SFRB SFSM SHM SMMR SOVM SPAC SPH SPL SPLK SPM SQRA SQRS SST SST0 SST1 SSXM STC STXM SUB SUBB SUBC SUBH SUBI SUBK SUBS SUBT SWI SXF
X: XC XOR XORB XORI XORK XPL XPLI XPLK
Building a program
The tools
Normative sources: TI SPRU056D and SPRU018D (see References).
License: MIT - (c) 2026 Grzegorz Worona.