Skip to content

Commit

Permalink
Implement smart linking
Browse files Browse the repository at this point in the history
  • Loading branch information
Rangi42 committed Apr 1, 2024
1 parent 1d39e5e commit 21a1eba
Show file tree
Hide file tree
Showing 35 changed files with 539 additions and 5 deletions.
1 change: 1 addition & 0 deletions contrib/bash_compl/_rgblink.bash
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ _rgblink_completions() {
[O]="overlay:glob-*.gb *.gbc *.sgb"
[o]="output:glob-*.gb *.gbc *.sgb"
[p]="pad:unk"
[s]="smart:unk"
)
# Parse command-line up to current word
local opt_ena=true
Expand Down
1 change: 1 addition & 0 deletions contrib/zsh_compl/_rgblink
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ local args=(
'(-o --output)'{-o,--output}"+[Write ROM image to this file]:rom file:_files -g '*.{gb,sgb,gbc}'"
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
'(-S --scramble)'{-s,--scramble}'+[Activate scrambling]:scramble spec'
'(-s --smart)'{-s,--smart}'+[Perform smart linking from this section]:section name:'

'*'":object files:_files -g '*.o'"
)
Expand Down
14 changes: 14 additions & 0 deletions include/link/patch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
#ifndef RGBDS_LINK_PATCH_H
#define RGBDS_LINK_PATCH_H

#include <vector>

struct Patch;
struct Section;
struct Symbol;

/*
* Checks all assertions
* @return true if assertion failed
Expand All @@ -14,4 +20,12 @@ void patch_CheckAssertions();
*/
void patch_ApplyPatches();

/**
* Executes a callback on all sections referenced by a patch's expression
* @param patch The patch to scan the expression of
*/
void patch_FindReferencedSections(
Patch const &patch, void (*callback)(Section &), std::vector<Symbol> const &fileSymbols
);

#endif // RGBDS_LINK_PATCH_H
8 changes: 8 additions & 0 deletions include/link/section.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ struct Section {
std::vector<Symbol> *fileSymbols;
std::vector<Symbol *> symbols;
std::unique_ptr<Section> nextu; // The next "component" of this unionized sect
bool smartLinked = false; // Set to true if kept by smart linking
};

struct Assertion {
Expand Down Expand Up @@ -88,4 +89,11 @@ Section *sect_GetSection(std::string const &name);
*/
void sect_DoSanityChecks();

/**
* Adds a section as a new "root" of the smart link graph
*/
void sect_AddSmartSection(std::string const &name);

void sect_PerformSmartLink();

#endif // RGBDS_LINK_SECTION_H
2 changes: 2 additions & 0 deletions include/link/symbol.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@ void sym_AddSymbol(Symbol &symbol);
*/
Symbol *sym_GetSymbol(std::string const &name);

void sym_RemoveSymbol(std::string const &name);

#endif // RGBDS_LINK_SYMBOL_H
108 changes: 108 additions & 0 deletions man/rgblink.1
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
.Op Fl o Ar out_file
.Op Fl p Ar pad_value
.Op Fl S Ar spec
.Op Fl s Ar symbol
.Ar
.Sh DESCRIPTION
The
Expand Down Expand Up @@ -105,6 +106,16 @@ See
.Sx Scrambling algorithm
below for an explanation and a description of
.Ar spec .
.It Fl s Ar sect_name , Fl Fl smart Ar sect_name
This option specifies the name of a section that will be used as a starting point for smart linking; it may appear several times per
.Nm
invocation.
See
.Sx SMART LINKING
below.
If no section with that name is found,
.Nm
will error out.
.It Fl t , Fl \-tiny
Expand the ROM0 section size from 16 KiB to the full 32 KiB assigned to ROM.
ROMX sections that are fixed to a bank other than 1 become errors, other ROMX sections are treated as ROM0.
Expand Down Expand Up @@ -192,6 +203,103 @@ to fix these so that the program will actually run in a Game Boy:
Here is a more complete example:
.Pp
.Dl $ rgblink -o bin/game.gb -n bin/game.sym -p 0xFF obj/title.o obj/engine.o
.Sh SMART LINKING
Smart linking is a feature of
.Nm
that allows "trimming the fat" off of a ROM.
It is enabled only if at least one
.Fl s
option is given on the command line.
.Pp
The smart linking process begins by finding all the
.Ql referenced
sections.
A section is referenced if its name is specified using a
.Fl s
option, or if it is referred to by a referenced section.
This definition applies recursively, so if section
.Ql A
is specified using
.Fl s Va A ,
section
.Ql A
references section
.Ql B ,
and section
.Ql B
references section
.Ql C ,
then all three sections
.Ql A ,
.Ql B ,
and
.Ql C
are referenced.
.Pp
Sections refer to each other through expressions. For example:
.Bd -literal -offset indent
SECTION "A", ROM0
db Someplace
db BANK("C")
SECTION "B", ROM0
Someplace:
SECTION "C", ROMX
db 42
.Ed
Here, section
.Ql A
references section
.Ql B
via the label
.Ql Someplace ,
and references section
.Ql C
via its name in
.Ql BANK("C") .
.Pp
After all the referenced sections are found, all sections that were not referenced are deleted, and the linking process continues as normal.
.Sy This should not cause any symbols not to be found, please report a bug (see Sx BUGS Ns Sy ) if this occurs.
.Pp
This is useful to detect
.Dq unused
sections, i.e. sections that contain data not used by anything.
Typically, the section containing the header, including the entry point at
.Ad $00:0100
will be one of the starting sections; more exotic use cases may require more starting sections.
It may be a good idea to start with the header as the only root, and if needed, add more root sections.
.Pp
Be careful, as numeric expressions do
.Sy not
cause references:
.Bd -literal -offset indent
DEF BASE_ADDR EQU $4000
SECTION "A", ROM0
dw BASE_ADDR
SECTION "B", ROMX[BASE_ADDR]
db 42
.Ed
Section
.Ql A
does
.Sy not
reference section
.Ql B ,
since
.Va BASE_ADDR
is a constant, and thus does not belong to section
.Ql B .
.Pp
Finally, be careful that
.Xr rgbasm 1
tries to fill in data by itself to speed up
.Nm Ap s
work, which may cause
.Nm
not to see references to sections whose bank and/or address are fixed.
It may be advisable to avoid fixing those (notably, to enable
.Nm
to improve section placement), but they can still be manually referenced using
.Fl s .
.Sh BUGS
Please report bugs on
.Lk https://github.com/gbdev/rgbds/issues GitHub .
Expand Down
3 changes: 3 additions & 0 deletions src/link/assign.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,9 @@ void assign_AssignSections() {

initFreeSpace();

// Check if we need to do smart linking and discard any sections
sect_PerformSmartLink();

// Generate linked lists of sections to assign
nbSectionsToAssign = 0;
sect_ForEach(categorizeSection);
Expand Down
8 changes: 6 additions & 2 deletions src/link/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ void argErr(char flag, char const *fmt, ...) {
}

// Short options
static char const *optstring = "dl:m:Mn:O:o:p:S:tVvWwx";
static char const *optstring = "dl:m:Mn:O:o:p:S:s:tVvWwx";

/*
* Equivalent long options
Expand All @@ -171,6 +171,7 @@ static option const longopts[] = {
{"output", required_argument, nullptr, 'o'},
{"pad", required_argument, nullptr, 'p'},
{"scramble", required_argument, nullptr, 'S'},
{"smart", required_argument, nullptr, 's'},
{"tiny", no_argument, nullptr, 't'},
{"version", no_argument, nullptr, 'V'},
{"verbose", no_argument, nullptr, 'v'},
Expand All @@ -183,7 +184,7 @@ static void printUsage() {
fputs(
"Usage: rgblink [-dMtVvwx] [-l script] [-m map_file] [-n sym_file]\n"
" [-O overlay_file] [-o out_file] [-p pad_value]\n"
" [-S spec] <file> ...\n"
" [-S spec] [-s symbol] <file> ...\n"
"Useful options:\n"
" -l, --linkerscript <path> set the input linker script\n"
" -m, --map <path> set the output map file\n"
Expand Down Expand Up @@ -392,6 +393,9 @@ int main(int argc, char *argv[]) {
case 'S':
parseScrambleSpec(musl_optarg);
break;
case 's':
sect_AddSmartSection(musl_optarg);
break;
case 't':
is32kMode = true;
break;
Expand Down
Loading

0 comments on commit 21a1eba

Please sign in to comment.