Skip to content
kombos edited this page Oct 6, 2026 · 1 revision

Linker and Command Language

Normative source: SPRU018D, chapter 8 (Linker Description). The directive and option syntax described here is the linker command language; c5xlnk implements it.

The linker takes one or more COFF object files, resolves the references between them, lays their sections out in target memory, and writes a single executable COFF module. It also accepts archive libraries and linker command files as input. SPRU018D names its linker dsplnk; the invocation examples below use c5xlnk, the tool this wiki documents, but the options and command-language constructs are exactly those of the normative source.


1. Invoking the linker

The command line has the form:

    c5xlnk [-options] file1 ... filen

The input files can be object files, linker command files, or archive libraries. The default input extension is .obj; any other extension must be written out. The linker inspects each file and decides for itself whether it is an object file (which it links) or an ASCII command file (which it reads for further commands). The default output filename is a.out.

Files and options can be given three ways, and the three can be mixed:

  • On the command line. This links two object files and names the output link.out:
    c5xlnk file1.obj file2.obj -o link.out
  • Interactively. Run the linker with no arguments and it prompts for object files, command files, an output file, and options in turn.

  • From a command file. The linker reads and processes a command file the moment it meets the filename, so files pull in from the command file in the order they appear there, before any files that follow on the command line:

    c5xlnk -m link.map linker.cmd file3.obj

Options can sit anywhere on the command line or in a command file. Order does not matter except for -l and -i (see Libraries below).


2. Common options

An option is introduced by a hyphen and may be separated from its argument by a space.

Option Meaning
-a Produce an absolute, executable module. This is the default when neither -a nor -r is given.
-ar Produce an executable module that still carries relocation entries.
-b Do not merge duplicate symbolic debugging entries (faster, less memory).
-c Use the C compiler ROM autoinitialization linking conventions.
-cr Use the C compiler RAM autoinitialization linking conventions.
-e global_symbol Set the program entry point to global_symbol (must be external).
-f fill_value Default fill value for holes in output sections; a 16-bit constant.
-h Make all global symbols static (used with partial linking).
-heap size Set the C heap (.sysmem) size in words and define a size symbol. Default 1K words.
-i dir Add dir to the front of the library search path. Must precede -l.
-l filename Name an archive library as input.
-m filename Write a link map (memory configuration, section allocation, symbol addresses) to filename.
-o filename Name the output module. Default a.out.
-q Quiet run; suppress the banner (effective when first on the line or in the file).
-r Produce a relocatable output module (retains relocation entries).
-s Strip the symbol table and line-number entries from the output.
-stack size Set the C stack (.stack) size in words and define a size symbol. Default 1K words.
-u symbol Enter symbol as unresolved so a library member defining it is pulled in.
-v0 Generate the older version 0 COFF format.
-w Warn when an output section not named in SECTIONS is created.
-x Reread libraries until no more references resolve (resolves back references).

Relocation choice: -a alone gives an absolute module with no relocation information and no unresolved references; -r alone retains relocation entries and may leave unresolved references (an unexecutable file suitable for relinking); -ar gives an executable module that still carries relocation entries.

Entry point: the linker chooses, in order, the -e symbol, then _c_int0, then _main, then zero.


3. Linker command files

A command file is an ASCII file that collects linking information you would otherwise retype. It may hold input filenames, options (written exactly as on the command line), the MEMORY and SECTIONS directives, and symbol assignment statements. The MEMORY and SECTIONS directives can only appear in a command file, never on the command line.

Comments are delimited by /* and */. Blanks and blank lines are insignificant except as separators. One command file may call another as its last statement (nesting up to 16 levels); the linker does not return to the calling file.

    a.obj                     /* First input file  */
    b.obj                     /* Second input file */
    -o prog.out               /* Output file       */
    -m prog.map               /* Map file          */

The following words are reserved as directive keywords and must not be used as symbol or section names: align/ALIGN, attr/ATTR, block/BLOCK, COPY, DSECT, f/fill/FILL, group/GROUP, l/len/length/LENGTH, load/LOAD, MEMORY, NOLOAD, o/org/origin/ORIGIN, page/PAGE, range, run/RUN, SECTIONS, spare, type/TYPE, and UNION.

Constants in a command file may be written in assembler form (for example 32, 40q, 20h) or in C form (32, 040, 0x20).


4. The MEMORY directive

MEMORY describes the target's physical memory so the linker knows where object code may go. Memory you name is configured; memory you leave out is unconfigured, and the linker never places code there.

    MEMORY
    {
      PAGE 0 : name1 [(attr)] : origin = constant , length = constant ;
      PAGE n : nameN [(attr)] : origin = constant , length = constant ;
    }
Field Meaning
PAGE A memory space, up to 255 of them. PAGE 0 is usually program memory, PAGE 1 data memory. Omitting PAGE means PAGE 0. Each page is an independent address space and may overlap another page.
name One to eight characters from A-Z a-z $ . _. Names only label ranges; they are internal to the linker and are not written to the output. Within a page names must be unique and ranges must not overlap.
attr One to four optional attributes in parentheses: R readable, W writable, X can hold executable code, I can be initialized. A range with no attributes has all four.
origin Start address; may be written origin, org, or o. A 16-bit constant.
length Range length; may be written length, len, or l. A 16-bit constant.

Annotated example: 4K words of ROM at 0 in program memory, plus two data-memory blocks:

    MEMORY
    {
      PAGE 0 : ROM  : origin = 0h   , length = 1000h   /* program memory */
      PAGE 1 : B2   : origin = 60h  , length = 20h     /* data memory    */
               B0B1 : origin = 200h , length = 200h    /* data memory    */
    }

Ranges written outside a PAGE heading default to PAGE 0.


5. The SECTIONS directive

SECTIONS says how input sections are gathered into output sections, where those output sections go, and may rename them. Each entry begins with a name and a list of properties:

    SECTIONS
    {
      name : [ property , property , ... ]
    }

The properties of an output section are:

  • Load allocation: load = allocation, or just allocation, or > allocation.
  • Run allocation: run = allocation, or run > allocation.
  • Input sections: { input_sections }.
  • Section type: type = COPY, type = DSECT, or type = NOLOAD.
  • Fill value: fill = value, or a = value after the braces.

5.1 Allocation parameters

Each parameter is a keyword, an = or > sign, and a value that may be put in parentheses.

Parameter Keyword Example
Binding an address .text: load = 0x1000
Named memory > name or an attribute set .text: load > ROM
Alignment align .text: align = 0x80
Blocking block .text: block(0x80)
Page PAGE .text: PAGE 0

For the usual (load) allocation you may drop the load keyword and write just > ROM or > 0x1000. Parameters may be strung together, optionally in parentheses for readability:

    .text: > ROM align 16 PAGE 2
    .text: load = (ROM align(16) PAGE (2))

Binding fixes a section at a 16-bit address; a bound section cannot also use alignment or named memory. Named memory places a section into a MEMORY range by name or by attribute set; within the range the linker fills from low addresses and avoids fragmentation, but you cannot choose the exact spot (use binding for that). Alignment starts a section on an n-word boundary (n a power of 2). Blocking is weaker: the section fits inside one n-word block, or else starts on a block boundary. Alignment and blocking cannot be combined.

5.2 Input sections

An input-section list names the pieces that form an output section; they are concatenated in the order given. A file with no section list contributes all its sections. Input sections of a given name not mentioned anywhere are appended to the matching output section automatically.

    SECTIONS
    {
      .text :
      {
        f1.obj(.text)       /* .text from f1.obj                 */
        f2.obj(sec1)        /* sec1 from f2.obj                  */
        f3.obj              /* every section from f3.obj         */
        f4.obj(.text,sec2)  /* .text and sec2 from f4.obj        */
      }
    }

The bare form .text: is shorthand for .text: { *(.text) }, where *(.text) means every not-yet-allocated .text input section from all input files.

5.3 Annotated example

    SECTIONS
    {
      .text:   load = ROM                /* combine all .text into ROM         */
      .const:  load = ROM run = 0800h    /* load in ROM, relocate to run 0800h */
      .bss:    load = RAM                /* uninitialized, placed in RAM       */
      vectors: load = 0h                 /* bind at address 0                  */
      {
        t1.obj(.intvec1)
        t2.obj(.intvec2)
        endvec = .;                      /* symbol at end of the section       */
      }
      .data:   align = 16                /* placed anywhere, 16-word aligned   */
    }

6. Load address versus run address

A section has a load location (where a loader deposits its raw data) and a run location (the address all references to the section resolve to). Usually they are the same. To separate them, give both:

    .fir: load = ROM, run = RAM

The application itself must copy the section from its load address to its run address; the linker does not. Everything after load qualifies the load address until run appears, after which everything qualifies the run address; run may also come first. The two allocations are independent, so aligning one does not affect the other:

    .data: load = ROM, align = 32, run = RAM   /* align applies to load */
    .data: load = (ROM align 32), run = RAM    /* identical             */

Giving both load and run reserves space for the section twice; the two copies cannot overlap each other or anything else (UNION is the way to overlay). An uninitialized section (such as .bss) is never loaded: if you specify both addresses the linker warns and ignores the load address, and a single address is always treated as the run address.

A section's load address can be reached at runtime through a symbol defined with the .label assembler directive; ordinary labels relocate to the run address, .label symbols to the load address.


7. UNION and GROUP

Both conserve memory, in opposite ways.

UNION allocates several sections at the same run address, so the union takes only as much room as its largest member. Its members stay independent sections. Union affects run allocation only; sections are never overlaid for loading, so an initialized member needs its own load address.

    SECTIONS
    {
      .text: load = ROM
      UNION: run = RAM
      {
        .bss1: { file1.obj(.bss) }
        .bss2: { file2.obj(.bss) }
      }
      .bss3: run = RAM { globals.obj(.bss) }
    }

For initialized union members, specify a load address per member:

    UNION: run = RAM
    {
      .text1: load = ROM, { file1.obj(.text) }
      .text2: load = ROM, { file2.obj(.text) }
    }

GROUP forces several output sections to be allocated one after another in memory. The group as a whole can be bound, aligned, or placed in named memory like a single section:

    SECTIONS
    {
      .text
      .bss
      GROUP 1000h :         /* .data at 1000h, term_rec right after */
      {
        .data
        term_rec
      }
    }

A group may also have one run address with a separate load address for each initialized member:

    GROUP run = ONCHIP
    {
      .text : load = ROM1
      .data : load = ROM2
      .bss                  /* uninitialized, no load address */
    }

Pages are a distinct feature. Each page is a full, independent 16-bit address space defined in MEMORY, so two sections can occupy the same address on different pages. Without a PAGE qualifier, initialized sections default to PAGE 0 and uninitialized sections to PAGE 1. UNION overlays sections inside one address space; pages define several address spaces.


8. Default allocation algorithm

Without MEMORY and SECTIONS, the linker uses a device-specific default model (read from the device field in the object header). For a 'C5x device it is:

    MEMORY
    {
      PAGE 0: PROG: origin = 800h  length = 0F800h
      PAGE 1: DATA: origin = 800h  length = 0F800h
    }
    SECTIONS
    {
      .text:  PAGE = 0
      .data:  PAGE = 0
      .cinit: PAGE = 0    /* for -c and -cr */
      .bss:   PAGE = 1
    }

All .text input sections concatenate into .text, all .data into .data (both on PAGE 0); all .bss into .bss on PAGE 1. Initialized named sections follow .data in program memory; uninitialized named sections follow .bss in data memory.

An output section is formed either by a SECTIONS definition, or by combining all like-named input sections that SECTIONS does not mention. Once contents are known, the linker places sections to reduce fragmentation:

  1. Sections you bound to an address go there.
  2. Sections tied to a named range or to attributes go into the first free space in that area, honoring alignment.
  3. Everything else is placed in definition order (sections not in SECTIONS, in the order met) into the first free space, honoring alignment.

If you give no PAGE for a section it goes on PAGE 0, even when PAGE 0 is full and another page has room; to use another page name it explicitly.


9. Special section types: DSECT, COPY, NOLOAD

A type is placed in parentheses after the section definition:

    SECTIONS
    {
      sec1 2000h (DSECT)  : {f1.obj}
      sec2 4000h (COPY)   : {f2.obj}
      sec3 6000h (NOLOAD) : {f3.obj}
    }
Type Effect
DSECT A dummy section. It gets no memory and is absent from the map, may overlay other sections and unconfigured memory, and its contents, relocation, and line-number information are not written out. Its global symbols still relocate as if it had been loaded at its given address and may be referenced from elsewhere; undefined externals in it still trigger library searches.
COPY Like DSECT, but its contents and associated information are written to the output module. The C compiler's .cinit table uses this under the RAM model.
NOLOAD The linker allocates space for it and lists it in the map, but its contents, relocation, and line-number information are not written to the output.

10. Assigning symbols at link time

Assignment statements define global symbols and give them allocation-dependent values. They are processed after allocation, so a symbol used in an expression holds its final output address. Each statement ends with a semicolon:

Form Effect
symbol = expression; assign
symbol += expression; add to
symbol -= expression; subtract from
symbol *= expression; multiply
symbol /= expression; divide

If the symbol is not already external, the linker creates and enters it. The special symbol . is the current value of the section program counter during allocation; it may be used only in assignments inside a SECTIONS directive, and it refers to the current run address.

    SECTIONS
    {
      .text: {}
      .data: { Dstart = .; }    /* Dstart = start of .data */
      .bss:  {}
    }

Expression rules: operands are global symbols, constants, and the C operators below; all arithmetic is 32-bit; constants follow the assembler's rules (decimal by default, H/h hex, Q/q octal, C prefixes 0 octal and 0x hex, hex must start with a digit, no binary); a symbol contributes only its address, with no type checking; an expression is relocatable if it contains any relocatable symbol, otherwise absolute.

Precedence Operators
1 (highest) ! ~ - (unary)
2 * / %
3 + -
4 >> <<
5 == != > < <= >=
6 &
7 `
8 &&
9 `
10 (lowest) = += -= *= /=

An extra align operator aligns the SPC on the next n-word boundary (n a power of 2), usable only where . is, that is inside SECTIONS:

    . = align(16);

The linker defines these symbols automatically (reachable from assembly with .global): .text and etext (start and end of .text), .data and edata (start and end of .data), .bss and end (start and end of .bss). With -c or -cr it also defines __STACK_SIZE and __MEMORY_SIZE.


11. Creating and filling holes

An output section holds either raw data for all of it or none at all. A hole is empty space the linker is forced to leave inside an initialized output section; because the section has raw data, the hole must be given raw data too. Holes exist only inside output sections; the gaps between output sections are not holes and cannot be filled.

Create a hole by moving the SPC forward with an assignment to .:

    SECTIONS
    {
      outsect:
      {
        file1.obj(.text)
        . += 100h;        /* 256-word hole */
        file2.obj(.text)
        . = align(16);    /* hole to the next 16-word boundary */
        file3.obj(.text)
      }
    }

Assignments to . are relative to the start of the section (as if it began at address 0, even when bound). Decrementing . is illegal; the usual operators are += and align. A hole also appears when an uninitialized section is combined with an initialized one: the uninitialized part becomes a hole and gets raw data. Several uninitialized sections combined together stay uninitialized.

The linker fills a hole with a replicated 16-bit value, chosen in this order:

  1. A value written after an uninitialized member that became a hole, as file2.obj(.bss) = 0FFh.
  2. A fill = value given for the whole output section after its name.
  3. The -f value from the command line.
  4. Zero.

You can also force an uninitialized section to become raw data by giving it a fill value, for example .bss: fill = 1234h. Filling a large section or hole makes the output file large, since raw data is then generated for all of it. Every filled hole is noted in the link map with the value used.


12. Partial (incremental) linking

A linked output file can be linked again with more modules, which lets a large application be built in parts. Guidelines:

  • Link intermediate files with -r so they keep relocation information.
  • Do not use -s on intermediate files; symbolic information must survive.
  • Keep intermediate steps to forming output sections only. Do all allocation, binding, and MEMORY work in the final link.
  • Use -h on an intermediate file to make its like-named globals static so they do not clash in the final link.
  • When linking C code, use -c or -cr only in the final step, since each use makes the linker try to create an entry point.
    c5xlnk -r -o tempout1 file1.com
    c5xlnk -r -o tempout2 file2.com
    c5xlnk -m final.map -o final.out tempout1.out tempout2.out

13. Libraries

An object library is an archive of complete object files. When a library is given as input, the linker pulls in only those members that resolve currently undefined symbols. Order matters: a library satisfies only references that are undefined when it is reached, so a library may need to be listed more than once, or -x used to reread libraries until nothing more resolves. -u symbol injects an undefined symbol to force a defining member in.

The search path for a -l library is: directories from -i options (which must precede -l), then C_DIR, then A_DIR if C_DIR is unset, then the current directory.


14. Full command-file example

    -e SETUP                  /* program entry point */
    -o demo.out               /* output file         */
    -m demo.map               /* map file            */

    demo.obj
    fft.obj
    tables.obj

    MEMORY
    {
      PAGE 0: ROM:    origin = 00000h , length = 01000h
              RAM_B0: origin = 0FF00h , length = 0100h
      PAGE 1: IO:     origin = 00000h , length = 06h
              RAM_B2: origin = 00060h , length = 020h
              RAM_B1: origin = 00300h , length = 0100h
              RAM:    origin = 00400h , length = 0FC00h
    }

    SECTIONS
    {
      .text:    load = ROM, page = 0          /* code into ROM             */

      int_vecs: load = 0, page = 0            /* vectors bound at 0        */

      .data:    fill = 07A1Ch, load = RAM_B0, page = 0
      {
        tables.obj(.data)
        fft.obj(.data)
        . = 100h;                             /* hole to the end of block  */
      }                                       /* filled with 7A1Ch         */

      fftvars:  load = RAM_B2, page = 1       /* new section in B2         */
      {
        fft.obj(.bss)
      }

      .bss:     load = RAM_B1, page = 1, fill = 0FFFFh
    }

Running c5xlnk demo.cmd links the three objects into demo.out and writes demo.map. The map lists the memory configuration keyed by page, a section allocation map (output sections with their page, origin, length, and contributing input sections, with any --HOLE-- and its fill value marked), and the global symbols sorted both by name and by address.


How c5xlnk relates

c5xlnk is this toolchain's implementation of the linker command language above. It parses the MEMORY directive (named regions with origin/length/ attributes) and the SECTIONS directive (output-section specs, input-section lists, load and run allocation including the > region form, align, block, and fill). It supports PAGE 0 and PAGE 1 as independent address spaces, GROUP for contiguous allocation and UNION for shared run addresses, separate load and run addresses (load != run), and the special section types NOLOAD, COPY, and DSECT. It performs link-time symbol assignment, including the . SPC symbol and the align operator, and the automatic symbols (.text/etext, .data/edata, .bss/end). It handles archive libraries with the -l, -i, -u, and -x options and the documented search order. Its output is a COFF .out module, and with -m it writes the link map.

License

MIT - (c) 2026 Grzegorz Worona grzegorz@worona.pl. See LICENSE.

c5xtools wiki

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

D: DINT DMOV

E: EHWBS EHWBT EINT EMPSD EMRET ESTOP ESTPD ETRAP EXAR

F: FORT

I: IDLE IDLE1 IDLE2 IN INTR

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

N: NEG NMI NOP NORM

O: OPL OPLI OPLK OR ORB ORI ORK OUT

P: PAC POP POPD PSHD PUSH

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

T: TBLR TBLW TRAP TRAPD

X: XC XOR XORB XORI XORK XPL XPLI XPLK

Z: ZAC ZALH ZALR ZALS ZAP ZPR

Building a program

The tools

Normative sources: TI SPRU056D and SPRU018D (see References).

Changelog


License: MIT - (c) 2026 Grzegorz Worona.

Clone this wiki locally