LD(1)
โจ LD(1) โจ
okay bestie, this is the manual page for ld, the GNU linker ๐ฆ It's from the GNU Development Tools crew, so that's the manual name and section (1) ๐
๐ NAME ๐
ld โ the GNU linker ๐
๐ง SYNOPSIS ๐ง
ld [options] objfile ...
๐ธ DESCRIPTION ๐ธ
okay so ld is the friend who combines a bunch of object and archive files, relocates their data, and ties up symbol references โจ Usually the very last step when you compile a program is running ld ๐ฅ
ld also takes Linker Command Language files, written in a superset of AT&T's Link Editor Command Language syntax ๐ This gives you explicit and total control over the linking process. literally the whole vibe is control ๐ญ
ngl this page does not cover the command language ๐ For the full tea on the command language and everything else about the GNU linker, check the ld entry in "info" ๐
This version of ld runs on the general purpose BFD libraries to work with object files ๐ That means it can read, combine, and write object files in many different formats, like COFF or "a.out". Different formats can be linked together to make any available kind of object file ๐
Besides being super flexible, the GNU linker is way more helpful than other linkers when it comes to diagnostic information ๐
A lot of linkers just quit the second they hit an error. Whenever possible, ld keeps going so you can find other errors (or, in some cases, still get an output file even with the error) ๐ซถ
The GNU linker ld is built to cover a broad range of situations and to be as compatible as possible with other linkers โจ So you get a bunch of choices to control how it behaves ๐
๐ OPTIONS ๐
okay the linker has a plethora of command-line options, but in real life you'll only touch a few of them in any given situation ๐ For example, a super common move is linking standard Unix object files on a standard, supported Unix system. On a system like that, to link a file called "hello.o":
ld -o <output> /lib/crt0.o hello.o -lc
This tells ld to make a file called output by linking "/lib/crt0.o" with "hello.o" and the library "libc.a", which comes from the standard search directories ๐ฆ (Check the discussion of the -l option below for more on that one ๐)
Some of the command-line options to ld can go anywhere on the line โจ But options that refer to files, like -l or -T, make the file get read at the point where the option shows up on the command line, relative to the object files and other file options ๐ง Repeating a non-file option with a different argument will either do nothing extra, or override earlier occurrences (the ones further left on the command line) of that option ๐
Options that can be meaningfully given more than once are noted in the descriptions below ๐
Non-option arguments are object files or archives you want linked together ๐ซถ They can come after, before, or mixed in with the options, except that an object file argument can't be stuck between an option and its argument ๐
Usually you invoke the linker with at least one object file, but you can also specify other kinds of binary input files using -l, -R, and the script command language ๐ If you don't specify any binary input files at all, the linker won't produce any output and it'll just say: No input files ๐
If the linker can't recognize the format of an object file, it'll assume it's a linker script ๐ A script passed this way augments the main linker script used for the link (either the default linker script or the one you pick with -T) โจ This lets you link against a file that looks like an object or an archive but really just defines some symbol values, or uses "INPUT" or "GROUP" to load other objects ๐ฆ Passing a script like this only augments the main linker script, with the extra commands placed after the main script. If you want to replace the default linker script entirely, use the -T option, but keep an eye on the effect of the "INSERT" command โ ๏ธ
โ ๏ธ bestie, the -T option is the one that replaces the default linker script, while passing a script without it only adds to the main one. don't mix those up ๐ญ
For options with single-letter names, the option argument has to either follow the letter with no whitespace in between, or be given as a separate argument right after the option that needs it ๐
For options with multiple-letter names, you can put either one dash or two in front of the name ๐ธ For example, -trace-symbol and --trace-symbol are equivalent. BUT there's one exception, and it's a big one ๐ Multiple-letter options that start with a lowercase 'o' can only be preceded by two dashes. This cuts down on confusion with the -o option. So -omagic sets the output file name to magic, whereas --omagic sets the NMAGIC flag on the output ๐จ
Arguments to multiple-letter options have to be separated from the option name by an equals sign, OR given as separate arguments right after the option that needs them ๐ For example, --trace-symbol foo and --trace-symbol=foo are equivalent. Unique abbreviations of multiple-letter option names are accepted too โจ
Note, though: if the linker is being invoked indirectly through a compiler driver (like gcc), then all the linker command-line options should be prefixed with -Wl, (or whatever's appropriate for your particular compiler driver) like this:
gcc -Wl,--start-group foo.o bar.o -Wl,--end-group
This is super important, because otherwise the compiler driver might silently drop the linker options and you'll get a bad link ๐ Confusion can also pop up when you pass options that need values through a driver, because a space between the option and its argument acts as a separator. That makes the driver pass only the option to the linker and the argument to the compiler ๐ญ In this case, the simplest move is to use the joined forms of both single- and multiple-letter options, like so:
gcc foo.o bar.o -Wl,-eENTRY -Wl,-Map=a.map
Here's a table, well, a list, of the generic command-line switches the GNU linker accepts ๐
@fileokay so this one reads command-line options from file ๐ The options it reads get inserted in place of the original@fileoption. If file doesn't exist or can't be read, the option gets treated literally and isn't removed ๐
Options inside file are separated by whitespace. To include a whitespace character in an option, wrap the whole option in either single or double quotes. You can include any character (including a backslash) by putting a backslash in front of it ๐
The file can also contain more @file options, and those get processed recursively ๐
-a keywordthis one's here for HP/UX compatibility ๐ฆ The keyword argument has to be one of the stringsarchive,shared, ordefault.-aarchiveis functionally equivalent to-Bstatic, and the other two keywords are functionally equivalent to-Bdynamicโจ You can use this option any number of times ๐--audit AUDITLIBadds AUDITLIB to the "DT_AUDIT" entry of the dynamic section ๐ง AUDITLIB doesn't get checked for existence, and it won't use the DT_SONAME specified in the library ๐ If you specify it multiple times, "DT_AUDIT" will hold a colon-separated list of audit interfaces to use. If the linker finds an object with an audit entry while searching for shared libraries, it'll add a matching "DT_DEPAUDIT" entry to the output file ๐ธ This option only means something on ELF platforms that support the rtld-audit interface ๐-b input-format,--format=input-formatldcan be configured to support more than one kind of object file ๐ง If yours is set up that way, use-bto specify the binary format for the input object files that follow this option on the command line. Even whenldsupports alternative formats, you usually don't need to specify this, sinceldshould be configured to expect the most usual format on each machine as its default input format ๐ซถ input-format is a text string, the name of a particular format supported by the BFD libraries. (You can list the available binary formats withobjdump -i) ๐
You might want this if you're linking files with an unusual binary format ๐ฆ You can also use -b to switch formats explicitly (when linking object files of different formats) by putting -b input-format before each group of object files in a particular format ๐
The default format comes from the environment variable "GNUTARGET" ๐
You can also define the input format from a script, using the command "TARGET"; ๐
-c MRI-commandfile,--mri-script=MRI-commandfilefor compatibility with linkers made by MRI,ldaccepts script files written in an alternate, restricted command language ๐ That's described in the MRI Compatible Script Files section of the GNU ld documentation ๐ Introduce MRI script files with the option-c, and use the-Toption to run linker scripts written in the general-purposeldscripting language ๐ If MRI-cmdfile doesn't exist,ldlooks for it in the directories specified by any-Loptions ๐-d,-dc,-dpthese three options are equivalent ๐ธ Multiple forms are supported just for compatibility with other linkers. They assign space to common symbols even if a relocatable output file is specified (with-r) โจ The script command "FORCE_COMMON_ALLOCATION" has the same effect ๐--depaudit AUDITLIB,-P AUDITLIBadds AUDITLIB to the "DT_DEPAUDIT" entry of the dynamic section ๐ง Same deal as before: AUDITLIB isn't checked for existence, and it won't use the DT_SONAME specified in the library ๐ If you specify it multiple times, "DT_DEPAUDIT" will hold a colon-separated list of audit interfaces to use ๐ This option only means something on ELF platforms that support the rtld-audit interface. The-Poption is provided for Solaris compatibility ๐ฆ--enable-linker-versionturns on the "LINKER_VERSION" linker script directive, which is described in Output Section Data ๐ If this directive is used in a linker script and this option has been enabled, a string containing the linker version gets inserted at the current point โจ
โ ๏ธ the location of this option on the linker command line matters! It only affects linker scripts that come after it on the command line, or ones that are built into the linker ๐
--disable-linker-versionturns off the "LINKER_VERSION" linker script directive, so it doesn't insert a version string ๐ This is the default ๐ซถ--enable-non-contiguous-regions๐ โ okay so this one stops the linker from throwing a fit when an input section doesn't fit its matching output section. Instead, it tries the next matching output sections in line, and only errors if none of them are big enough. Super useful when you've got a bunch of non-contiguous memory regions and the section doesn't care which one it lands in. โจ The order input sections get evaluated in stays exactly the same. Check out this example:
MEMORY {
MEM1 (rwx) : ORIGIN = 0x1000, LENGTH = 0x14
MEM2 (rwx) : ORIGIN = 0x1000, LENGTH = 0x40
MEM3 (rwx) : ORIGIN = 0x2000, LENGTH = 0x40
}
SECTIONS {
mem1 : { *(.data.*); } > MEM1
mem2 : { *(.data.*); } > MEM2
mem3 : { *(.data.*); } > MEM3
}
with input sections:
.data.1: size 8
.data.2: size 0x10
.data.3: size 4
So .data.1 gets affected to mem1, and .data.2 and .data.3 both get affected to mem2, even though .data.3 would've fit in mem3. Lowkey wild ๐
โ ๏ธ This option is incompatible with INSERT statements, because it changes the way input sections get mapped to output sections. Don't mix them, bestie ๐ญ
--enable-non-contiguous-regions-warnings๐ โ turns on warnings for when--enable-non-contiguous-regionsmakes possibly unexpected matches in sections mapping. The vibe here is that a section could get quietly discarded instead of the linker failing, just because it didn't fit any output region. Warnings catch that sneaky stuff ๐-e entry,--entry=entry๐ โ sets entry as the explicit symbol where your program starts executing, instead of the default entry point. If there's no symbol named entry, the linker tries to read entry as a number and uses that as the entry address. The number is base 10 by default, but you can add a leading0xfor base 16, or a leading0for base 8. For i386 PE, entry can also be the original function name, minus the leading underscore and/or the trailingstdcall @numberwhen that applies ๐ง--exclude-libs lib,lib,...๐ โโ๏ธ โ gives a list of archive libraries whose symbols should not get auto-exported. The library names can be separated by commas or colons. Passing--exclude-libs ALLexcludes symbols from all archive libraries from auto export. This one's only available for the i386 PE targeted port and for ELF targeted ports. On i386 PE, symbols explicitly listed in a.deffile still get exported no matter what, but on ELF targeted ports, the affected symbols get treated as hidden ๐ซฃ--exclude-modules-for-implib module,module,...๐ฆ โ lists object files or archive members whose symbols should not be auto-exported, but which should still get copied wholesale into the import library generated during the link. Module names can be separated by commas or colons and have to match exactly the filenames ld used to open the files. For archive members, that's just the member name. For object files, the name you list must include and precisely match any path you used on the linker's command line. This one's only available for the i386 PE targeted port. Symbols explicitly listed in a.deffile still get exported regardless ๐-E,--export-dynamic,--no-export-dynamic๐ธ โ when you're creating a dynamically linked executable,-Eor--export-dynamictells the linker to add all symbols to the dynamic symbol table. That table is the set of symbols visible to dynamic objects at run time.
If you skip both of these (or use --no-export-dynamic to get the default back), the dynamic symbol table will normally only hold the symbols referenced by some dynamic object mentioned in the link.
If you use dlopen to load a dynamic object that needs to refer back to symbols defined by your program (instead of some other dynamic object), you'll probably need this option when linking the program itself ๐ฆ
You can also use the dynamic list to control which symbols get added to the dynamic symbol table, if the output format supports it. See the description of --dynamic-list.
Heads up: this option is specific to ELF targeted ports. PE targets have a similar move for exporting all symbols from a DLL or EXE, which you can read about in the description of --export-all-symbols below โจ
--export-dynamic-symbol=glob๐ฏ โ when creating a dynamically linked executable, symbols matching glob get added to the dynamic symbol table. When creating a shared library, references to symbols matching glob won't be bound to the definitions inside the shared library. This is a no-op when creating a shared library unless-Bsymbolicor--dynamic-listis specified. It's only meaningful on ELF platforms that support shared libraries ๐--export-dynamic-symbol-list=file๐ โ gives a--export-dynamic-symbolfor each pattern in the file. The file format is the same as a version node without the scope and node name. Check out VERSION for more info ๐-EB๐ โ links big-endian objects. This affects the default output format.-EL๐ฃ โ links little-endian objects. This affects the default output format.-f name,--auxiliary=name๐ญ โ when creating an ELF shared object, this sets the internalDT_AUXILIARYfield to the name you give. It tells the dynamic linker that the shared object's symbol table should be used as an auxiliary filter on the symbol table of the shared object name.
If you later link a program against this filter object, then when you run the program, the dynamic linker will see the DT_AUXILIARY field. If it resolves any symbols from the filter object, it'll first check whether there's a definition in the shared object name. If there is one, that gets used instead of the definition in the filter object. The shared object name doesn't even need to exist! So it's a neat way to offer an alternative implementation of certain functions, maybe for debugging or machine-specific performance ๐
You can specify this more than once. The DT_AUXILIARY entries get created in the order they show up on the command line.
-F name,--filter=name๐ซง โ when creating an ELF shared object, this sets the internalDT_FILTERfield to the name you give. It tells the dynamic linker that the shared object being created should act as a filter on the symbol table of the shared object name.
If you later link a program against this filter object, the dynamic linker will see the DT_FILTER field when you run the program. It resolves symbols using the filter object's symbol table as usual, but it actually links to the definitions found in the shared object name. So the filter object can be used to pick a subset of the symbols provided by name.
Some older linkers used -F across a whole compilation toolchain to specify the object-file format for both input and output object files. The GNU linker uses other mechanisms for that instead: the -b, --format, --oformat options, the "TARGET" command in linker scripts, and the "GNUTARGET" environment variable. The GNU linker will ignore -F when it's not creating an ELF shared object ๐
-fini=name๐ โ when creating an ELF executable or shared object, this calls NAME when the executable or shared object gets unloaded, by settingDT_FINIto the address of the function. By default, the linker uses"_fini"as the function to call.-g๐คทโโ๏ธ โ ignored. It's only here for compatibility with other tools.-G value,--gpsize=value๐งฒ โ sets the maximum size of objects to be optimized using the GP register to size. This only matters for object file formats like MIPS ELF that support putting large and small objects into different sections. It's ignored for other object file formats.-h name,-soname=name๐ท๏ธ โ when creating an ELF shared object, this sets the internalDT_SONAMEfield to the name you give. When an executable is linked with a shared object that has aDT_SONAMEfield, the dynamic linker will try to load the shared object named inDT_SONAMEwhen you run the executable, instead of using the file name you gave the linker ๐-i๐ โ performs an incremental link (same as option-r).-init=name๐ฑ โ when creating an ELF executable or shared object, this calls NAME when the executable or shared object gets loaded, by settingDT_INITto the address of the function. By default, the linker uses"_init"as the function to call.-l namespec,--library=namespec๐ โ adds the archive or object file specified by namespec to the list of files to link. You can use this as many times as you want. If namespec is in the form:filename, ld searches the library path for a file called filename. Otherwise, it searches the library path for a file calledlibnamespec.a.
On systems that support shared libraries, ld may also search for files other than libnamespec.a. Specifically, on ELF and SunOS systems, ld searches a directory for a library called libnamespec.so before searching for one called libnamespec.a. (By convention, a .so extension means it's a shared library ๐ง) Note that this behaviour doesn't apply to :filename, which always means a file called filename.
The linker only searches an archive once, at the spot where it's specified on the command line. If the archive defines a symbol that was undefined in some object that came before the archive on the command line, the linker will include the appropriate file(s) from the archive. But an undefined symbol in an object appearing later on the command line won't make the linker search the archive again ๐ค
If you want to force the linker to search archives multiple times, check out the -( option.
You're also allowed to list the same archive multiple times on the command line ๐ซถ
Hold up, bestie, this is the tail end of the -l vibe, and it's giving "archive searching, but make it Unix" ๐
This type of archive searching is standard for Unix linkers โจ BUT if you're using ld on AIX, lowkey know that it acts different from the AIX linker ๐
-L searchdir,--library-path=searchdirโ okay this one is so useful ๐บ๏ธ It adds the path searchdir to the list of placesldchecks for archive libraries and linker control scripts. Slay it as many times as you want. The directories get searched in the order you typed them on the command line, and any directories you give on the command line get searched before the default ones. Every-Lapplies to every-l, no matter where they sit in the command, so the order doesn't matter there ๐ Also,-Loptions don't change howldlooks for a linker script unless you also use the-Toption.
If searchdir starts with = or $SYSROOT, that prefix gets swapped out for the sysroot prefix. That prefix is set with the --sysroot option, or when the linker was configured ๐ธ
The default paths (the ones ld checks when you don't use -L) depend on which emulation mode ld is in, and sometimes on how it was configured too. It's giving "it depends" energy ๐ซ
You can also add paths inside a link script with the SEARCH_DIR command. Those directories get searched right where the linker script shows up on the command line ๐ง
-m emulationโ this one makesldpretend to be a particular emulation linker ๐ To see which emulations you've got, use--verboseor-V.
If you skip -m, the emulation comes from the LDEMULATION environment variable, but only if that's actually defined ๐
If neither of those is set, the default emulation depends on how the linker was configured ๐
--remap-inputs=pattern=filename,--remap-inputs-file=fileโ main character move alert ๐ฆ These let you rename input files before the linker tries to open them. For example,--remap-inputs=foo.o=bar.omeans any attempt to loadfoo.owill actually loadbar.oinstead. Wildcards work in the first filename too, so--remap-inputs=foo*.o=bar.orenames any input file matchingfoo*.otobar.oโจ
The other way to do it is --remap-inputs-file=filename, which reads your remappings from a file. Each line can hold one remapping. Blank lines get ignored, and anything from a hash character (#) to the end of a line counts as a comment and gets ignored too ๐
The pattern and the filename can be separated by whitespace or an equals (=) sign.
You can use these options as many times as you want, and they stack up. Remappings get processed in the order they show up on the command line, and if they come from a file, in the order they're written in that file. Once something matches, no more checking happens for that filename ๐
If the replacement filename is /dev/null or just NUL, the input file gets ignored completely. This is a cute way to test what happens when you yeet input files out of a messy build setup ๐
Heads up, these options are position dependent. They only affect filenames that come after them on the command line. So this:
ld foo.o --remap-inputs=foo.o=bar.o
will have no effect, whereas this:
ld --remap-inputs=foo.o=bar.o foo.o
will rename the input file foo.o to bar.o ๐ซถ
Note: these options also affect files referenced by INPUT statements in linker scripts. But linker scripts get processed after the whole command line is read, so where the remap options sit on the command line doesn't matter for those.
If the verbose option is turned on, any mappings that match get reported. Just remember that verbose has to be enabled on the command line before the remapped filenames show up ๐ฅ
If -Map or --print-map is enabled, the remapping list gets included in the map output too ๐บ๏ธ
-M,--print-mapโ this prints a link map to the standard output, and it's basically the tea on your whole link ๐ต A link map tells you about:- Where object files get mapped into memory.
- How common symbols get allocated.
- All the archive members pulled into the link, plus the symbol that caused each one to get dragged in.
- The values assigned to symbols.
Note: symbols whose values are computed by an expression that references a previous value of the same symbol might not show the right result in the link map ๐ญ The linker throws away intermediate results and only keeps the final value of an expression. In that case, the linker shows the final value wrapped in square brackets. So a linker script containing:
foo = 1
foo = foo * 4
foo = foo + 8
will produce this in the link map if -M is used:
0x00000001 foo = 0x1
[0x0000000c] foo = (foo * 0x4)
[0x0000000c] foo = (foo + 0x8)
Check out Expressions for more info on expressions in linker scripts ๐
- How GNU properties get merged.
When the linker merges input .note.gnu.property sections into one output .note.gnu.property section, some properties get removed or updated, and the link map reports those actions. For example:
Removed property 0xc0000002 to merge foo.o (0x1) and bar.o (not found)
This means property 0xc0000002 gets removed from the output while merging properties in foo.o, whose property 0xc0000002 value is 0x1, and bar.o, which doesn't have property 0xc0000002 at all.
Updated property 0xc0010001 (0x1) to merge foo.o (0x1) and bar.o (0x1)
This means the property 0xc0010001 value gets updated to 0x1 in the output while merging foo.o, whose 0xc0010001 property value is 0x1, and bar.o, whose 0xc0010001 property value is also 0x1.
- On some ELF targets, a list of fixups inserted by
--relax:
foo.o: Adjusting branch at 0x00000008 towards "far" in section .text
This means the branch at 0x00000008 in foo.o, which targets the symbol "far" in section .text, got replaced by a trampoline ๐ฆ
--print-map-discarded,--no-print-map-discardedโ prints (or doesn't print) the list of discarded and garbage collected sections in the link map. It's on by default, so you only need the--no-version if you want it gone ๐งน--print-map-locals,--no-print-map-localsโ prints (or doesn't print) local symbols in the link map. Local symbols get the word(local)in front of their name, and they're listed after all the global symbols in a given section. Temporary local symbols (usually the ones starting with.L) stay out of the output ๐ This one is off by default.-n,--nmagicโ turns off page alignment of sections and disables linking against shared libraries ๐ If the output format supports Unix style magic numbers, the output gets marked as"NMAGIC".-N,--omagicโ makes the text and data sections readable and writable ๐ It also stops page-aligning the data segment and disables linking against shared libraries. If the output format supports Unix style magic numbers, the output gets marked as"OMAGIC". Note: a writable text section is allowed for PE-COFF targets, but it doesn't follow the format spec Microsoft published, so be careful ๐ซ--no-omagicโ this one basically undoes most of what-Ndoes ๐ It sets the text section back to read-only and forces the data segment to be page-aligned. Note: this option does not turn on linking against shared libraries. For that, use-Bdynamic๐-o output,--output=outputโ names the programldproduces as output ๐ If you don't use this option, the default name isa.out. TheOUTPUTscript command can also set the output file name.
Note: the linker deletes the output file before it starts writing to it. It does this even if the link ends up failing because of errors, so no cap, don't panic when your old binary vanishes ๐ญ
Also, the linker checks that the output file name doesn't match any of the input file names, and that's all it checks. It won't complain if the output file might overwrite a source file or some other important file ๐ So in build systems, it's recommended to put the -o option last on the linker command line. For example, consider:
ld -o $(EXE) $(OBJS)
ld $(OBJS) -o $(EXE)
If the EXE variable isn't defined for some reason, the first version could end up deleting one of the object files (the first one in the OBJS list), whereas the second version will throw an error message and delete nothing ๐ฅบ
--dependency-file=depfileโ writes a dependency file to depfile ๐ง This file has a rule thatmakecan read, describing the output file and every input file that got read to produce it. The output is similar to the compiler's output with-M -MP. There's no equivalent of the compiler's-MMto exclude "system files" (that's not a well-specified concept in the linker, unlike "system headers" in the compiler), so the output from--dependency-fileis always specific to the exact state of the installation where it was made. Don't copy it into distributed makefiles without careful editing โ ๏ธ-O levelโ if level is a numeric value greater than zero,ldoptimizes the output โจ This might take significantly longer, so it probably should only be turned on for the final binary. Right now, this option only affects ELF shared library generation. Future releases of the linker may make more use of it. Also, there's currently no difference in the linker's behaviour between different non-zero values of this option, and that might change in future releases too ๐ธ
-plugin name โ okay so this one lets you slide a plugin into the linking process ๐ The name is the absolute filename of the plugin. Usually the compiler adds it automatically when you're doing link time optimization, but you're totally allowed to bring your own plugins if you're feeling iconic ๐โจ
Heads up tho, the compiler's plugins live somewhere different from where ar, nm and ranlib look for theirs ๐ So if you want those commands to use a compiler based plugin, you gotta copy it into ${libdir}/bfd-plugins first. The good news is every gcc based linker plugin is backward compatible, so copying in just the newest one is enough ๐
--push-stateโ this one is a total lifesaver ๐ซถ It saves the current state of the flags that control how input files get handled, so they can all be brought back later with one matching--pop-state. The flags it covers are:-Bdynamic,-Bstatic,-dn,-dy,-call_shared,-non_shared,-static,-N,-n,--whole-archive,--no-whole-archive,-r,-Ur,--copy-dt-needed-entries,--no-copy-dt-needed-entries,--as-needed,--no-as-needed, and-a.- Pkg-config specs are a big reason this exists ๐ง When you use the
--libsoption, it lists every library that might be needed and then links all of them all the time, which is giving chaos ๐ So it's better to write it like this:
- Pkg-config specs are a big reason this exists ๐ง When you use the
-Wl,--push-state,--as-needed -libone -libtwo -Wl,--pop-state
--pop-stateโ undoes the effect of--push-stateand restores the previous values of the flags that govern input file handling ๐-q,--emit-relocsโ keeps the relocation sections and their contents in fully linked executables ๐ Post link analysis and optimization tools might need this info to make correct changes to executables. Fair warning, it makes your executables bigger ๐ It's currently only supported on ELF platforms.--force-dynamicโ forces the output file to have dynamic sections ๐ฆ This one is specific to VxWorks targets, so it's not for everyone.-r,--relocatableโ generates relocatable output, aka an output file that can be fed right back intoldas input ๐ This is often called partial linking. In environments that support standard Unix magic numbers, it also sets the output file's magic number to "OMAGIC" โจ If you don't use this option, you get an absolute file instead. When linking C++ programs, this won't resolve references to constructors, so if you need that, use-Ur๐- If an input file isn't the same format as the output, partial linking only works when that input has no relocations at all ๐ญ Some "a.out"-based formats don't support partial linking with input files in other formats at all, so be careful bestie.
- When the relocatable output has both stuff that needs link-time optimization (LTO) and stuff that doesn't, a
.gnu_object_onlysection gets created to hold a relocatable object file. It's as if-rgot applied to all the relocatable inputs that don't need LTO ๐ธ When the linker processes a relocatable input that has a.gnu_object_onlysection, it pulls that section out as its own separate input. - Be warned: since
-rgroups some sections from different input files together, it can hurt code size and locality in the final executable or shared library ๐ฅบ - This option does the same thing as
-i.
-R filename,--just-symbols=filenameโ reads symbol names and their addresses from filename, but doesn't relocate it or include it in the output ๐ง This lets your output file symbolically point at absolute memory locations defined in other programs. You can use this option more than once ๐- For compatibility with other ELF linkers, if you follow
-Rwith a directory name instead of a file name, it gets treated as the-rpathoption ๐๏ธ
- For compatibility with other ELF linkers, if you follow
--rosegment,--no-rosegmentโ tries to make sure only a single read-only, non-code segment gets created ๐ This is only useful together with the-z separate-codeoption, and the binaries should come out smaller than if you used-z separate-codeby itself. Without this option, or if you pass--no-rosegment,-z separate-codecreates two read-only segments: one before the code segment and one after it.- The names are kinda misleading, ngl ๐ But they were picked so the linker stays compatible with the LLD and GOLD linkers.
- These options are only supported by ELF targets.
-s,--strip-allโ omits all symbol information from the output file ๐ฅ-S,--strip-debugโ omits debugger symbol information (but not all symbols) from the output file ๐ช--strip-discarded,--no-strip-discardedโ omits (or doesn't omit) global symbols defined in discarded sections ๐๏ธ This one is enabled by default, so you get it unless you say otherwise.-plugin-save-tempsโ stores the plugin "temporary" intermediate files permanently ๐พ-t,--traceโ prints the names of the input files asldprocesses them ๐ If you give-ttwice, members inside archives get printed too. The-toutput is super useful for generating a list of all the object files and scripts involved in a link, like when you're packaging stuff up for a linker bug report ๐-T scriptfile,--script=scriptfileโ uses scriptfile as the linker script ๐ This one replaces ld's default linker script instead of adding to it, unless the script contains "INSERT", so your commandfile has to spell out everything needed to describe the output file.- If scriptfile isn't in the current directory, "ld" goes looking for it in the directories given by any
-Loptions that come before it ๐ - Command line options that show up before
-Tcan affect the script, but ones that show up after it don't ๐ โโ๏ธ - Multiple
-Toptions will pile up if they're augmenting the current script. Otherwise, the last non-augmenting-Toption is the one that gets used ๐ - There are other ways of specifying linker scripts. See
- If scriptfile isn't in the current directory, "ld" goes looking for it in the directories given by any
-dT scriptfile,--default-script=scriptfileโ uses scriptfile as the default linker script ๐ง This is kinda like--script, except the processing of the script gets delayed until after the rest of the command line has been handled. That means options placed after--default-scriptcan change how the linker script behaves ๐ช This matters a lot when the command line isn't directly in your control, like when another tool (such as gcc) is building it for you.-u symbol,--undefined=symbolโ forces symbol to be entered in the output file as an undefined symbol ๐ฆ Doing this can trigger linking of extra modules from standard libraries, for example. You can repeat-uwith different arguments to add more undefined symbols. This option is equivalent to the "EXTERN" linker script command.- If you're using this to force extra modules into the link, and it would be an error for the symbol to stay undefined, use
--require-definedinstead ๐ซถ
- If you're using this to force extra modules into the link, and it would be an error for the symbol to stay undefined, use
--require-defined=symbolโ requires that symbol is defined in the output file ๐ก๏ธ It's the same as--undefined, except if symbol isn't defined in the output file, the linker throws an error and exits. You can get the same effect in a linker script by using "EXTERN", "ASSERT" and "DEFINED" together. You can use this option multiple times to require more symbols.-Urโ for programs that don't use constructors or destructors, or for ELF based systems, this is equivalent to-r: it generates relocatable output, aka an output file that can serve as input toldagain ๐ For other binaries, though,-Uris similar to-rbut it also resolves references to constructors and destructors โจ- On systems where
-rand-Urbehave differently, you can't use-Uron files that were already linked with-Ur. Once the constructor table has been built, it can't be added to ๐ญ So use-Uronly for the last partial link, and-rfor the others.
- On systems where
--orphan-handling=MODEโ controls how orphan sections get handled ๐ An orphan section is one that isn't specifically mentioned in a linker script.MODEcan be any of these values:- "place" โ orphan sections get placed into a suitable output section, following the strategy described in Orphan Sections. The
--uniqueoption also affects how sections are placed. - "discard" โ all orphan sections get thrown out by placing them in the
/DISCARD/section ๐๏ธ - "warn" โ the linker places the orphan section like "place" does, and also issues a warning โ ๏ธ
- "error" โ the linker exits with an error if any orphan section is found ๐
- "place" โ orphan sections get placed into a suitable output section, following the strategy described in Orphan Sections. The
- If you don't give
--orphan-handling, the default is "place" โจ
--unique[=SECTION]โ creates a separate output section for every input section matching SECTION. If the optional wildcard SECTION argument is missing, it does this for every orphan input section instead. An orphan section is one not specifically mentioned in a linker script ๐งฉ You can use this option multiple times on the command line. It stops the normal merging of input sections with the same name, and it overrides output section assignments in a linker script ๐ โโ๏ธ-v,--version,-Vโ displays the version number forld๐-Valso lists the supported emulations. Check out the description of--enable-linker-versionin Options,,Command-line Options too, which can insert the linker version string into a binary ๐-x,--discard-allโ deletes all local symbols ๐งน-X,--discard-localsโ deletes all temporary local symbols ๐ซง (These symbols start with system-specific local label prefixes, typically.Lfor ELF systems orLfor traditional a.out systems.)-y symbol,--trace-symbol=symbolโ prints the name of each linked file that symbol shows up in ๐ต๏ธโโ๏ธ You can give this option any number of times. On many systems you need to prepend an underscore to the symbol name ๐ธ
This one is a lifesaver when you've got an undefined symbol in your link but no clue where the reference is coming from ๐ฅน
-Y pathโ okay so this one addspathto the default library search path ๐บ๏ธ It's only here for Solaris compatibility, so it's a little nostalgic. ๐ธ-z keywordโ bestie, this is the big-zbuffet ๐ฐ These are the recognized keywords:call-nop=prefix-addr,call-nop=suffix-nop,call-nop=prefix-byte,call-nop=suffix-byteโ these set the 1-byte "NOP" padding when the linker transforms an indirect call to a locally defined function,foo, through its GOT slot ๐ฆcall-nop=prefix-addrgives you0x67 call foo,call-nop=suffix-nopgives youcall foo 0x90,call-nop=prefix-bytegives youbyte call foo, andcall-nop=suffix-bytegives youcall foo byte. Supported for i386 and x86_64. โจcet-report=none,cet-report=warning,cet-report=errorโ this decides how missingGNU_PROPERTY_X86_FEATURE_1_IBTandGNU_PROPERTY_X86_FEATURE_1_SHSTKproperties get reported in the input.note.gnu.propertysection ๐cet-report=noneis the default, so the linker stays quiet about missing properties.cet-report=warningmakes it issue a warning, andcet-report=errormakes it issue an error. Heads up:ibtturns off the missingGNU_PROPERTY_X86_FEATURE_1_IBTreport, andshstkturns off the missingGNU_PROPERTY_X86_FEATURE_1_SHSTKreport ๐ Supported for Linux/i386 and Linux/x86_64.combreloc,nocombrelocโcombrelocmerges multiple dynamic relocation sections and sorts them so dynamic symbol lookup caching gets a glow-up ๐nocombrelocmeans don't do that.common,nocommonโ during a relocatable link,commongenerates common symbols with theSTT_COMMONtype ๐ Withnocommon, you get theSTT_OBJECTtype instead.common-page-size=valueโ sets the most commonly used page size tovalue. If the system uses pages of that size, the memory image layout gets optimized to minimize memory pages ๐งdefsโ reports unresolved symbol references from regular object files ๐ This happens even when the linker is making a non-symbolic shared library. It's the opposite of-z undefs.dynamic-undefined-weak,nodynamic-undefined-weakโdynamic-undefined-weakmakes undefined weak symbols dynamic when building a dynamic object, as long as they're referenced from a regular object file and aren't forced local by symbol visibility or versioning ๐ธnodynamic-undefined-weakmeans don't make them dynamic. If you pass neither, a target might default to either one being in force, or pick its own way of making some undefined weak symbols dynamic. Not every target supports these, fyi.execstackโ marks the object as requiring an executable stack ๐globalโ only meaningful when building a shared object ๐ It makes the symbols defined by this shared object available for symbol resolution of libraries loaded afterwards.globalauditโ only meaningful when building a dynamic executable ๐ต๏ธโโ๏ธ This marks the executable as requiring global auditing by setting the"DF_1_GLOBAUDIT"bit in the"DT_FLAGS_1"dynamic tag. Global auditing means any auditing library defined through the--depauditor-Pcommand-line options has to run for every dynamic object the application loads.ibtpltโ generates Intel Indirect Branch Tracking (IBT) enabled PLT entries ๐ฆ Supported for Linux/i386 and Linux/x86_64.ibtโ generatesGNU_PROPERTY_X86_FEATURE_1_IBTin the.note.gnu.propertysection to say "yep, I'm IBT compatible" ๐ It also impliesibtplt. Supported for Linux/i386 and Linux/x86_64.indirect-extern-access,noindirect-extern-accessโindirect-extern-accessgeneratesGNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESSin the.note.gnu.propertysection, which tells everyone the object file needs canonical function pointers and can't be used with copy relocation ๐ซถ It also impliesnoextern-protected-dataandnocopyreloc. Supported for i386 and x86-64.noindirect-extern-accessremovesGNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESSfrom the.note.gnu.propertysection ๐งนinitfirstโ only meaningful when building a shared object โญ It marks the object so its runtime initialization happens before the runtime initialization of any other objects brought into the process at the same time. Likewise, its runtime finalization happens after the finalization of any other objects.interposeโ tells the dynamic loader to change its symbol search order so symbols in this shared library interpose all the other shared libraries that aren't marked this way ๐unique,nouniqueโ when generating a shared library or another dynamically loadable ELF object,uniquemarks it as one that should (by default) only ever be loaded once, and only in the main namespace when using"dlmopen"๐ This is mainly for fundamental libraries like libc and libpthread, which don't usually work right unless they're the only instances of themselves. The"dlmopen"caller can override this behaviour, and it doesn't apply to certain loading mechanisms, like audit libraries.nouniqueis the opposite.lam-u48โ generatesGNU_PROPERTY_X86_FEATURE_1_LAM_U48in the.note.gnu.propertysection to show compatibility with Intel LAM_U48 ๐ Supported for Linux/x86_64.lam-u57โ generatesGNU_PROPERTY_X86_FEATURE_1_LAM_U57in the.note.gnu.propertysection to show compatibility with Intel LAM_U57 ๐ Supported for Linux/x86_64.lam-u48-report=none,lam-u48-report=warning,lam-u48-report=errorโ controls how the missingGNU_PROPERTY_X86_FEATURE_1_LAM_U48property gets reported in the input.note.gnu.propertysection ๐ฃlam-u48-report=noneis the default and keeps the linker quiet about it.lam-u48-report=warningmakes the linker issue a warning, andlam-u48-report=errormakes it issue an error. Supported for Linux/x86_64.lam-u57-report=none,lam-u57-report=warning,lam-u57-report=errorโ same vibe, but for the missingGNU_PROPERTY_X86_FEATURE_1_LAM_U57property ๐ฃlam-u57-report=noneis the default,lam-u57-report=warninggives a warning, andlam-u57-report=errorgives an error. Supported for Linux/x86_64.lam-report=none,lam-report=warning,lam-report=errorโ this one handles the missingGNU_PROPERTY_X86_FEATURE_1_LAM_U48andGNU_PROPERTY_X86_FEATURE_1_LAM_U57properties in the input.note.gnu.propertysection ๐ซงlam-report=noneis the default and stays silent,lam-report=warningissues a warning, andlam-report=errorissues an error. Supported for Linux/x86_64.lazyโ when generating an executable or shared library, this tells the dynamic linker to defer function call resolution until the function is actually called (lazy binding), instead of doing it at load time ๐ด Lazy binding is the default, so you're already getting it.loadfltrโ specifies that the object's filters get processed immediately at runtime โกmax-page-size=valueโ sets the maximum supported memory page size tovalue๐mark-plt,nomark-pltโmark-pltmarks PLT entries with the dynamic tagsDT_X86_64_PLT,DT_X86_64_PLTSZandDT_X86_64_PLTENT๐ท๏ธ Heads up, this one is a bit spicy: since it stores a non-zero value in ther_addendfield ofR_X86_64_JUMP_SLOTrelocations, the resulting executables and shared libraries are incompatible with dynamic linkers like the ones in older versions of glibc that didn't get the change to ignorer_addendinR_X86_64_GLOB_DATandR_X86_64_JUMP_SLOTrelocations. Those older linkers don't ignore ther_addendfield ofR_X86_64_JUMP_SLOTrelocations. โ ๏ธ Supported for x86_64.nomark-pltis the "no thanks" option.muldefsโ allows multiple definitions ๐คทโโ๏ธnocopyrelocโ disables the linker-generated.dynbssvariables that get used in place of variables defined in shared libraries ๐ โโ๏ธ This may result in dynamic text relocations.nodefaultlibโ specifies that the dynamic loader, when searching for this object's dependencies, should ignore any default library search paths ๐ซnodeleteโ specifies that the object shouldn't be unloaded at runtime ๐งทnodlopenโ specifies that the object isn't available to"dlopen"๐nodumpโ specifies that the object can't be dumped by"dldump"๐noexecstackโ marks the object as not requiring an executable stack ๐noextern-protected-dataโ stops the linker from treating protected data symbols as external when building a shared library ๐ท This overrides the linker backend default. It can be used to work around incorrect relocations against protected data symbols that the compiler generates. Heads up: updates on protected data symbols made by another module won't be visible to the resulting shared library. Supported for i386 and x86-64.noreloc-overflowโ disables the relocation overflow check ๐งจ You can use this if there won't be any dynamic relocation overflow at run-time. Supported for x86_64.memory-seal,nomemory-sealโmemory-sealtells the executable or shared library that allPT_LOADsegments should be sealed so nobody can mess with them later, like changing the protection flags, the segment size, or removing the mapping ๐ It's a security hardening feature that needs system support, and it generatesGNU_PROPERTY_MEMORY_SEALin the.note.gnu.propertysection.nomemory-sealis the opt-out ๐ฆ
-z nowโ Okay so this one tells the dynamic linker to resolve every symbol right away when the program starts up, or when a shared library gets loaded bydlopen, instead of waiting until a function is called for the first time. It only applies when you're generating an executable or shared library. ๐ฅ-z originโ Marks the object as needing$ORIGINhandling in paths. ๐ธ-z pack-relative-relocs,-z nopack-relative-relocsโ Generates compact relative relocations in position-independent executables and shared libraries. It addsDT_RELR,DT_RELRSZandDT_RELRENTentries to the dynamic section. It gets ignored when you're building position-dependent executables and relocatable output.nopack-relative-relocsis the default, which turns compact relative relocation off. When you link against the GNU C Library, aGLIBC_ABI_DT_RELRsymbol version dependency on the shared C Library gets added to the output. Supported for i386 and x86-64. โจ-z relro,-z norelroโ Creates an ELFPT_GNU_RELROsegment header in the object. This marks a memory segment that should become read-only after relocation, if the system supports it. Heads up: setting common-page-size smaller than the system page size makes this protection totally ineffective. ๐norelromeans noPT_GNU_RELROsegment gets created.-z report-relative-relocโ Reports the dynamic relative relocations the linker generated. Supported for Linux/i386 and Linux/x86_64. ๐ฆ-z sectionheader,-z nosectionheaderโ Generates the section header.nosectionheadermeans no section header gets generated.sectionheaderis the default, so you're getting it unless you ask otherwise. ๐-z separate-code,-z noseparate-codeโ Creates a separate codePT_LOADsegment header in the object. This is a memory segment that should contain only instructions and has to sit in wholly disjoint pages from any other data.noseparate-codemeans no separate codePT_LOADsegment gets created. ๐-z shstkโ GeneratesGNU_PROPERTY_X86_FEATURE_1_SHSTKin the.note.gnu.propertysection, which signals compatibility with Intel Shadow Stack. Supported for Linux/i386 and Linux/x86_64. ๐ซถ-z stack-size=valueโ Sets a stack size for an ELFPT_GNU_STACKsegment. Be careful here: specifying zero overrides any default non-zero sizedPT_GNU_STACKsegment creation. ๐-z start-stop-gc,-z nostart-stop-gcโ Okay this one is kind of a lot, bestie ๐ญ When--gc-sectionsis in effect, a reference from a retained section to__start_SECNAMEor__stop_SECNAMEcauses all input sections namedSECNAMEto also get retained. This only happens ifSECNAMEis representable as a C identifier and the linker synthesizes either__start_SECNAMEor__stop_SECNAME.-z start-stop-gcturns this effect off, so sections can get garbage collected as if the special synthesized symbols were never defined. It has no effect on a definition of__start_SECNAMEor__stop_SECNAMEin an object file or linker script. Such a definition stops the linker from providing a synthesized__start_SECNAMEor__stop_SECNAMErespectively, which means the special garbage collection treatment for those references doesn't happen either. ๐ง-z start-stop-visibility=valueโ Sets the ELF symbol visibility for the synthesized__start_SECNAMEand__stop_SECNAMEsymbols.valuemust be exactlydefault,internal,hidden, orprotected. If you don't give a-z start-stop-visibilityoption,protectedgets used for compatibility with historical practice. However, it's highly recommended to use-z start-stop-visibility=hiddenin new programs and shared libraries, so these symbols don't get exported between shared objects. That's usually not what's intended. ๐-z text,-z notext,-z textoffโ Reports an error ifDT_TEXTRELis set. That means the position-independent or shared object has dynamic relocations in read-only sections.notextandtextoffmean no error gets reported. ๐-z undefsโ Stops the linker from reporting unresolved symbol references from regular object files, whether you're creating an executable or a shared library. This is the inverse of-z defs. โญ-z unique-symbol,-z nounique-symbolโ Avoids duplicated local symbol names in the symbol string table. If you useunique-symbol, a"."number""gets appended to duplicated local symbol names.nounique-symbolis the default. ๐-z x86-64-baseline,-z x86-64-v2,-z x86-64-v3,-z x86-64-v4โ Specifies the x86-64 ISA level needed in the.note.gnu.propertysection.x86-64-baselinegeneratesGNU_PROPERTY_X86_ISA_1_BASELINE.x86-64-v2generatesGNU_PROPERTY_X86_ISA_1_V2.x86-64-v3generatesGNU_PROPERTY_X86_ISA_1_V3.x86-64-v4generatesGNU_PROPERTY_X86_ISA_1_V4. Supported for Linux/i386 and Linux/x86_64. ๐-z isa-level-report=none,-z isa-level-report=all,-z isa-level-report=needed,-z isa-level-report=usedโ Decides how x86-64 ISA levels get reported in input relocatable files.isa-level-report=noneis the default, and it stops the linker from reporting x86-64 ISA levels in input files.isa-level-report=allmakes the linker report both needed and used x86-64 ISA levels in input files.isa-level-report=neededreports only the needed levels, andisa-level-report=usedreports only the used levels. Supported for Linux/i386 and Linux/x86_64. ๐ธ
Other keywords are ignored for Solaris compatibility.
โจ OPTIONS โจ
-(archives-),--start-grouparchives--end-groupโ The archives should be a list of archive files, either explicit file names or-loptions. The linker searches the specified archives repeatedly until no new undefined references get created. Normally an archive only gets searched once, in the order it's specified on the command line. So if a symbol in that archive is needed to resolve an undefined symbol referenced by an object in a later archive on the command line, the linker won't be able to resolve it. Grouping the archives means they all get searched repeatedly until every possible reference is resolved. ๐
Using this option has a significant performance cost, so it's best to use it only when there are unavoidable circular references between two or more archives. ๐ข
--accept-unknown-input-arch,--no-accept-unknown-input-archโ Tells the linker to accept input files whose architecture can't be recognised. The assumption is that you know what you're doing and deliberately want to link in these unknown input files. This was the linker's default behaviour before release 2.14. From release 2.14 onwards the default is to reject such input files, so--accept-unknown-input-archgot added to bring back the old behaviour. ๐ฐ๏ธ--as-needed,--no-as-neededโ This option affects ELFDT_NEEDEDtags for dynamic libraries mentioned on the command line after--as-needed. Normally the linker adds aDT_NEEDEDtag for every dynamic library on the command line, whether or not it's actually needed.--as-neededmeans aDT_NEEDEDtag only gets emitted for a library that, at that point in the link, satisfies a non-weak undefined symbol reference from a regular object file. Or, if the library isn't found in theDT_NEEDEDlists of other needed libraries, it can satisfy a non-weak undefined symbol reference from another needed dynamic library. Object files or libraries appearing after the library in question on the command line don't affect whether it's seen as needed. This works similarly to the rules for extracting object files from archives.--no-as-neededrestores the default behaviour. ๐ง
Note: On Linux based systems the--as-neededoption also affects the behaviour of the--rpathand--rpath-linkoptions. Check the description of--rpath-linkfor more details.
--add-needed,--no-add-neededโ These two are deprecated, because their names look way too similar to--as-neededand--no-as-needed. ๐ฌ They've been replaced by--copy-dt-needed-entriesand--no-copy-dt-needed-entries.-assert keywordโ This option is ignored for SunOS compatibility. ๐-Bdynamic,-dy,-call_sharedโ Links against dynamic libraries. This only means something on platforms that support shared libraries, and it's normally the default on those platforms. The different variants are there for compatibility with various systems. You can use this option multiple times on the command line, and it affects library searching for any-loptions that come after it. ๐ถ-Bgroupโ Sets theDF_1_GROUPflag in theDT_FLAGS_1entry in the dynamic section. This makes the runtime linker perform lookups in this object and its dependencies only inside the group.--unresolved-symbols=report-allis implied. This option is only meaningful on ELF platforms that support shared libraries. ๐ซง-Bstatic,-dn,-non_shared,-staticโ Doesn't link against shared libraries. This only matters on platforms that support shared libraries, and the different variants are for compatibility with various systems. You can use this option multiple times on the command line, and it affects library searching for any-loptions that come after it. It also implies--unresolved-symbols=report-all. This option can be used with-shared, which means a shared library is being created, but all of the library's external references must be resolved by pulling in entries from static libraries. ๐ชจ-Bsymbolicโ When creating a shared library, binds references to global symbols to the definition inside the shared library, if there is one. Normally a program linked against a shared library can override the definition inside that library. This option is only meaningful on ELF platforms that support shared libraries. ๐-Bsymbolic-functionsโ okay so when you're making a shared library ๐ฆ, this tells the linker to bind references to global function symbols to the definition inside that same shared library, if there is one. It only matters on ELF platforms that support shared libraries ๐-Bno-symbolicโ the cancel button ๐ โโ๏ธ It undoes any earlier-Bsymbolicand-Bsymbolic-functionsyou set.--dynamic-list=dynamic-list-fileโ lets you hand the linker the name of a dynamic list file ๐โจ It's usually for two things: when you're making shared libraries and want a list of global symbols that should not get bound to the definition inside the library, or when you're making dynamically linked executables and want a list of symbols that should be added to the executable's symbol table. Same as before, this only matters on ELF platforms that support shared libraries. The format of the list is the same as a version node without the scope and node name, so check outVERSIONfor the deets ๐--dynamic-list-dataโ pulls every global data symbol into the dynamic list ๐--dynamic-list-cpp-newโ provides the builtin dynamic list for C++operator newanddelete๐ง It's mainly useful when you're building a sharedlibstdc++.--dynamic-list-cpp-typeinfoโ provides the builtin dynamic list for C++ runtime type identification ๐ธ--check-sections,--no-check-sectionsโ by default the linker double-checks section addresses after they've been assigned, looking for overlaps ๐ If it finds any, it spits out helpful error messages.--no-check-sectionstells it to skip that check. The linker does know about overlays and makes allowances for them, so no drama there ๐ซถ Heads up: overlap isn't usually checked for relocatable links, but you can force the check with--check-sections. And the default behaviour can be restored by using the command-line switch--check-sections๐--copy-dt-needed-entries,--no-copy-dt-needed-entriesโ this one is all about dynamic libraries that are referenced byDT_NEEDEDtags inside ELF dynamic libraries you mention on the command line ๐ Normally the linker won't add aDT_NEEDEDtag to the output binary for every library listed in aDT_NEEDEDtag of an input dynamic library. But if you pass--copy-dt-needed-entries, then any dynamic libraries that come after it get theirDT_NEEDEDentries added. Go back to the default with--no-copy-dt-needed-entriesโจ
This also changes how symbols get resolved in dynamic libraries ๐ With --copy-dt-needed-entries, the dynamic libraries on the command line get searched recursively, following their DT_NEEDED tags to other libraries, so the linker can resolve symbols the output binary needs. With the default setting, the search stops at the dynamic library itself, and no DT_NEEDED links get traversed to resolve symbols.
--crefโ outputs a cross reference table ๐ If you're generating a linker map file, the table gets printed into the map file. Otherwise it shows up on standard output. It's intentionally simple so a script can chew through it if needed. The symbols are printed sorted by name, and for each one you get a list of file names. If the symbol is defined, the first file listed is where the definition lives. If it's defined as a common value, any files where that happens come next. And finally, you get every file that references the symbol ๐ฆ--ctf-variables,--no-ctf-variablesโ the CTF debuginfo format has a section that stores the names and types of variables in your program that don't show up in any symbol table ๐ Those variables can't be looked up by address with regular debuggers, so the space for their types and names is usually wasted. The types are usually small, but the names often aren't.--ctf-variablesmakes the linker generate that section anyway, and--no-ctf-variablesgets you back to the default ๐--ctf-share-types=methodโ adjusts how types get shared between translation units in CTF ๐งฉ There are two methods:share-unconflictedโ puts every type that doesn't have an ambiguous definition into the shared dictionary, where debuggers can easily reach them, even if they only show up in one translation unit. This is the default ๐share-duplicatedโ only puts types that appear in multiple translation units into the shared dictionary. Types with only one definition go into per-translation-unit dictionaries instead. Types with ambiguous definitions across multiple translation units always go into per-translation-unit dictionaries. This tends to make the CTF bigger, but it may shrink what's in the shared dictionary. For very large projects it can speed up opening the CTF and save memory in the CTF consumer at runtime ๐
--no-define-commonโ stops the linker from assigning addresses to common symbols ๐ The script commandINHIBIT_COMMON_ALLOCATIONdoes the exact same thing. This option lets you decouple the choice to assign addresses to Common symbols from the choice of output file type. Normally a non-Relocatable output type forces the linker to assign addresses to Common symbols anyway. With--no-define-common, Common symbols referenced from a shared library only get addresses in the main program ๐ That kills the unused duplicate space in the shared library, and it also prevents any confusion about resolving to the wrong duplicate when there are lots of dynamic modules with specialized search paths for runtime symbol resolution.--force-group-allocationโ makes the linker place section group members like normal input sections, and deletes the section groups themselves ๐ง That's already the default for a final link, but you can use this option to change the behaviour of a relocatable link (-r). The script commandFORCE_GROUP_ALLOCATIONhas the same effect.--defsym=symbol=expressionโ creates a global symbol in the output file, containing the absolute address given byexpression๐ You can use this as many times as you want to define multiple symbols on the command line. The expression gets a limited form of arithmetic: you can give a hexadecimal constant or the name of an existing symbol, and use+and-to add or subtract hexadecimal constants or symbols. If you need fancier expressions, use the linker command language from a script instead ๐ Also, there should be no white space between symbol, the equals sign (=), and expression. ๐ โโ๏ธ
Order matters here ๐ฏ The linker processes --defsym and -T arguments in order. Put --defsym before -T and the symbol gets defined before the linker script from -T is processed. Put it after -T and the symbol gets defined after the script has been processed. This affects expressions inside the linker script that use the --defsym symbols, so which order is correct depends on what you're trying to achieve.
--demangle[=style],--no-demangleโ these control whether symbol names get demangled in error messages and other output โจ When demangling is on, the linker tries to make symbol names readable: it strips leading underscores if the object file format uses them, and converts C++ mangled symbol names into user-readable names. Different compilers have different mangling styles, so the optional style argument lets you pick one that matches your compiler. By default the linker demangles unless the environment variableCOLLECT_NO_DEMANGLEis set. You can use these options to override that default ๐ฆ-Ifile,--dynamic-linker=fileโ sets the name of the dynamic linker ๐ This only matters when you're generating dynamically linked ELF executables. The default dynamic linker is normally correct, so don't touch this unless you really know what you're doing ๐--no-dynamic-linkerโ when you're producing an executable file, this leaves out the request for a dynamic linker to be used at load-time ๐ธ It only matters for ELF executables that contain dynamic relocations, and it usually needs entry point code that can actually process those relocations.--embedded-relocsโ works like--emit-relocs, except the relocs get stored in a target-specific section ๐ This option is only supported by theBFIN,CR16andM68Ktargets.--disable-multiple-abs-defsโ doesn't allow multiple definitions of symbols that are included in the file invoked by-Ror--just-symbols๐ โโ๏ธ--fatal-warnings,--no-fatal-warningsโ treats every warning as an error ๐ค If you want to go back to the default, use--no-fatal-warnings๐-w,--no-warningsโ hides every warning and error message ๐ This overrides--fatal-warningsif it's been turned on. It's handy when you already know the output binary won't work but you still need to create it anyway ๐--force-exe-suffixโ makes sure the output file ends with a.exesuffix ๐ช If a successfully built, fully linked output file doesn't have a.exeor.dllsuffix, this forces the linker to copy the output to a file with the same name plus a.exesuffix. It's super useful when you're using unmodified Unix makefiles on a Microsoft Windows host, since some versions of Windows won't run an image unless it ends in.exe๐--gc-sections,--no-gc-sectionsโ enables garbage collection of unused input sections ๐๏ธโจ It gets ignored on targets that don't support this option. The default behaviour (which is not doing this garbage collection) comes back if you specify--no-gc-sectionson the command line. Heads up: garbage collection for COFF and PE format targets is supported, but the implementation is currently considered to be experimental โ ๏ธ๐ฅบ--gc-sectionsdecides which input sections get to stick around by checking symbols and relocations. ๐งน The section holding the entry symbol is kept, and so is every section with symbols that are undefined on the command line. Sections with symbols referenced by dynamic objects stay too. Heads up: when you're building shared libraries, the linker has to assume that any visible symbol is referenced. ๐ซถ Once that first set is locked in, the linker recursively marks as used any section that gets referenced by their relocations. See--entry,--undefined, and--gc-keep-exported.
This option can be set during a partial link (turned on with -r). ๐ฆ In that case, the roots of the symbols you want to keep must be spelled out explicitly, either with one of --entry, --undefined, or --gc-keep-exported, or with an "ENTRY" command in the linker script. No guessing allowed bestie ๐
As a GNU extension, ELF input sections marked with the "SHF_GNU_RETAIN" flag will not be garbage collected. ๐
--print-gc-sections,--no-print-gc-sectionsโ this one is giving receipts ๐งพ It lists every section that got removed by garbage collection, and the listing gets printed on stderr. It only works if garbage collection is turned on with--gc-sections. If you want to go back to the default (no list of removed sections), just pass--no-print-gc-sectionson the command line.--gc-keep-exportedโ when--gc-sectionsis on, this one stops unused input sections from being garbage collected if they contain global symbols with default or protected visibility. ๐ก๏ธ It's meant for executables, where unreferenced sections would otherwise get collected no matter how visible their symbols are. Lowkey useful. It has no effect when linking shared objects, because that's already the default behaviour. And it's only supported for ELF format targets. ๐ธ--print-output-formatโ prints the name of the default output format, which might be influenced by your other command-line options. ๐ It's the same string that would show up in an"OUTPUT_FORMAT"linker script command.--print-memory-usageโ prints the used size, total size and used size of the memory regions created with theMEMORYcommand. ๐ง This is a lifesaver on embedded targets when you want a quick look at how much free memory you've got left. The output has one headline and one line per region, and it's both human readable and easy for tools to parse. Here's what an example looks like:
Memory region Used Size Region Size %age Used
ROM: 256 KB 1 MB 25.00%
RAM: 32 B 2 GB 0.00%
Note: if you want to find out about the memory usage of the linker itself, the --stats option will do that for you. ๐ค
--helpโ prints a summary of the command-line options to standard output, then exits. ๐--target-helpโ prints a summary of all the target-specific options to standard output, then exits. ๐-Map=mapfileโ prints a link map to the file mapfile. Check out the description of the-Moption up above for the full story. โจ If mapfile is just-, the map gets written to stdout.
Okay, so, if you point this at a directory, the linker map gets written as a file inside that directory. ๐ Normally the file name inside the directory is the basename of the output file with ".map" tacked on the end. But if you use the special character %, it gets replaced by the full path of the output file instead. And if there are any characters after the % symbol, then ".map" won't get appended anymore. Iykyk ๐
-o foo.exe -Map=bar [Creates ./bar]
-o ../dir/foo.exe -Map=bar [Creates ./bar]
-o foo.exe -Map=../dir [Creates ../dir/foo.exe.map]
-o ../dir2/foo.exe -Map=../dir [Creates ../dir/foo.exe.map]
-o foo.exe -Map=% [Creates ./foo.exe.map]
-o ../dir/foo.exe -Map=% [Creates ../dir/foo.exe.map]
-o foo.exe -Map=%.bar [Creates ./foo.exe.bar]
-o ../dir/foo.exe -Map=%.bar [Creates ../dir/foo.exe.bar]
-o ../dir2/foo.exe -Map=../dir/% [Creates ../dir/../dir2/foo.exe.map]
-o ../dir2/foo.exe -Map=../dir/%.bar [Creates ../dir/../dir2/foo.exe.bar]
It is an error to specify more than one % character. ๐ Don't do it bestie.
If the map file already exists, it will get overwritten by this operation. ๐ซ
--no-keep-memoryโ ld normally goes for speed over memory usage, caching the symbol tables of input files in memory. ๐๏ธ This option tells ld to optimize for memory instead, by rereading the symbol tables as necessary. You might need this if ld runs out of memory space while linking a large executable. ๐ฅน--no-undefined,-z defsโ reports unresolved symbol references from regular object files. ๐จ It does this even if the linker is creating a non-symbolic shared library. The switch--[no-]allow-shlib-undefinedcontrols how unresolved references found in shared libraries being linked in get reported. And if you change your mind, the effects of this option can be reverted by using-z undefs.--allow-multiple-definition,-z muldefsโ normally, if a symbol gets defined more than once, the linker reports a fatal error. ๐ค These options allow multiple definitions, and the first definition is the one that gets used.--allow-shlib-undefined,--no-allow-shlib-undefinedโ allows or disallows undefined symbols in shared libraries. ๐ This is kind of like--no-undefined, except it decides what happens when the undefined symbols live in a shared library instead of a regular object file. It doesn't change how undefined symbols in regular object files are handled, so don't mix those up ๐ โโ๏ธ
By default, the linker reports errors for any undefined symbols referenced in shared libraries when it's making an executable. But when it's making a shared library, it just allows them. ๐ฆ
So why would anyone want undefined symbol references in shared libraries specified at link time? Two big reasons:
- A shared library specified at link time might not be the same one that's available at load time, so the symbol could actually be resolvable at load time. ๐ง
- Some operating systems, eg BeOS and HPPA, have undefined symbols in shared libraries as the norm. ๐คฏ
The BeOS kernel, for example, patches shared libraries at load time to select whichever function is most appropriate for the current architecture. โจ This is used, for example, to dynamically select an appropriate memset function. Main character energy from the kernel honestly ๐
--error-handling-script=scriptnameโ if you give this option, the linker will invoke scriptname whenever it hits an error. ๐ ๏ธ Right now only two kinds of error are supported: missing symbols and missing libraries. The script gets two arguments: either the keyword"undefined-symbol"ormissing-lib", plus the name of the undefined symbol or missing library. The whole point is that the script can give the user suggestions about where the symbol or library might be found. ๐ Once the script finishes, the normal linker error message gets displayed. Also, this option's availability is controlled by a configure time switch, so it might not be present in every implementation. Not every build has it, babe ๐ฅฒ--no-undefined-versionโ normally, when a symbol has an undefined version, the linker just ignores it. ๐ This option disallows symbols with an undefined version, and a fatal error is issued instead.--default-symverโ creates and uses a default symbol version (the soname) for unversioned exported symbols. ๐--default-imported-symverโ creates and uses a default symbol version (the soname) for unversioned imported symbols. ๐ง--no-warn-mismatchโ normally ld will throw an error if you try to link together input files that don't match for some reason, like being compiled for different processors or different endiannesses. ๐ง This option tells ld to silently permit those possible errors. โ ๏ธ Use it with care, and only when you've taken some special action that makes the linker errors inappropriate. Don't be reckless ๐ซถ--no-warn-search-mismatchโ normally ld gives a warning if it finds an incompatible library during a library search. This option silences that warning. ๐คซ--no-whole-archiveโ turns off the effect of--whole-archivefor subsequent archive files. ๐--noinhibit-execโ retains the executable output file whenever it's still usable. ๐พ Normally the linker won't produce an output file if it runs into errors during the link process. It exits without writing an output file whenever it issues any error whatsoever. So this one is for when you want the file anyway ๐ฅบ-nostdlibโ only searches library directories that were explicitly specified on the command line. ๐ Library directories specified in linker scripts (including linker scripts specified on the command line) get ignored. Totally ignored, no cap ๐--oformat=output-formatโ ld may be configured to support more than one kind of object file. ๐งฉ If yours is set up that way, you can use--oformatto specify the binary format for the output object file. Even when ld supports alternative object formats, you usually don't need to specify this, because ld should be configured to produce the most usual format on each machine as the default. output-format is a text string, the name of a particular format supported by the BFD libraries. (You can list the available binary formats withobjdump -i. ๐) The script command"OUTPUT_FORMAT"can also specify the output format, but this option overrides it. Override energy ๐--out-implib fileโ creates an import library in file that matches the executable the linker is generating (eg. a DLL or ELF program). ๐ This import library, which should be called"*.dll.a"or"*.a"for DLLs, can be used to link clients against the generated executable. This means you can skip a separate import library creation step (eg."dlltool"for DLLs). โจ This option is only available for the i386 PE and ELF targetted ports of the linker. Very niche, very specific, very iconic ๐
okay so this part is where the options get spicy ๐ถ๏ธ bestie, pay attention ๐
-pie,--pic-executableโ okay this one makes a position independent executable ๐ฆ It's only supported on ELF platforms rn. The dynamic linker relocates it to whatever virtual address the OS picks, and that address can change between runs โจ It's marked ET_DYN in the ELF file header, but it's not a shared library, it's giving different energy in a bunch of ways. The big one: defined symbols in a PIE by default can't be overridden by another object, unlike in a shared library ๐-no-pieโ makes a position dependent executable instead ๐ This is the default, so you literally don't need to ask for it ๐-qmagicโ ignored, purely for Linux compatibility ๐ง It does nothing, babe.-Qyโ also ignored, but this one's for SVR4 compatibility ๐ง--relax,--no-relaxโ this one has machine dependent effects, so it's a bit of a vibe check ๐ฅ It's only supported on a few targets. On some platforms,--relaxdoes target specific, global optimizations once the linker has resolved addressing in the program. We're talking relaxing address modes, synthesizing new instructions, picking shorter versions of current instructions, and combining constant values. Lowkey genius ๐ง
Heads up though: on some platforms these link time global optimizations can make symbolic debugging of the resulting executable impossible ๐ญ This is known to be the case for the Matsushita MN10200 and MN10300 family of processors.
Where the feature is supported, --no-relax turns it off. Where it isn't supported, both --relax and --no-relax are accepted and just ignored, no drama โจ
--retain-symbols-file=filenameโ keeps only the symbols listed in the filefilenameand tosses everything else ๐๏ธfilenameis just a flat file with one symbol name per line. This is super useful in environments like VxWorks, where a huge global symbol table builds up gradually, so it helps conserve run-time memory ๐
--retain-symbols-file does not discard undefined symbols, or symbols needed for relocations, so those stay safe ๐ซถ
You can only specify --retain-symbols-file once on the command line. It overrides -s and -S, so it's the boss ๐
-rpath=dirโ adds a directory to the runtime library search path ๐ธ This is used when linking an ELF executable with shared objects. All-rpatharguments get concatenated and passed to the runtime linker, which uses them to find shared objects at runtime.
The -rpath option is also used when locating shared objects that are needed by shared objects explicitly included in the link. See the description of the -rpath-link option for that one ๐ฆ This way of searching -rpath is only supported by native linkers and cross linkers that were configured with the --with-sysroot option.
If -rpath isn't used when linking an ELF executable, the contents of the environment variable "LD_RUN_PATH" get used, as long as it's defined ๐
-rpath also works on SunOS. By default there, the linker forms a runtime search path out of all the -L options it's given. But if you use a -rpath option, the runtime search path gets formed exclusively from the -rpath options, and the -L options get ignored. This is handy when using gcc, since gcc adds a ton of -L options that might be on NFS mounted file systems ๐
For compatibility with other ELF linkers: if the -R option is followed by a directory name instead of a file name, it gets treated as -rpath ๐
-rpath-link=dirโ okay this is the one that gets complicated ๐ญ With ELF or SunOS, one shared library may need another. That happens when an"ld -shared"link includes a shared library as one of the input files.
When the linker hits this kind of dependency during a non-shared, non-relocatable link, it'll automatically try to find the required shared library and include it, if it isn't already included explicitly. Several directories get searched for that, as described below. The -rpath-link option specifies the first set of directories to search. You can give a sequence of directory names either as a list separated by colons, or by writing the option multiple times โจ
The tokens $ORIGIN and $LIB can show up in these search directories. $ORIGIN gets replaced by the full path to the directory containing the program or shared object. $LIB gets replaced by either lib - for 32-bit binaries or lib64 - for 64-bit binaries ๐
The alternative form of these tokens, ${ORIGIN} and ${LIB}, works too. The token $PLATFORM is not supported, sorry bestie ๐ฅฒ
Use --rpath-link with caution!! It overrides the search path that may have been hard compiled into a shared library. So you could accidentally end up using a different search path than the runtime linker would have used ๐
When more shared libraries are needed, the linker searches directories in the order listed below to find them. Important bit: this only applies to additional libraries needed to satisfy shared libraries that are already included. It does not apply to libraries pulled in with the -l command line option. Searches for -l libraries only happen in directories specified by the -L option ๐ซถ
- Any directories specified by
-rpath-linkoptions. - Any directories specified by
-rpathoptions. The difference between-rpathand-rpath-linkis that directories given with-rpathget included in the executable and used at runtime, whereas-rpath-linkonly matters at link time. Searching-rpaththis way is only supported by native linkers and cross linkers configured with the--with-sysrootoption. - On an ELF system, for native linkers, if neither
-rpathnor-rpath-linkwas used, search the contents of the environment variable"LD_RUN_PATH". - On SunOS, if
-rpathwasn't used, search any directories specified with-Loptions. - For a native linker, search the contents of the environment variable
"LD_LIBRARY_PATH". - For a native ELF linker, the directories in
"DT_RUNPATH"or"DT_RPATH"of a shared library get searched for shared libraries that it needs. The"DT_RPATH"entries get ignored if"DT_RUNPATH"entries exist โจ - For a linker on a Linux system: if the file
/etc/ld.so.confexists, search the list of directories found in that file. Note: the path to this file gets prefixed with the"sysroot"value, if that's defined, and then with any"prefix"string if the linker was configured with the--prefix=<path>option ๐ง - For a native linker on a FreeBSD system, search any directories specified by the
"_PATH_ELF_HINTS"macro defined in the elf-hints.h header file. - Any directories specified by a
"SEARCH_DIR"command in a linker script given on the command line, including scripts specified by-T(but not-dT). - The default directories, normally
/liband/usr/lib. - Any directories specified by a plugin's
LDPT_SET_EXTRA_LIBRARY_PATH. - Any directories specified by a
"SEARCH_DIR"command in a default linker script.
Okay, but there's an extra caveat on Linux based systems ๐ง If the --as-needed option is active, and a shared library gets located that would normally satisfy the search, and that library does not have a DT_NEEDED tag for libc.so, AND there's a shared library later in the set of search directories that also satisfies the search and does have a DT_NEEDED tag for libc.so, then the second library gets picked instead of the first. Chaotic, I know ๐ญ
If the required shared library isn't found, the linker issues a warning and keeps going with the link โ ๏ธ
--section-ordering-file=scriptโ this one augments the current linker script with extra mapping of input sections to output sections ๐ธ The file has to use the same syntax for"SECTIONS"as normal linker scripts do, but it should only place input sections into output sections and nothing else.@pxref{SECTIONS}
Second constraint: the section ordering script can only reference output sections that are already defined by whichever linker script is currently in use (the default linker script, or a script given on the command line). The perk is that input sections get mapped to the start of the output sections, so they can control the ordering inside them โจ Say the default linker script looks like this:
SECTIONS {
.text : { *(.text.hot) ; *(.text .text.*) }
.data : { *(.data.big) ; *(.data .data.*) }
}
And then you use a section ordering file like this:
.text : { *(.text.first) ; *(.text.z*) }
.data : { foo.o(.data.first) ; *(.data.small) }
That's giving the same result as a linker script like this:
SECTIONS {
.text : { *(.text.first) ; *(.text.z*) ; *(.text.hot) ; *(.text .text.*) }
.data : { foo.o(.data.first) ; *(.data.small) ; *(.data.big) ; *(.data .data.*) }
}
The advantage of the section ordering file is that you can order just the sections you care about, without having to worry about every other section, memory region, or anything else ๐
-shared,-Bshareableโ creates a shared library ๐ This is currently only supported on ELF, XCOFF and SunOS platforms. On SunOS, the linker automatically creates a shared library if the-eoption isn't used and there are undefined symbols in the link ๐ฆ--sort-common,--sort-common=ascending,--sort-common=descendingโ tells ld to sort the common symbols by alignment, in ascending or descending order, when it places them in the appropriate output sections โจ The alignments considered are sixteen-byte or larger, eight-byte, four-byte, two-byte, and one-byte. This prevents gaps between symbols caused by alignment constraints ๐งฉ If you don't specify a sorting order, descending order is assumed ๐--sort-section=nameโ applies"SORT_BY_NAME"to all wildcard section patterns in the linker script ๐
โจ OPTIONS โจ (cont.)
okay so bestie, this next bit is all about how the linker handles sections, stats, and symbols ๐
--sort-section=alignmentโ this one tells the linker to applySORT_BY_ALIGNMENTto every wildcard section pattern in the linker script ๐ฆ--spare-dynamic-tags=countโ sets how many empty slots get left in the.dynamicsection of ELF shared objects ๐ those slots are handy for post processing tools, like the prelinker. the default is5โจ--split-by-file[=size]โ kinda like--split-by-reloc, but it makes a brand new output section for each input file once size is reached ๐ธ if you don't give size, it defaults to1๐--split-by-reloc[=count]โ this one tries to make extra sections in the output file so no single section has more than count relocations ๐ฅ it's super useful when you're making huge relocatable files for downloading into certain real time kernels that use the COFF object file format, since COFF can't hold more than65535relocations in one section ๐ญ heads up: this will fail with object file formats that don't support arbitrary sections ๐ and the linker won't split up individual input sections, so if one input section has more than count relocations, its output section will have that many too ๐ count defaults to32768--stats[=filename]โ computes and shows stats about how the linker is doing, like execution time and memory usage ๐- no filename? you just get basic info, sent to the standard error output stream ๐
- give it a filename and you get the extended info written to that file โจ
- if filename is just
-, the extended info goes to the standard output stream ๐ซถ - if filename starts with
+, the file gets opened in append mode instead of overwrite mode ๐ง - > Note: if both
--statsand-Maphave filename arguments and they match, the info only gets written once, not twice ๐ - if the
LD_STATSenvironment variable is defined, this behaves like--stats๐ if its value is a string, that string becomes the name of the file the info gets recorded in. otherwise it goes to the standard output stream. using the env var means you can record stats without touching the linker's command line ๐ฅ - > Note: if both the env var and
--statsare used,--statswins ๐ - -Map: if
-Mapis enabled, the info gets recorded in the map file too ๐ฆ - the extended info includes cpu time, and if the
getrusage()system library call is available, memory use gets recorded too ๐ this info is reported for individual parts of the linking process, called phases ๐ธ there's also a special phase calledALLthat covers the entire linking process โจ - careful: phases can contain or overlap with each other, so you can't assume the overall resources used by the linker is the sum of the phases ๐
- extended info also includes the linker version, command line arguments, and linker start time ๐ this is clutch when a build system is running multiple links, so you can figure out exactly which arguments made the stats you're looking at ๐ซถ
- here's what the extended output looks kinda like:
Stats: linker version: (GNU Binutils) 2.44.50.20250401
Stats: linker started: Wed Apr 2 09:36:41 2025
Stats: args: ld -z norelro -z nomemory-seal -z no-separate-code -o a.out [...]
Stats: phase cpu time memory user time system time
Stats: name (microsec) (KiB) (seconds) (seconds)
Stats: ALL 390082 217740 0 0
Stats: ctf processing 12 0 0 0
Stats: string merge 1324 0 0 0
Stats: parsing 349 288 0 0
Stats: plugins 1 0 0 0
Stats: processing files 259616 214524 0 0
Stats: write 116493 0 0 0
--no-statsโ turns off the usage stats reporting, if it was turned on by the--statsoption or theLD_STATSenv var ๐ โโ๏ธ--sysroot=directoryโ uses directory as the sysroot location, overriding the default set at configure time ๐บ๏ธ only linkers configured with--with-sysrootsupport this one ๐--task-linkโ used by COFF/PE based targets to make a task-linked object file, where all the global symbols get turned into statics ๐--traditional-formatโ for some targets, ld's output is different in some ways from an existing linker's output ๐ญ this switch tells ld to use the traditional format instead ๐- example: on SunOS, ld combines duplicate entries in the symbol string table, which can shrink an output file with full debugging info by over
30percent โจ but the SunOS "dbx" program can't read that result (gdb has no trouble tho ๐ ) so--traditional-formattells ld not to combine duplicate entries ๐ซถ
- example: on SunOS, ld combines duplicate entries in the symbol string table, which can shrink an output file with full debugging info by over
--section-start=sectionname=orgโ places a section in the output file at the absolute address org ๐ use this as many times as you need to place multiple sections ๐ฆ org has to be a single hexadecimal integer, but for compatibility with other linkers you can skip the leading0x๐ง- > Note: there should be no white space between sectionname, the equals sign (
=), and org ๐
- > Note: there should be no white space between sectionname, the equals sign (
--image-base=orgโ for ELF, this is the same as-Ttext-segment, and both set the base address of the ELF executable ๐- for PE, value is the base address of your program or dll ๐ฅ it's the lowest memory location used when your program or dll is loaded ๐ธ to cut down on relocating and boost dll performance, each dll should have a unique base address that doesn't overlap any other dll โจ the default is
0x400000for executables and0x10000000for dlls ๐
- for PE, value is the base address of your program or dll ๐ฅ it's the lowest memory location used when your program or dll is loaded ๐ธ to cut down on relocating and boost dll performance, each dll should have a unique base address that doesn't overlap any other dll โจ the default is
-Tbss=org,-Tdata=org,-Ttext=orgโ same as--section-start, just with.bss,.data, or.textas the sectionname ๐-Ttext-segment=orgโ when making an ELF executable, this sets the address of the first byte of the first segment ๐ heads up: if you use-piewith-Ttext-segment=org, the output executable gets markedET_EXEC, so the first byte of the text segment is guaranteed to be org at run time ๐ญ-Trodata-segment=orgโ when making an ELF executable or shared object for a target where the read-only data lives in its own segment separate from the executable text, this sets the address of the first byte of the read-only data segment ๐ฆ-Tldata-segment=orgโ when making an ELF executable or shared object for the x86-64 medium memory model, this sets the address of the first byte of the ldata segment ๐ง--unresolved-symbols=methodโ decides how unresolved symbols get handled ๐ there are four possible values for method:ignore-allโ don't report any unresolved symbols ๐report-allโ report all unresolved symbols. this is the default ๐ignore-in-object-filesโ report unresolved symbols contained in shared libraries, but ignore them if they come from regular object files ๐ธignore-in-shared-libsโ report unresolved symbols from regular object files, but ignore them if they come from shared libraries ๐ this can be super handy when making a dynamic binary and you know all the shared libraries it should reference are on the linker's command line โจ- the behaviour for shared libraries on their own can also be controlled by the
--[no-]allow-shlib-undefinedoption ๐ซถ - normally the linker throws an error message for each reported unresolved symbol, but
--warn-unresolved-symbolscan turn those into warnings instead โ ๏ธ
--dll-verbose,--verbose[=NUMBER]โ shows the version number for ld and lists the linker emulations it supports ๐ฃ it also shows which input files can and can't be opened, and the linker script being used ๐ if the optional NUMBER is greater than1, the plugin symbol status gets displayed too ๐ฅ--version-script=version-scriptfileโ tells the linker the name of a version script ๐งพ this is usually used when making shared libraries, to add extra info about the version hierarchy for the library ๐ it's only fully supported on ELF platforms that support shared libraries (see VERSION) ๐ it's partially supported on PE platforms, which can use version scripts to filter symbol visibility in auto-export mode: any symbols marked local in the version script won't get exported ๐--warn-commonโ warns when a common symbol gets combined with another common symbol or with a symbol definition ๐จ unix linkers allow this slightly sloppy practice, but linkers on some other operating systems don't ๐ญ this option helps you find potential problems from combining global symbols ๐ unfortunately some C libraries do this, so you might get warnings about symbols in the libraries as well as in your programs ๐
there are three kinds of global symbols, shown here with C examples:
int i = 1;โ a definition, which goes in the initialized data section of the output file ๐extern int i;โ an undefined reference, which doesn't allocate space. there must be either a definition or a common symbol for the variable somewhere ๐ง
int i;
okay so this is a common symbol ๐ If there's only one or more common symbols for a variable, it gets tucked into the uninitialized data area of the output file ๐ง The linker merges multiple common symbols for the same variable into one single symbol ๐ซถ If they're different sizes, it picks the largest one, no cap ๐ฅ And if there's an actual definition for that same variable, the linker turns the common symbol into a declaration ๐ธ
The --warn-common option can produce five kinds of warnings ๐ญ Each warning comes as a pair of lines. The first line describes the symbol you just ran into, and the second describes the previous symbol that had the same name ๐ At least one of those two symbols will be a common symbol, periodt.
- Turning a common symbol into a reference, because there's already a definition for the symbol ๐
<file>(<section>): warning: common of `<symbol>'
overridden by definition
<file>(<section>): warning: defined here
- Turning a common symbol into a reference, because a later definition for the symbol shows up ๐ฆ It's the same as the one above, just with the symbols showing up in a different order โจ
<file>(<section>): warning: definition of `<symbol>'
overriding common
<file>(<section>): warning: common is here
- Merging a common symbol with a previous common symbol of the same size ๐
<file>(<section>): warning: multiple common
of `<symbol>'
<file>(<section>): warning: previous common is here
- Merging a common symbol with a previous, larger common symbol ๐
<file>(<section>): warning: common of `<symbol>'
overridden by larger common
<file>(<section>): warning: larger common is here
- Merging a common symbol with a previous, smaller common symbol ๐ This is the same as the one above, except the symbols show up in a different order ๐
<file>(<section>): warning: common of `<symbol>'
overriding smaller common
<file>(<section>): warning: smaller common is here
๐ --warn-constructors ๐
Warns you if any global constructors get used ๐ง This is only useful for a few object file formats, though. For formats like COFF or ELF, the linker literally can't detect when global constructors are being used ๐
โจ --warn-execstack, --warn-execstack-objects, --no-warn-execstack โจ
okay so on ELF platforms, the linker may generate warning messages if you ask it to make an output file with an executable stack ๐ญ There are three possible states:
- Don't generate any warnings at all ๐ซถ
- Always generate warnings, even if the executable stack is requested with the
-z execstackcommand line option ๐ฅ - Only generate a warning if an object file requests an executable stack, but not if the
-z execstackoption is used ๐ฆ
The default state depends on how the linker was configured when it was built ๐ The --no-warn-execstack option always puts the linker into the no-warnings state ๐ The --warn-execstack option puts it into the warn-always state ๐ And the --warn-execstack-objects option puts it into the warn-for-object-files-only state ๐
Note: ELF input files can say they need an executable stack by having a.note.GNU-stacksection with the executable bit set in its section flags ๐ญ They can say they don't need one by having that same section without the executable flag bit set. If an input file doesn't have a.note.GNU-stacksection, the default behaviour is target specific ๐ For some targets, a missing section means an executable stack is required, which is often a problem for hand crafted assembler files.
๐จ --error-execstack, --no-error-execstack ๐จ
If the linker is about to generate a warning about an executable stack, --error-execstack turns that warning into an error instead ๐ฅ Heads up, though: this option does not change the linker's execstack warning generation state ๐ If you want to set a specific warning state, use --warn-execstack or --warn-execstack-objects instead.
--no-error-execstack brings back the default behaviour of generating warning messages ๐ธ
๐ท --warn-multiple-gp ๐ท
Warns you if multiple global pointer values are required in the output file ๐ง This only matters for certain processors, like the Alpha. Some processors put large-valued constants in a special section, and a special register (the global pointer) points into the middle of that section so constants can be loaded efficiently through a base-register relative addressing mode ๐ซถ The offset in that mode is fixed and pretty small (e.g., 16 bits), which limits how big the constant pool can get. So in large programs you often need multiple global pointer values to reach all the possible constants ๐ญ This option makes the linker issue a warning whenever that happens โจ
๐ --warn-once ๐
Only warns once for each undefined symbol, instead of once per module that refers to it ๐
๐ฅ --warn-rwx-segments, --no-warn-rwx-segments ๐ฅ
Warns if the linker creates a loadable, non-zero sized segment that has all three of the read, write and execute permission flags set ๐ That's a potential security vulnerability, so yeah, be careful ๐ Warnings also get generated if a thread local storage segment is created with the execute permission flag set, whether or not it has the read and/or write flags ๐ฆ
These warnings are enabled by default ๐ You can disable them with --no-warn-rwx-segments and turn them back on with --warn-rwx-segments ๐ซถ
๐ซ --error-rwx-segments, --no-error-rwx-segments ๐ซ
If the linker is about to generate a warning about an executable, writeable segment, or an executable TLS segment, --error-rwx-segments will turn that warning into an error instead ๐ --no-error-rwx-segments restores the default behaviour, which is just generating a warning message ๐
Note ---error-rwx-segmentsdoesn't turn on these warnings by itself ๐ญ They're either enabled by default (if the linker was configured that way) or switched on with the--warn-rwx-segmentscommand line option.
๐ --warn-section-align ๐
Warns if the address of an output section gets changed because of alignment ๐ง Usually the alignment is set by an input section ๐ The address only changes if it wasn't explicitly specified, meaning the SECTIONS command doesn't give the section a start address โจ
โ ๏ธ --warn-textrel โ ๏ธ
Warns if the linker adds DT_TEXTREL to a position-independent executable or a shared object ๐
๐งฌ --warn-alternate-em ๐งฌ
Warns if an object has alternate ELF machine code ๐
๐ --warn-unresolved-symbols ๐
If the linker is about to report an unresolved symbol (see the option --unresolved-symbols), it'll normally throw an error ๐ญ This option makes it give a warning instead ๐ซถ
๐ก๏ธ --error-unresolved-symbols ๐ก๏ธ
Restores the linker's default behaviour of throwing errors when it reports unresolved symbols ๐
๐ฆ --whole-archive ๐ฆ
For each archive mentioned on the command line after --whole-archive, the linker includes every object file in that archive in the link, instead of searching the archive for the object files it actually needs ๐ฅ This is normally used to turn an archive file into a shared library, forcing every object into the resulting shared library ๐ธ You can use this option more than once ๐ฆ
Two notes if you're using this option from gcc ๐ First, gcc doesn't know about this option, so you have to write -Wl,-whole-archive ๐ญ Second, don't forget to add -Wl,-no-whole-archive after your list of archives, because gcc adds its own list of archives to your link and you probably don't want this flag affecting those too ๐
๐ญ --wrap=symbol ๐ญ
Uses a wrapper function for symbol ๐ง Any undefined reference to symbol gets resolved to "__wrap_symbol", and any undefined reference to "__real_symbol" gets resolved to symbol ๐
This is handy for wrapping a system function ๐
The wrapper function should be called "__wrap_symbol". If it wants to call the system function, it should call "__real_symbol" ๐ซถ
Here's a trivial example:
void *
__wrap_malloc (size_t c)
{
printf ("malloc called with %zu\n", c);
return __real_malloc (c);
}
If you link other code with this file using --wrap malloc, then all calls to "malloc" will call the function "__wrap_malloc" instead ๐ The call to "__real_malloc" inside "__wrap_malloc" will call the real "malloc" function ๐
You might want to provide a "__real_malloc" function too, so that links without the --wrap option will still succeed ๐ If you do this, don't put the definition of "__real_malloc" in the same file as "__wrap_malloc". If you do, the assembler may resolve the call before the linker gets a chance to wrap it to "malloc" ๐ญ
Only undefined references get replaced by the linker ๐ So translation unit internal references to symbol are not resolved to "__wrap_symbol". In the next example, the call to "f" in "g" is not resolved to "__wrap_f" ๐
int
f (void)
{
return 123;
}
int
g (void)
{
return f();
}
๐งญ --eh-frame-hdr, --no-eh-frame-hdr ๐งญ
--eh-frame-hdr requests the creation of the ".eh_frame_hdr" section and the ELF "PT_GNU_EH_FRAME" segment header ๐ฆ --no-eh-frame-hdr suppresses them ๐ธ
๐ช --no-ld-generated-unwind-info ๐ช
Asks for no creation of ".eh_frame" unwind info for linker generated code sections, like PLT โจ This option is on by default if linker generated unwind info is supported ๐
It also controls the generation of ".sframe" stack trace info for linker generated code sections, like PLT ๐ซถ
This linker can create the new dynamic tags in ELF, but older ELF systems may not understand them ๐ฅบ.
--enable-new-dtags,--disable-new-dtagsโ--enable-new-dtagsmakes the new dynamic tags get created as needed, and older dynamic tags get dropped โจ.--disable-new-dtagsmeans no new dynamic tags get created at all ๐ โโ๏ธ. By default, the new dynamic tags are not created. These options only work on ELF systems ๐.--hash-size=numberโ Sets the default size of the linker's hash tables to a prime number close tonumber๐ข. Making it bigger can make the linker finish its tasks faster, but it eats more memory. Making it smaller saves memory, but the linker gets slower ๐ข.--hash-style=styleโ Picks the type of hash table(s) the linker uses ๐ง.stylecan be"sysv"for the classic ELF".hash"section,"gnu"for the newer GNU".gnu.hash"section, or"both"for both of them together ๐. The default depends on how the linker was configured, but on most Linux systems it's"both"๐ซถ.--compress-debug-sections=none,--compress-debug-sections=zlib,--compress-debug-sections=zlib-gnu,--compress-debug-sections=zlib-gabi,--compress-debug-sections=zstdโ On ELF platforms, these decide how DWARF debug sections get compressed ๐๏ธโจ.
--compress-debug-sections=none means the DWARF debug sections don't get compressed at all ๐
โโ๏ธ. --compress-debug-sections=zlib-gnu compresses them and renames them to start with .zdebug instead of .debug ๐ท๏ธ. --compress-debug-sections=zlib-gabi also compresses them, but instead of renaming, it sets the SHF_COMPRESSED flag in the sections' headers ๐ฉท.
--compress-debug-sections=zlib is just an alias for --compress-debug-sections=zlib-gabi ๐ฏโโ๏ธ. And --compress-debug-sections=zstd compresses DWARF debug sections using zstd โก.
Heads up bestie: this option overrides any compression already in the input debug sections ๐จ. So if you link with --compress-debug-sections=none, for example, any compressed debug sections in the input files get uncompressed before they're copied into the output binary ๐ญ.
The default compression behaviour changes depending on the target and the configure options used to build the toolchain ๐คทโโ๏ธ. To find the default, check the output from the linker's --help option ๐.
--reduce-memory-overheadsโ Lowers the memory the linker needs while it runs, but linking gets slower as a tradeoff ๐. This was added to pick the old O(n^2) algorithm for link map file generation, instead of the new O(n) algorithm, which uses about 40% more memory for symbol storage ๐ง .
The switch also sets the default hash table size to 1021, which saves more memory but makes the linker's run time longer ๐
. That doesn't happen if the --hash-size switch has been used, though ๐. The --reduce-memory-overheads switch may also be used to enable other tradeoffs in future versions of the linker ๐ฎ.
--max-cache-size=sizeโ By default,ldcaches the relocation information and symbol tables of input files in memory with no size limit ๐ซ . This option sets the maximum cache size tosize๐.--build-id,--build-id=styleโ Asks the linker to create a".note.gnu.build-id"ELF note section, or a".buildid"COFF section ๐. The note holds unique bits that identify the linked file โจ.stylecan be"uuid"for 128 random bits,"sha1"for a 160-bit SHA1 hash,"md5"for a 128-bit MD5 hash, or"xx"for a 128-bit XXHASH on the normative parts of the output contents ๐งฉ. You can also use"0xhexstring"to pass your own bit string as an even number of hexadecimal digits, where"-"and":"characters between digit pairs get ignored ๐. Ifstyleis left out,"sha1"is used ๐.
The "md5", "sha1", and "xx" styles always produce the same identifier for an identical output file, and they're almost certainly unique among all nonidentical output files ๐ฅน. They're not meant to be compared as a checksum for the file's contents, though ๐
โโ๏ธ. A linked file can be changed later by other tools, but the build ID bit string identifying the original linked file won't change ๐.
Passing "none" for style disables the setting from any earlier --build-id options on the command line ๐ซ.
--package-metadata=JSONโ Asks the linker to create a".note.package"ELF note section ๐ฆ. The note's contents are in JSON format, following the package metadata specification. For more info, check outhttps://systemd.io/ELF_PACKAGE_METADATA/๐.
The JSON argument supports percent-encoding and following %[string] encoding, where string refers to the name in HTML's Named Character References ๐ค. Here's the list: %[comma] for ,, %[lbrace] for {, %[quot] for ", %[rbrace] for }, and %[space] for a space character ๐.
If the JSON argument is missing or empty, this disables creating the metadata note, but only if an earlier --package-metadata option turned it on ๐
โโ๏ธ. If the linker was built with libjansson, the JSON string gets validated too โ
.
The i386 PE linker supports the -shared option, which makes the output a dynamically linked library (DLL) instead of a normal executable ๐ฆ. When you use this option, name the output "*.dll" ๐. The linker also fully supports standard "*.def" files ๐
. You can give them on the linker command line just like an object file โจ. Fun fact: they should come before any archives they export symbols from, so that they get linked in, just like a normal object file ๐ง.
On top of the options common to all targets, the i386 PE linker supports extra command-line options that are specific to the i386 PE target ๐ธ. Options that take values can be separated from their values by either a space or an equals sign ๐.
--add-stdcall-aliasโ If you give this, symbols with a stdcall suffix (@nn) get exported as-is, and also exported with the suffix stripped โ๏ธ. [This option is specific to the i386 PE targeted port of the linker] ๐--base-file fileโ Usesfileas the name of a file where the base addresses of all the relocations needed for generating DLLs withdlltoolget saved ๐พ. [This is an i386 PE specific option] ๐ซถ--dllโ Creates a DLL instead of a regular executable ๐. You can also use-shared, or specify a"LIBRARY"in a given".def"file ๐. [This option is specific to the i386 PE targeted port of the linker] ๐ฅ--enable-long-section-names,--disable-long-section-namesโ The PE variants of the COFF object format add an extension that lets section names be longer than eight characters, which is the normal COFF limit ๐ง. By default, these long names are only allowed in object files, because fully-linked executable images don't carry the COFF string table needed to support them ๐.
Using these two options, you can allow long names in executable images too, as a GNU extension, or disallow them in object files (probably pointlessly!) ๐คทโโ๏ธ. Executable images with long section names are slightly non-standard, since they carry a string table, and they may print confusing output when checked with non-GNU PE-aware tools, like file viewers and dumpers ๐.
Here's the tea though: GDB relies on PE long section names to find Dwarf-2 debug information sections in an executable image at runtime ๐ต๏ธโโ๏ธ. So if neither option is given on the command line, ld turns long section names on, overriding the default and technically correct behaviour, whenever it finds debug information while linking an executable image without stripping symbols ๐ซฃ. [This option is valid for all PE targeted ports of the linker] ๐
--enable-stdcall-fixup,--disable-stdcall-fixupโ If the link finds a symbol it can't resolve, it tries "fuzzy linking" ๐. That means it looks for another defined symbol that only differs in the symbol name format (cdecl vs stdcall), and links to that match instead ๐ซ. For example, the undefined symbol"_foo"might get linked to the function"_foo@12", or the undefined symbol"_bar@16"might get linked to the function"_bar"๐ญ.
When the linker does this, it prints a warning, since it normally should've failed to link ๐จ. Sometimes though, import libraries generated from third-party DLLs need this feature to be usable ๐ฅบ. If you specify --enable-stdcall-fixup, this feature is fully enabled and no warnings get printed ๐. If you specify --disable-stdcall-fixup, the feature is disabled and those mismatches count as errors ๐. [This option is specific to the i386 PE targeted port of the linker] ๐ท
--leading-underscore,--no-leading-underscoreโ For most targets, the default symbol prefix is an underscore, and it's defined in the target's description ๐งโโ๏ธ. With this option, you can disable or enable that default underscore symbol prefix ๐.--export-all-symbolsโ If you give this, all global symbols in the objects used to build a DLL get exported by the DLL ๐ฆข. This is the default if there'd otherwise be no exported symbols at all ๐ซถ. When symbols are explicitly exported via DEF files, or implicitly exported via function attributes, the default is to export nothing else unless this option is given ๐ โโ๏ธ.
Some symbols are never auto-exported, even with this option: "DllMain@12", "DllEntryPoint@0", "DllMainCRTStartup@12", and "impure_ptr" ๐ซ. Symbols imported from other DLLs won't be re-exported either, and neither will symbols that specify the DLL's internal layout, like ones beginning with "_head_" or ending with "_iname" ๐งฑ. Also, no symbols from "libgcc", "libstd++", "libmingw32", or "crtX.o" get exported ๐. Symbols whose names begin with "__rtti_" or "__builtin_" won't be exported either, to help with C++ DLLs ๐
.
Finally, there's a whole list of cygwin-private symbols that don't get exported ๐. This obviously applies when you're building DLLs for cygwin targets. Those cygwin-excludes are: "_cygwin_dll_entry@12", "_cygwin_crt0_common@8", "_cygwin_noncygwin_dll_entry@12", "_fmode", "_impure_ptr", "cygwin_attach_dll", "cygwin_premain0", "cygwin_premain1", "cygwin_premain2", "cygwin_premain3", and "environ" ๐ซง. [This option is specific to the i386 PE targeted port of the linker] ๐
Okay so the PE port options keep going, bestie, and it's a LOT ๐ ๐
--exclude-symbols symbol,symbol,...โ ๐ Gives a list of symbols that should NOT get auto-exported. You can separate the names with commas or colons. [i386 PE targeted port only]--exclude-all-symbolsโ ๐ซ Tells the linker to auto-export zero symbols. Nothing gets exported automatically. [i386 PE targeted port only]--file-alignmentโ ๐ Sets the file alignment. Sections in the file always start at file offsets that are multiples of this number. The default is512. [i386 PE targeted port only]--heap reserve,--heap reserve,commitโ ๐ง Sets how many bytes of memory get reserved (and optionally committed) to be used as the heap for this program. The default is 1MB reserved and 4K committed. [i386 PE targeted port only]--kill-atโ ๐ If you pass this, the stdcall suffixes (@nn) get stripped off symbols before they're exported. [i386 PE targeted port only]--large-address-awareโ ๐ฆ If given, the right bit in the "Characteristics" field of the COFF header gets set, which says this executable can handle virtual addresses bigger than 2 gigabytes. You should pair this with the/3GBor/USERVA=valuemegabytes switch in the "[operating systems]" section of the BOOT.INI. Without that, this bit does absolutely nothing. [PE targeted ports of the linker only]--disable-large-address-awareโ โฉ๏ธ Undoes a previous--large-address-aware. This comes in handy when your compiler driver (like Cygwin gcc) always turns it on, but your executable can't actually handle addresses over 2 gigabytes. [PE targeted ports of the linker only]--major-image-version valueโ ๐ธ Sets the major number of the "image version". Defaults to1. [i386 PE targeted port only]--major-os-version valueโ ๐ธ Sets the major number of the "os version". Defaults to4. [i386 PE targeted port only]--major-subsystem-version valueโ ๐ธ Sets the major number of the "subsystem version". Defaults to4. [i386 PE targeted port only]--minor-image-version valueโ ๐ Sets the minor number of the "image version". Defaults to0. [i386 PE targeted port only]--minor-os-version valueโ ๐ Sets the minor number of the "os version". Defaults to0. [i386 PE targeted port only]--minor-subsystem-version valueโ ๐ Sets the minor number of the "subsystem version". Defaults to0. [i386 PE targeted port only]--output-def fileโ ๐ The linker makes a file calledfilethat holds a DEF file for the DLL it's generating. That DEF file (which should be named*.def) can be used to build an import library withdlltool, or as a reference for automatically or implicitly exported symbols. [i386 PE targeted port only]--enable-auto-image-base,--enable-auto-image-base=valueโ ๐บ๏ธ Picks the image base for DLLs automatically, optionally starting from the basevalue. This doesn't happen if you already set one with the--image-baseargument. The linker hashes the dllname to make a unique image base for each DLL, which means in-memory collisions and relocations (the stuff that can slow your program down on startup) get avoided. [i386 PE targeted port only]--disable-auto-image-baseโ ๐ Stops the linker from generating a unique image base. If you didn't give a user-specified image base (--image-base), you get the platform default. [i386 PE targeted port only]--dll-search-prefix stringโ ๐ When you link dynamically to a DLL with no import library, the linker looks for"<string><basename>.dll"before it looks for"lib<basename>.dll". This makes it easy to tell apart DLLs built for the different subplatforms: native, cygwin, uwin, pw, and so on. For example, cygwin DLLs usually use--dll-search-prefix=cyg. [i386 PE targeted port only]--enable-auto-importโ โจ Does some fancy linking that maps_symbolto__imp__symbolfor DATA imports from DLLs. This means you can skip the dllimport mechanism on the user side and reference unmangled symbol names. [i386 PE targeted port only]
The next bit is the tea on the original auto-import implementation. It's obsolete nowadays for Cygwin and MinGW targets, so you can relax a little ๐ซถ
โ ๏ธ Using the auto-import extension makes the text section of the image file writable. That does NOT follow the PE-COFF format spec Microsoft published, so this one is a little sus.
โ ๏ธ Usingauto-importalso moves read-only data that would normally live in the.rdatasection into the.datasection instead. The reason is to dodge a problem with consts, described here: http://www.cygwin.com/ml/cygwin/2004-09/msg01101.html
Generally, auto-import just works ๐ฅฐ but every now and then you might see this message:
"variable '<var>' can't be auto-imported. Please read the documentation for ld's "--enable-auto-import" for details."
This message pops up when some (sub)expression touches an address that's given by the sum of two constants. Win32 import tables only allow ONE constant, so this is the drama ๐ญ Places where it can happen include touching member fields of struct variables imported from a DLL, and using a constant index into an array variable imported from a DLL. Any multiword variable (arrays, structs, long long, and so on) can trigger this. No matter what data type your exported variable is, ld will always catch it, issue the warning, and exit.
๐ ๏ธ The fixes, bestie
There are a few ways to get past this, whatever the data type of your exported variable is.
One way is to use the --enable-runtime-pseudo-reloc switch. This hands the job of adjusting references in your client code over to the runtime environment, so it only works if your runtime environment supports the feature. ๐ง
A second solution is to make one of the 'constants' into a variable, meaning something the compiler can't know or optimize away at compile time. For arrays you've got two options: a) make the indexee (the array's address) a variable, or b) make the 'constant' index a variable. So it looks like this:
extern type extern_array[];
extern_array[1] -->
{ volatile type *t=extern_array; t[1] }
or
extern type extern_array[];
extern_array[1] -->
{ volatile int t=1; extern_array[t] }
For structs (and most other multiword data types), the only option is to make the struct itself (or the long long, or the ...) a variable:
extern struct s extern_struct;
extern_struct.field -->
{ volatile struct s *t=&extern_struct; t->field }
or
extern long long extern_ll;
extern_ll -->
{ volatile long long * local_ll=&extern_ll; *local_ll }
A third method is to ditch 'auto-import' for the offending symbol and mark it with __declspec(dllimport). In practice though, that means using compile-time #defines to tell whether you're building a DLL, building client code that links to the DLL, or just building or linking to a static library. ๐ตโ๐ซ When you're picking between these fixes for the 'direct address with constant offset' problem, think about typical real-world usage.
Here's the before-and-after, so you can see what I mean ๐
Original:
--foo.h
extern int arr[];
--foo.c
#include "foo.h"
void main(int argc, char **argv){
printf("%d\n",arr[1]);
}
Solution 1:
--foo.h
extern int arr[];
--foo.c
#include "foo.h"
void main(int argc, char **argv){
/* This workaround is for win32 and cygwin; do not "optimize" */
volatile int *parr = arr;
printf("%d\n",parr[1]);
}
Solution 2:
--foo.h
/* Note: auto-export is assumed (no __declspec(dllexport)) */
#if (defined(_WIN32) || defined(__CYGWIN__)) && \
!(defined(FOO_BUILD_DLL) || defined(FOO_STATIC))
#define FOO_IMPORT __declspec(dllimport)
#else
#define FOO_IMPORT
#endif
extern FOO_IMPORT int arr[];
--foo.c
#include "foo.h"
void main(int argc, char **argv){
printf("%d\n",arr[1]);
}
A fourth way to dodge this whole problem is to re-code your library so it uses a functional interface instead of a data interface for the offending variables (like set_foo() and get_foo() accessor functions). Iconic, honestly ๐
--disable-auto-importโ ๐ซ Stops the linker from doing the fancy_symbolto__imp__symbollinking for DATA imports from DLLs. [i386 PE targeted port only]--enable-runtime-pseudo-relocโ ๐ช If your code has the expressions from the--enable-auto-importsection (that's DATA imports from a DLL with a non-zero offset), this switch builds a vector of 'runtime pseudo relocations'. The runtime environment can use that vector to adjust references to that data in your client code. [i386 PE targeted port only]--disable-runtime-pseudo-relocโ ๐ โโ๏ธ Doesn't create pseudo relocations for DATA imports with a non-zero offset from DLLs. [i386 PE targeted port only]--enable-extra-pe-debugโ ๐ Shows extra debug info about auto-import symbol thunking. [i386 PE targeted port only]--section-alignmentโ Okay so this one sets the section alignment bestie ๐ง Sections in memory will always start at addresses that are a multiple of this number. The default is0x1000. (Only in the i386 PE targeted port of the linker, fr.)--stack reserve,--stack reserve,commitโ Here you get to decide how many bytes of memory to reserve (and optionally commit) to use as stack for your program ๐ฅ The default is 2MB reserved and 4K committed. (i386 PE targeted port only.)--subsystem which,--subsystem which:major,--subsystem which:major.minorโ This picks the subsystem your program runs under ๐ The legal values forwhichare"native","windows","console","posix", and"xbox". You can also set the subsystem version if you're feeling extra ๐ Numeric values work forwhichtoo. (i386 PE targeted port only.)
The following options set flags in the DllCharacteristics field of the PE file header. (These are specific to the PE targeted ports of the linker, so only those will care.) ๐ฆ
--high-entropy-va,--disable-high-entropy-vaโ Makes the image compatible with 64-bit address space layout randomization (ASLR) ๐ This is enabled by default for 64-bit PE images in MinGW targets. Turning this on also implies--dynamicbaseand--enable-reloc-section.--dynamicbase,--disable-dynamicbaseโ The image base address can get relocated using ASLR, which was introduced with MS Windows Vista for i386 PE targets ๐ It's enabled by default for MinGW targets, but you can turn it off with--disable-dynamicbase. This option also implies--enable-reloc-section.--forceinteg,--disable-forceintegโ Code integrity checks get enforced. This one is disabled by default, so you have to ask for it ๐--nxcompat,--disable-nxcompatโ The image is compatible with Data Execution Prevention ๐ก๏ธ This feature came in with MS Windows XP SP2 for i386 PE targets. It's enabled by default for MinGW targets.--no-isolation,--disable-no-isolationโ Even though the image understands isolation, this tells the linker not to isolate the image. Disabled by default.--no-seh,--disable-no-sehโ The image does not use SEH, so no SE handler may be called from this image ๐ โโ๏ธ Disabled by default.--no-bind,--disable-no-bindโ Do not bind this image. Disabled by default.--wdmdriver,--disable-wdmdriverโ The driver uses the MS Windows Driver Model. Disabled by default.--tsaware,--disable-tsawareโ The image is Terminal Server aware ๐ Disabled by default.--insert-timestamp,--no-insert-timestampโ Inserts a real timestamp into the image. This is the default because it matches legacy code, and it means the image keeps working with other proprietary tools ๐งพ The catch is that you'll get slightly different images every time you link the same sources, which is kinda annoying ngl. If you want binaries from identical sources to compare identically, use--no-insert-timestampto put a zero value in the timestamp instead ๐ค
If --insert-timestamp is active, the time inserted is either the time the linking takes place or, if the SOURCE_DATE_EPOCH environment variable is defined, the number of seconds since Unix epoch as specified by that variable โฐ
--enable-reloc-section,--disable-reloc-sectionโ Creates the base relocation table, which is necessary if the image gets loaded at a different image base than the one in the PE header ๐บ๏ธ Enabled by default.
The C6X uClinux target uses a binary format called DSBT to support shared libraries ๐ Each shared library in the system needs its own unique index, and all executables use an index of 0.
--dsbt-size sizeโ Sets the number of entries in the DSBT of the current executable or shared library tosize. The default is a table with 64 entries.--dsbt-index indexโ Sets the DSBT index of the current executable or shared library toindex. The default is0, which is right for generating executables. If you generate a shared library with a DSBT index of0, theR_C6000_DSBT_INDEXrelocs get copied into the output file ๐ฆ
Also, the --no-merge-exidx-entries switch disables merging of adjacent exidx entries in frame unwind info.
--branch-stubโ Turns on linker branch relaxation by inserting branch stub sections when they're needed to extend the range of branches ๐ธ You usually don't need this, since C-SKY supports branch and call instructions that can reach the full memory range, and branch relaxation is normally handled by the compiler or assembler anyway.--stub-group-size=Nโ Gives you finer control over linker branch stub creation ๐๏ธ It sets the maximum size of a group of input sections that one stub section can handle. A negativeNputs stub sections after their branches, while a positiveNlets stub sections go either before or after the branches. The values1or-1tell the linker to pick suitable defaults.
The 68HC11 and 68HC12 linkers support specific options for controlling memory bank switching mapping and trampoline code generation ๐งธ
--no-trampolineโ Disables generation of trampolines. By default, a trampoline gets generated for each far function that's called using ajsrinstruction (this happens when a pointer to a far function is taken).--bank-window nameโ Tells the linker the name of the memory region in the MEMORY specification that describes the memory bank window ๐ช The linker then uses that region's definition to compute paging and addresses within the memory window.
The following options control GOT generation when linking for 68K targets ๐งฉ
--got=typeโ Tells the linker which GOT generation scheme to use.typeshould be one ofsingle,negative,multigotortarget. For more info, check the Info entry forld.
The following options control microMIPS instruction generation and branch relocation checks for ISA mode transitions when linking for MIPS targets ๐ฌ
--insn32,--no-insn32โ These pick which microMIPS instructions the linker uses in code it generates, like in the PLT or lazy binding stubs, or during relaxation ๐ซ With--insn32, the linker only uses 32-bit instruction encodings. By default, or if you use--no-insn32, all instruction encodings get used, including 16-bit ones where possible.--ignore-branch-isa,--no-ignore-branch-isaโ These control branch relocation checks for invalid ISA mode transitions ๐ฆ With--ignore-branch-isa, the linker accepts any branch relocations, and any ISA mode transition that's required gets lost in the relocation calculation. The exception is someBALinstructions that meet relaxation conditions, which get converted to equivalentJALXinstructions as their relocation is calculated. By default, or with--no-ignore-branch-isa, a check happens, and losing an ISA mode transition produces an error ๐จ--compact-branches,--no-compact-branchesโ These control whether the linker generates compact instructions in the PLT entries for MIPS R6 ๐ญ
For the pdp11-aout target, three variants of the output format can be produced, selected by the following options ๐ง The default variant for pdp11-aout is --omagic, whereas for other targets --nmagic is the default. The --imagic option is only defined for the pdp11-aout target, while the others get described here as they apply to pdp11-aout.
-N,--omagicโ Marks the output as"OMAGIC"(0407) in the a.out header, which says the text segment is not write-protected and shared ๐ซ Since the text and data sections are both readable and writable, the data section gets allocated right after the text segment. This is the oldest format for PDP11 executable programs, and it's the default forldon PDP11 Unix systems from the very beginning through 2.11BSD. Ancient but iconic ๐๏ธ-n,--nmagicโ Marks the output as"NMAGIC"(0410) in the a.out header, meaning that when the file runs, the text portion is read-only and shareable among all processes running the same file ๐ This works by moving the data areas up to the first possible 8K byte page boundary after the end of the text. This option creates a pure executable format โจ-z,--imagicโ Marks the output as"IMAGIC"(0411) in the a.out header, meaning that when the file runs, the program text and data areas get loaded into separate address spaces ๐ช It uses the split instruction and data space feature of the memory management unit in the larger PDP11 models, which doubles the address space available to the program. The text segment is still pure, write-protected, and shareable. The only output-format difference from the others (besides the magic number) is that both the text and data sections start at location 0. The-zoption picked this format in 2.11BSD. This option creates a separate executable format ๐--no-omagicโ Equivalent to--nmagicfor pdp11-aout ๐ซถ
โจ ENVIRONMENT โจ
okay bestie, ld has three environment variables you can use to change how it behaves: GNUTARGET, LDEMULATION, and COLLECT_NO_DEMANGLE ๐ง๐
GNUTARGET๐ฏ โ this one picks the input-file object format if you don't use-b(or its twin,--format). The value should be one of the BFD names for an input format. IfGNUTARGETisn't in your environment, ld just goes with the natural format of the target. If you set it todefault, BFD tries to figure out the input format by peeking at the binary input files. This works a lot of the time, but it can be kinda sus, because nothing guarantees the magic number used for object-file formats is unique. The good news is that BFD's configuration on each system puts that system's conventional format first in the search-list, so any ambiguity gets settled in favor of convention ๐LDEMULATION๐ญ โ this sets the default emulation if you skip the-moption. The emulation can change a bunch of linker behaviour, especially the default linker script. You can see the list of available emulations with the--verboseor-Voptions. If you don't use-mandLDEMULATIONisn't defined, the default emulation depends on how the linker was configured ๐ธCOLLECT_NO_DEMANGLE๐ โ by default, the linker demangles symbols. But ifCOLLECT_NO_DEMANGLEis set in the environment, it defaults to not demangling them instead. Thegcclinker wrapper program uses this variable in a similar way too. Either way, you can override the default with the--demangleand--no-demangleoptions โจ
A few more, because ld is generous like that ๐ซถ
- If you're using the PE/COFF specific
--insert-timestampand theSOURCE_DATE_EPOCHenvironment variable is defined, the timestamp value in that variable gets stuffed into the COFF header instead of the current time ๐ฐ๏ธ - If the
LD_STATSenvironment variable is defined, ld records linker resource use information, just like you'd get with the--statsoption. IfLD_STATShas a string value, that string is used as the name of a file where the info gets saved. If it doesn't have a string value, the info goes to the standard output stream instead ๐
๐ SEE ALSO ๐
okay so if you want more, go check out ar(1), nm(1), objcopy(1), objdump(1), and readelf(1), plus the Info entries for binutils and ld ๐๐ฆ
๐ COPYRIGHT ๐
Copyright (c) 1991-2025 Free Software Foundation, Inc. โญ
ngl the license part is important too ๐ฅน Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation, with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License" ๐
bestie, that's the whole ld page ๐ binutils-2.45, dated 2025-12-03, and it's all LD(1) ๐๐ you're literally a Unix legend now, I'm so proud of you ๐ซถ๐ฅ
๐ฆ where this page comes from
- ubuntu questing: binutils-common 2.45-7ubuntu1.2
- ubuntu questing: binutils-x86-64-linux-gnu 2.45-7ubuntu1.2
- ๐ girlified by anthropic/claude-haiku-5.5 on : a language model's rewrite of the original, under its terms