Linker¶
The linker is responsible for taking a number of object files and turning them into a single executable binary file. To link a program there are a few prerequisites:
produce the object files you wish to link
produce a LINK file to describe the desired layout for the linked program
link the program
Building object files¶
Object files are nothing more than individually assembled fragments. Anything you assemble ( +
) can
be stored to disk in the object format. This is done with the
:o Ex command. The linker will
specifically look for files that end in .o when it goes to link, so be sure to enter a filename
with that suffix: e.g. :o hello.o.
Your assembled program may itself specify where it should be loaded (this is what the .org directive does).
In these cases, the linker doesn’t have much work. It will, at least, ensure that all the linked
files don’t overlap.
Its real value comes when you use the .seg directive instead. The linker’s job is to find all the
code and data that was defined in the same segment and to put it together into one contiguous block.
For example, say we have two object files: a.o and b.o
a.s
.seg "CODE"
lda #$00
sta $900f
.seg "DATA"
.db "hello"
b.s
.seg "CODE"
rol $9000
.seg "DATA"
.db " world"
The linker will concatenate each segment in b.o to the corresponding ones defined in a.o. Effectively, the linked binary will correspond to something like this:
.seg "CODE"
lda #$00
sta $900f
rol $9000
.seg "DATA"
.db "hello"
.db " world"
But what physical address will “CODE” and “DATA” actually correspond to? Enter the LINK file.
LINK file format¶
The LINK file is responsible for producing the desired layout for the binary program. It contains two “blocks” of definitions for the two concepts that define how the linker performs its job of laying out the program.
MEMORY: defines the SECTION addresses, sizes, and propertiesSEGMENTS: defines how and where the SEGMENTS defined in the object code map to the memory SECTIONS.
The LINK file must always be named “LINK”. Therefore, only 1 such file may exist on a given disk. The linker loads this file before beginning the link process and uses it to initialize the layout for the final linked binary as well as define the constraints for it.
Every MEMORY section must define both START and END. Note that the END address is exclusive.
Every SEGMENTS entry must define LOAD. RUN is optional and defaults to the
same memory section as LOAD. ALIGN is optional and defaults to no alignment.
FILL is also optional and defaults to disabled.
Names must be unique within each block.
LOAD vs RUN¶
The LOAD property decides which SECTION the memory SEGMENT’s bytes are written to in the linked
binary. The RUN property tells the linker where the SEGMENT’s code will be executed at
runtime. This is useful if you have code that is loaded somewhere but copied somewhere else
before execution.
When RUN and LOAD name the same section (as is usually the case) the two layouts are
identical. When they differ, the linker places the bytes at the LOAD address
but generates all relocation data against the RUN address, so the segment can
be copied to its run address by your program (the linker does nothing to perform the actual
copy) before execution.
SEGMENTs are packed into their SECTION in the order they appear in the
SEGMENTS block: the first segment listed for a section starts at that
section’s START, and each subsequent one begins where the previous ended.
If a SEGMENT defines both a LOAD and a RUN SECTION, it occupies each. That is,
the SECTIONs that it loads and runs in are both advanced by the size of the SEGMENT.
ALIGN¶
The ALIGN property tells the linker to begin a SEGMENT on an address boundary instead of
wherever the previous SEGMENT ended. Its value is the size of the
boundary, (in decimal or, with a $ prefix, in hexadecimal). Any value in the range [1,$ffff]
is allowed.
To achieve the alignment, the linker pads the binary (fills it with 0’s) until it arrives on the next address evenly divisible by the requested boundary.
Given the SEGMENTS block
SEGMENTS [
CODE:
LOAD=ROM;
TABLE:
LOAD=ROM
ALIGN=$100;
]
and a ROM SECTION starting at $2000, $18 bytes of CODE places TABLE at
$2100 rather than $2018, and $2018-$20ff is padding (0) in the linked binary.
Alignment like this may be desirable for timing sensitive code, where you want to make sure a table stays within a single page to avoid the cycle penalty for crossing one.
Note that while padding logically applies to RUN sections as well as LOAD ones, the
LOAD one is the only one that causes the linker to emit padding bytes (remember that RUN
sections just represent the execution region at runtime).
Note
To illustrate how the LINK file functions in practice, let’s walk through an example.
LINK
MEMORY [
ROM:
START=$2000
END=$3000;
RAM:
START=$0400
END=$1000;
]
SEGMENTS [
BOOT:
LOAD=ROM;
FAST:
LOAD=ROM
RUN=RAM;
VARS:
LOAD=RAM;
]
If BOOT assembles to $100 bytes, FAST to $80, and VARS to $40, the layout is:
SEGMENT |
LOAD ADDRESS (bytes written here) |
RUN ADDRESS (relocated for this) |
|---|---|---|
BOOT |
$2000 (ROM) |
$2000 (ROM) |
FAST |
$2100 (ROM) |
$0400 (RAM) |
VARS |
$0480 (RAM) |
$0480 (RAM) |
FAST consumes $80 bytes of ROM, where its bytes are actually stored, and another
$80 bytes of RAM, where nothing is written but the address range $0400-$047f is
reserved for it.
VARS picks up where FAST’s RUN left off: it is placed at $0480 rather than at
RAM’s START, because FAST is listed first and reserved $0400-$047f ahead of
it.
Limits¶
ITEM |
LIMIT |
|---|---|
|
8 |
|
8 |
|
64 |
Object files in one link |
16 |
Imports per object file |
128 |
Exports per object file |
32 |
Note
Below is a simple LINK file example to demonstrate its configuration format
Each item (SECTION or SEGMENT) is terminated with a ; character. Note that this
does not denote a comment as it does in assembly files; comments are not valid in
the LINK file.
MEMORY [
SECTIONA:
START=$0400
END=$1000
FILL=1;
SECTIONB:
START=$1000
END=$1200;
]
SEGMENTS [
SEGA:
LOAD=SECTIONA
RUN=SECTIONB;
]
Section flags¶
In the above example, we declared the key “FILL” with the value of “1” for SECTIONA. This is called a section flag. The FILL flag tells the linker how to handle unused memory within a SECTION. The table below describes the available flags and their names.
Note that any nonzero value for these flags will enable them while the zero value disables them.
NAME |
DESCRIPTION |
|---|---|
FILL |
if ‘1’ fills unused memory in the section with 0’s |
Zero page sections¶
A SEGMENT declared with .SEGZP or .BSSZP is an address assignment only: it
reserves zero page locations for its symbols and contributes no bytes to the
linked binary. The SECTION such a SEGMENT loads into is therefore not part of
the program image, and the linker leaves it out when working out the program’s
start and end addresses. Without that, a single zero page byte would drag the
program’s start address down into the zero page, making the saved .PRG load
over the stack and KERNAL workspace on its way to your code.
For the same reason FILL is ignored on a SECTION that any zero page SEGMENT
loads into. There is nothing to pad – the SECTION contributes no bytes – and
padding it would pull the program’s start address back into the zero page.
Note that this applies to the SEGMENT’s type, not its address. A SEGMENT
declared with plain .SEG that you place below $0100 is treated like any other
SEGMENT: its bytes are part of the image and the program will start there.