LD.LLD(1)

๐ŸŒธ NAME ๐ŸŒธ

okay so bestie, you're reading the General Commands Manual entry for ld.lld(1) โ€” the ELF linker from the LLVM project, and she is literally that girl ๐Ÿ’…โœจ.

โœจ SYNOPSIS โœจ

ld.lld [options] objfile ...

๐Ÿฆ‹ DESCRIPTION ๐Ÿฆ‹

So a linker takes one or more object files, archive files, and library files, and combines them into an output file โ€” which can be an executable, a shared library, or another object file. It relocates code and data from the input files and resolves symbol references between them. That's the whole vibe ๐Ÿซถ.

ld.lld is a drop-in replacement for the GNU BFD and gold linkers. It accepts most of the same command line arguments and linker scripts as GNU linkers. No cap.

Right now ld.lld supports i386, x86-64, ARM, AArch64, LoongArch, PowerPC32, PowerPC64, MIPS32, MIPS64, RISC-V, AMDGPU, Hexagon, and SPARC V9 targets. It acts as a Microsoft link.exe-compatible linker if invoked as lld-link, and as macOS's ld if invoked as ld.ld64. All these targets are always supported however ld.lld was built, so you can always use ld.lld as a native linker as well as a cross linker. Iconic behavior, honestly ๐Ÿ’–.

๐Ÿ‘‘ OPTIONS ๐Ÿ‘‘

Many options have both a single-letter and long form. When using the long form options other than those beginning with the letter o, you may specify them using either one or two dashes preceding the option name. Long options beginning with o require two dashes to avoid confusion with the -o path option. Dash lore, but make it useful ๐ŸŽ€.

  • --allow-multiple-definition โ€” don't error if a symbol is defined multiple times. The first definition will be used. Periodt.
  • --allow-shlib-undefined โ€” allow unresolved references in shared libraries. This option is enabled by default when linking a shared library.
  • --apply-dynamic-relocs โ€” apply link-time values for dynamic relocations.
  • --as-needed โ€” only set DT_NEEDED for shared libraries if used.
  • --auxiliary=value โ€” set the DT_AUXILIARY field to the specified name.
  • --Bdynamic, --dy โ€” link against shared libraries.
  • --Bstatic, --static, --dn โ€” do not link against shared libraries.
  • -Bno-symbolic โ€” don't bind default visibility defined symbols locally for -shared (default).
  • -Bsymbolic โ€” bind default visibility defined symbols locally for -shared. Also set the DF_SYMBOLIC flag.
  • -Bsymbolic-non-weak โ€” bind default visibility defined STB_GLOBAL symbols locally for -shared.
  • -Bsymbolic-functions โ€” bind default visibility defined function symbols locally for -shared.
  • -Bsymbolic-non-weak-functions โ€” bind default visibility defined STB_GLOBAL function symbols locally for -shared.
  • --be8 โ€” write a Big Endian ELF file using BE8 format (AArch32 only).
  • --branch-to-branch โ€” enable the branch-to-branch optimizations: a branch whose target is another branch instruction is rewritten to point to the latter branch target (AArch64 and X86_64 only), and CFI jump table entries are replaced with the function body where possible (X86_64 only). Enabled by default at -O2.
  • --build-id=value โ€” generate a build ID note. value may be one of fast, md5, sha1, tree, uuid, 0xhex-string, and none. tree is an alias for sha1. Build-IDs of type fast, md5, sha1, and tree are calculated from the object contents. fast is not intended to be cryptographically secure.
  • --build-id โ€” synonym for --build-id=sha1.
  • --call-graph-profile-sort=algorithm โ€” algorithm may be:
    • none โ€” ignore call graph profile.
    • hfsort โ€” use hfsort.
    • cdsort โ€” use cdsort (default).
  • --color-diagnostics=value โ€” use colors in diagnostics. value may be one of always, auto, and never. auto enables color if and only if output is to a terminal.
  • --color-diagnostics โ€” alias for --color-diagnostics=auto.
  • --compress-debug-sections=value โ€” compress DWARF debug sections. The sections remain uncompressed if compressed content would be larger. value may be:
    • none โ€” no compression.
    • zlib โ€” the default compression level is 1 (fastest), as the debug info usually compresses well at that level.
    • zstd โ€” use the default compression level in zstd.
  • --compress-sections=section-glob={none,zlib,zstd}[:level] โ€” compress output sections that match the glob and do not have the SHF_ALLOC flag. The matched sections remain uncompressed if compressed content would be larger. The compression level is level (if specified) or a default speed-focused level. This is like a generalized --compress-debug-sections.
  • --cref โ€” output cross reference table. If -Map is specified, print to the map file.
  • --debug-names โ€” generate a merged .debug_names section.
  • --default-script=file, -dT file โ€” in the absence of --script, read this default linker script.
  • --defsym=symbol=expression โ€” define a symbol alias. expression may be another symbol or a linker script expression. For example, --defsym=foo=bar or --defsym=foo=bar+0x100.
  • --demangle โ€” demangle symbol names.
  • --disable-new-dtags โ€” disable new dynamic tags.
  • --discard-all, -x โ€” delete all local symbols.
  • --discard-locals, -X โ€” delete temporary local symbols.
  • --discard-none โ€” keep all symbols in the symbol table.
  • --dynamic-linker=value โ€” specify the dynamic linker to be used for a dynamically linked executable. This is recorded in an ELF segment of type PT_INTERP.
  • --dynamic-list=file โ€” similar to --export-dynamic-symbol-list. When creating a shared object, implies -Bsymbolic but does not set DF_SYMBOLIC.
  • --EB โ€” select the big-endian format in the OUTPUT_FORMAT command.
  • --EL โ€” select the little-endian format in the OUTPUT_FORMAT command.
  • --eh-frame-hdr โ€” request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header.
  • --emit-relocs, -q โ€” generate relocations in the output.
  • --enable-new-dtags โ€” enable new dynamic tags.
  • --enable-non-contiguous-regions โ€” spill input sections to later matching output sections to avoid memory region overflow.
  • --end-lib โ€” end a grouping of objects that should be treated as if they were together in an archive.
  • --entry=entry โ€” name of entry point symbol.
  • --error-limit=value โ€” maximum number of errors to emit before stopping. A value of zero indicates that there is no limit.
  • --error-unresolved-symbols โ€” report unresolved symbols as errors.
  • --error-handing-script=script_path โ€” call script script_path upon some error, with tag as first argument, and an extra parameter as second argument. The script is expected to return 0 on success. Any other value is considered a generic error. tag may be missing-lib followed by the name of the missing library, or undefined-symbol followed by the name of the undefined symbol. Yes bestie, that's the exact spelling in the manual โ€” copy it exactly ๐Ÿซถ.
  • --execute-only โ€” mark executable sections unreadable. This option is currently only supported on AArch64.
  • --exclude-libs=value โ€” exclude static libraries from automatic export.
  • --export-dynamic, -E โ€” put symbols in the dynamic symbol table.
  • --export-dynamic-symbol=glob โ€” (executable) put matched non-local defined symbols to the dynamic symbol table. (shared object) references to matched non-local STV_DEFAULT symbols shouldn't be bound to definitions within the shared object even if they would otherwise be due to -Bsymbolic, -Bsymbolic-functions, or --dynamic-list.
  • --export-dynamic-symbol-list=file โ€” read a list of dynamic symbol patterns from file. Apply --export-dynamic-symbol on each pattern.
  • --fatal-warnings โ€” treat warnings as errors.
  • --filter=value, -F value โ€” set the DT_FILTER field to the specified value.
  • --fini=symbol โ€” specify a finalizer function.
  • --force-group-allocation โ€” only meaningful for -r. Section groups are discarded. If two section group members are placed to the same output section, combine their relocations as well.
  • --format=input-format, -b input-format โ€” specify the format of the inputs following this option. input-format may be one of binary, elf, and default. default is a synonym for elf.
  • --gc-sections โ€” enable garbage collection of unused sections.
  • --gdb-index โ€” generate .gdb_index section.
  • --hash-style=value โ€” specify hash style. value may be sysv, gnu, or both. both is the default.
  • --help โ€” print a help message.
  • --icf=all โ€” enable identical code folding.
  • --icf=safe โ€” enable safe identical code folding.
  • --icf=none โ€” disable identical code folding.
  • --ignore-data-address-equality โ€” ignore address equality of data. C/C++ requires each data to have a unique address. This option allows lld to do unsafe optimization that breaks the requirement: create copies of read-only data or merge two or more read-only data that happen to have the same value. Unsafe optimization alert, bestie โ€” it literally breaks the requirement.
  • --ignore-function-address-equality โ€” ignore address equality of functions. This option allows non-PIC calls to a function with non-default visibility in a shared object. The function may have different addresses within the executable and within the shared object.
  • --image-base=value โ€” set the base address to value.
  • --init=symbol โ€” specify an initializer function.
  • --keep-unique=symbol โ€” do not fold symbol during ICF.
  • -l libName, --library=libName โ€” root name of library to use.
  • -L dir, --library-path=dir โ€” add a directory to the library search path.
  • --lto-aa-pipeline=value โ€” AA pipeline to run during LTO. Used in conjunction with --lto-newpm-passes.
  • --lto-newpm-passes=value โ€” passes to run during LTO.
  • --lto-Oopt-level โ€” optimization level for LTO.
  • --lto-partitions=value โ€” number of LTO codegen partitions.
  • -m value โ€” set target emulation.
  • --Map=file, -M file โ€” print a link map to file.
  • --nmagic, -n โ€” do not page align sections, link against static libraries.
  • --no-allow-shlib-undefined โ€” nope, we are not letting shared libraries have loose unresolved references lying around. ๐Ÿ’… This one's on by default when you're linking an executable, so the vibes stay clean automatically.
  • --no-as-needed โ€” always set DT_NEEDED for shared libraries. Like, yes bestie, you are needed, we're writing it down. ๐ŸŽ€
  • --no-color-diagnostics โ€” no colors in diagnostics. monochrome era, very demure. ๐Ÿ–ค
  • --no-demangle โ€” don't demangle symbol names. you get the raw mangled names, no glow-up. ๐Ÿ˜ญ
  • --no-dynamic-linker โ€” inhibits output of an .interp section. no interp bestie, we're not doing that today.
  • --no-fortran-common โ€” don't go searching archive members for definitions to override COMMON symbols. fortran girlies, this one's for you ๐Ÿซถ
  • --no-gc-sections โ€” disables garbage collection of unused sections. nothing gets collected, it all stays, hoarder core. ๐Ÿ‘‘
  • --no-gnu-unique โ€” disables STB_GNU_UNIQUE symbol binding. un-uniqued. ๐Ÿ’€
  • --no-merge-exidx-entries โ€” disables merging .ARM.exidx entries. keep 'em separated โœจ
  • --no-nmagic โ€” page align sections. (yes the name is confusing, it's giving chaotic but it's literally just page-aligning, trust ๐Ÿงš)
  • --no-omagic โ€” don't set the text data sections to be writable, and page align sections. so: not writable, but still page aligned. โœจ
  • --no-relax โ€” disables target-specific relaxations. for x86-64 this specifically kills the R_X86_64_GOTPCRELX and R_X86_64_REX_GOTPCRELX GOT optimization. unclench, no relaxing allowed ๐Ÿšซ
  • --no-rosegment โ€” do not put read-only non-executable sections in their own segment. they gotta share, it's giving communal living ๐Ÿ 
  • --undefined-version โ€” do not report version scripts that refer to undefined symbols. snitches get silence ๐Ÿคซ
  • --no-undefined โ€” report unresolved symbols even if the linker is creating a shared library. normally shared libs get a pass, but not today. ๐Ÿ”ฅ
  • --no-warn-mismatch โ€” do not reject unknown section types. live and let live, lowkey.
  • --no-warn-symbol-ordering โ€” do not warn about problems with the symbol ordering file or call graph profile. warnings cancelled ๐Ÿ’…
  • --no-warnings, -w โ€” suppress warnings AND cancel --fatal-warnings. double homicide of the warning system, iconic. ๐Ÿ’€
  • --no-whole-archive โ€” restores the default behavior of loading archive members. back to normal, we're so back. ๐ŸŒˆ
  • --no-pie, --no-pic-executable โ€” do not create a position independent executable. fixed addresses, no PIE for you ๐ŸฅงโŒ
  • --noinhibit-exec โ€” retain the executable output file whenever it's still usable. even if stuff went sideways, if the file works, we keep it. bestie's resilient ๐Ÿซถ
  • --nostdlib โ€” only search directories specified on the command line. no default paths, main character energy only. ๐Ÿ‘‘
  • -o path โ€” write the output executable, library, or object to path. if you don't specify it, a.out is used as a default. (yes, a.out, the OG name, we love a classic ๐ŸŽ€)
  • -Ovalue โ€” optimize the output file. value may be:
    • 0 โ€” disable string merging.
    • 1 โ€” enable string merging.
    • 2 โ€” enable string tail merging and branch-to-branch optimization.

-O1 is the default. โœจ

  • --oformat=format โ€” specify the format for the output object file. the only supported format is binary, which produces output with no ELF header. raw and unbothered. ๐Ÿฆ‹
  • --omagic, -N โ€” set the text and data sections to be readable and writable, do not page align sections, and link against static libraries. the "handle with care" combo ngl โš ๏ธ
  • --opt-remarks-filename file โ€” write optimization remarks in YAML format to file. receipts, but YAML. ๐Ÿงพ
  • --opt-remarks-passes pass-regex โ€” filter optimization remarks by only allowing the passes matching pass-regex. selective hearing, but for passes ๐Ÿ’…
  • --opt-remarks-with-hotness โ€” include hotness information in the optimization remarks file. yes, hotness. ๐Ÿ”ฅ slay.
  • --orphan-handling=mode โ€” control how orphan sections are handled. an orphan section is one not specifically mentioned in a linker script (poor thing, unclaimed ๐Ÿ˜ญ). mode may be:
    • place โ€” place orphan sections in suitable output sections.
    • warn โ€” place orphan sections as for place, and also report a warning.
    • error โ€” place orphan sections as for place, and also report an error.

place is the default. ๐Ÿซถ

  • --pack-dyn-relocs=format โ€” pack dynamic relocations in the given format. format may be:
    • none โ€” do not pack. dynamic relocations are encoded in SHT_REL(A).
    • android โ€” pack dynamic relocations in SHT_ANDROID_REL(A).
    • relr โ€” pack relative relocations in SHT_RELR, and the rest of dynamic relocations in SHT_REL(A).
    • android+relr โ€” pack relative relocations in SHT_RELR, and the rest of dynamic relocations in SHT_ANDROID_REL(A).

none is the default. and heads up: if --use-android-relr-tags is specified, use SHT_ANDROID_RELR instead of SHT_RELR. ๐Ÿ’…

  • --package-metadata โ€” emit a percent-encoded string to the .note.package section. for example, %25 decodes to a single %. iykyk โœจ
  • --pic-veneer โ€” always generate position independent thunks. pico de gallo but make it position independent. ๐ŸŒฎ
  • --pie, --pic-executable โ€” create a position independent executable. ๐Ÿฅงโœจ
  • --power10-stubs=mode โ€” whether to use Power10 instructions in call stubs for R_PPC64_REL24_NOTOC and TOC/NOTOC interworking. mode may be:
    • yes โ€” (default) use.
    • auto โ€” currently the same as yes.
    • no โ€” don't use.
  • --print-gc-sections โ€” list removed unused sections. naming and shaming the discarded ๐Ÿ’€
  • --print-gc-sections=file โ€” list removed unused sections to the specified file. same energy, but it goes in a file ๐Ÿ“
  • --print-icf-sections โ€” list identical folded sections. twin reveal ๐Ÿชž
  • --print-map โ€” print a link map to the standard output. map girlie ๐Ÿ—บ๏ธ
  • --print-archive-stats=file โ€” write archive usage statistics to the specified file. prints the numbers of members and fetched members for each archive. stats era ๐Ÿ“Š
  • --push-state โ€” save the current state of --as-needed, --static, and --whole-archive. save file energy ๐Ÿ’พ
  • --pop-state โ€” restore the states saved by --push-state. load file energy ๐Ÿ”„
  • --randomize-section-padding=seed โ€” randomly insert padding between input sections and at the start of each segment using the given seed. padding gets inserted into output sections with names matching these patterns: .bss, .data, .data.rel.ro, .lbss, .ldata, .lrodata, .ltext, .rodata and .text*. chaos, but seeded. ๐ŸŽฒ
  • ---relax-gp โ€” enable global pointer relaxation for RISC-V. (yes that's three dashes, that's literally how it's written, don't come for me ๐Ÿ’€)
  • --relocatable, -r โ€” create a relocatable object file. ๐Ÿงฉ
  • --remap-inputs=from-glob=to-file โ€” input files matching from-glob are mapped to to-file. use /dev/null to ignore an input file entirely (ghosting, but technical ๐Ÿซฅ).
  • --remap-inputs-file=file โ€” remap input files based on patterns in file. each line in the remap file is of the format from-glob=to-file or a comment starting with #. ๐Ÿ’…
  • --reproduce=path โ€” write a tar file to path, containing all the input files needed to reproduce the link, a text file called response.txt containing the command line options, and a text file called version.txt containing the output of ld.lld --version. the archive, when unpacked, can be used to re-run the linker with the same options and input files. literally a time capsule bestie ๐Ÿ•ฐ๏ธ
  • --retain-symbols-file=file โ€” keep only the symbols listed in file in the static symbol table, along with symbols referenced by emitted relocations. this does not affect the dynamic symbol table. read that twice, it matters ๐Ÿ‘€
  • --rpath=value, -R value โ€” add a DT_RUNPATH to the output. path girlie ๐Ÿ›ค๏ธ
  • --rsp-quoting=value โ€” quoting style for response files. the supported values are windows and posix. pick your fighter ๐ŸฅŠ
  • --script=file, -T file โ€” read linker script from file. if multiple linker scripts are given, they are processed as if they were concatenated in the order they appeared on the command line. order matters, don't shuffle ๐Ÿ“œ
  • --section-start=section=address โ€” set address of section. ๐Ÿ“
  • --shared, --Bsharable โ€” build a shared object. sharing is caring ๐Ÿซถ
  • --shuffle-sections=seed โ€” shuffle matched sections using the given seed before mapping them to the output sections. if -1, reverse the section order. if 0, use a random seed. spotify shuffle but for sections ๐Ÿ”€
  • --soname=value, -h value โ€” set DT_SONAME to value. name tag, but ELF ๐Ÿท๏ธ
  • --sort-common โ€” this option is ignored for GNU compatibility. it's giving ghosted, bestie ๐Ÿซฅ
  • --sort-section=value โ€” specifies sections sorting rule when linkerscript is used. ๐Ÿ“š
  • --start-lib โ€” start a grouping of objects that should be treated as if they were together in an archive. squad assembly ๐Ÿซ‚
  • --strip-all, -s โ€” strip all symbols. implies --strip-debug. everything off, nothing left ๐Ÿ’€
  • --strip-debug, -S โ€” strip debugging information. bye debug info, you won't be missed (you will, but still) ๐Ÿ‘‹
  • --symbol-ordering-file=file โ€” lay out sections in the order specified by file. โœจ
  • --sysroot=value โ€” set the system root. ๐Ÿก
  • --target1-abs โ€” interpret R_ARM_TARGET1 as R_ARM_ABS32. ๐Ÿ’…
  • --target1-rel โ€” interpret R_ARM_TARGET1 as R_ARM_REL32. ๐Ÿ’…
  • --target2=type โ€” interpret R_ARM_TARGET2 as type, where type is one of rel, abs, or got-rel. ๐ŸŽฏ
  • --Tbss=value โ€” same as --section-start with .bss as the sectionname. ๐ŸŽ€
  • --Tdata=value โ€” same as --section-start with .data as the sectionname. ๐ŸŽ€
  • --Ttext=value โ€” same as --section-start with .text as the sectionname. ๐ŸŽ€
  • --thinlto-cache-dir=value โ€” path to ThinLTO cached object file directory. cache storage, bestie ๐Ÿ—„๏ธ
  • --thinlto-cache-policy=value โ€” pruning policy for the ThinLTO cache. โœ‚๏ธ
  • --thinlto-jobs=value โ€” number of ThinLTO jobs. ๐Ÿ’ผ
  • --threads=N โ€” number of threads. all (default) means all of concurrent threads supported. 1 disables multi-threading. go parallel or go home ๐Ÿƒโ€โ™€๏ธ
  • --fat-lto-objects โ€” use the .llvm.lto section, which contains LLVM bitcode, in fat LTO object files to perform LTO. thicc objects, immaculate vibes ๐Ÿ“
  • --no-fat-lto-objects โ€” ignore the .llvm.lto section in relocatable object files (default). โœจ
  • --time-trace โ€” record time trace. โฑ๏ธ
  • --time-trace-file=file โ€” write time trace output to file. ๐Ÿ“„
  • --time-trace-granularity=value โ€” minimum time granularity (in microseconds) traced by the time profiler. very precise, very demure ๐Ÿ”ฌ
  • --trace โ€” print the names of the input files. roll call ๐Ÿ“ข
  • --trace-symbol=symbol, -y symbol โ€” trace references to symbol. following it around like a detective ๐Ÿ”
  • --undefined=symbol, -u symbol โ€” if symbol is not defined after symbol resolution, and there's a static library that contains an object file defining the symbol, load the member to include the object file in the output file. pulling the one object file out of the archive, very "i just need this one thing" ๐Ÿ’…
  • --undefined-glob=pattern โ€” synonym for --undefined, except it takes a glob pattern. in a glob pattern, * matches zero or more characters, ? matches any single character, and [...] matches the characters within brackets. all symbols that match a given pattern are handled as if they were given as arguments of --undefined. ๐ŸŽฏ
  • --unique โ€” creates a separate output section for every orphan input section. everybody gets their own room ๐Ÿšช
  • --unresolved-symbols=value โ€” determine how to handle unresolved symbols. value may be:
  • report-all โ€” reports unresolved symbols. That's the default, bestie โœจ
  • ignore-in-object-files โ€” only complains about unresolved symbols that live in shared libraries ๐Ÿ™ˆ
  • ignore-in-shared-libs โ€” the flip side: only complains about unresolved symbols that live in object files ๐Ÿ’…
  • ignore-all โ€” doesn't report unresolved symbols at all. silence is golden ๐ŸŽ€
  • --use-android-relr-tags โ€” use the SHT_ANDROID_RELR / DT_ANDROID_RELR* tags instead of SHT_RELR / DT_RELR*. like, the Android-flavoured version of the same thing ๐Ÿ’–
  • -v, -V โ€” print the version number and then keep going with the link if you actually gave it object files ๐Ÿซถ
  • --version โ€” print the version number and dip out. that's it, that's the whole job ๐Ÿ‘‘
  • --verbose โ€” verbose mode. she talks more ๐Ÿ—ฃ๏ธ
  • *--version-script=file* โ€” read the version script from file ๐Ÿ“œ
  • --warn-backrefs โ€” warns you about reverse or cyclic dependencies to or between static archives. use it to keep your linker invocation compatible with traditional Unix-like linkers, no cap ๐Ÿ”ฅ
  • *--warn-backrefs-exclude=glob* โ€” a glob describing an archive (or an object file inside --start-lib) that should get ignored for --warn-backrefs. like a VIP list but for not being warned about ๐Ÿ’€
  • --warn-common โ€” warn about duplicate common symbols ๐Ÿ‘ฏโ€โ™€๏ธ
  • --warn-ifunc-textrel โ€” warns about using ifunc symbols together with text relocations. okay so storytime: older glibc (2.28 and earlier) had a bug where the segment containing ifunc symbols gets marked as not executable when they're relocated. so likeโ€ฆ the program compiles and links successfully (slay?) but then gives a segmentation fault the moment the instruction pointer reaches an ifunc symbol ๐Ÿ˜ญ. use -warn-ifunc-textrel to let lld give you a warning if your code may include ifunc symbols, may do text relocations, and is being linked with an older glibc version. otherwise there's literally no need to use it, because the default value does not give a warning. this flag got introduced in late 2018, has no counterpart in ld and gold linkers, and may be removed in the future. so, enjoy her while she's here ๐Ÿฆ‹
  • --warn-unresolved-symbols โ€” report unresolved symbols as warnings instead of full-on errors ๐Ÿซถ
  • --whole-archive โ€” force-load every single member of a static library. no skipping, everyone's invited ๐ŸŽ‰
  • --why-extract=file โ€” prints to file about **why archive members are extracted. the receipts, basically ๐Ÿงพ
  • *--why-live=glob* โ€” for each symbol matching the glob, report a whole chain of references that's preventing garbage collection. detective mode ๐Ÿ”
  • *--wrap=symbol* โ€” redirect references to symbol over to __wrap_symbol, and references to __real_symbol back to symbol. identity theft, but make it linker ๐ŸŽญ

๐Ÿ’… the -z option ๐Ÿ’…

Linker option extensions. the little extra-spicy menu ๐ŸŒถ๏ธ

  • dead-reloc-in-nonalloc=section_glob=value โ€” resolves a relocation in a matched non-SHF_ALLOC section that references a discarded symbol, setting it to value. accepts globs, and if a section matches more than one option, **the last option takes precedence. an order of least specific to most specific match is recommended, mwah ๐Ÿ’‹
  • execstack โ€” makes the main stack executable. stack permissions get recorded in the PT_GNU_STACK segment โš ๏ธ
  • **bti-report=[none|warning|error] โ€” specifies how to report the missing GNU_PROPERTY_AARCH64_FEATURE_1_BTI property. none is the default, so the linker won't report the missing property; otherwise it gets reported as a warning or an error ๐Ÿšจ
  • **cet-report=[none|warning|error] โ€” specifies how to report the missing GNU_PROPERTY_X86_FEATURE_1_IBT or GNU_PROPERTY_X86_FEATURE_1_SHSTK properties. none is the default, no report; otherwise warning or error ๐ŸŒธ
  • **gcs-report=[none|warning|error] โ€” specifies how to report a missing GNU_PROPERTY_AARCH64_FEATURE_1_GCS property. GNU_PROPERTY_AARCH64_FEATURE_1_GCS indicates that an object file supports the Guarded Control Stack security feature. none is the default, no report; otherwise warning or error ๐Ÿ›ก๏ธ
  • dynamic-undefined-weak โ€” makes undefined weak symbols dynamic when the dynamic symbol table is present, if they're referenced from relocatable object files and are not forced local by symbol visibility or versioning. don't make them dynamic when nodynamic-undefined-weak is specified. dynamic-undefined-weak is the default when building a shared object, or when an input shared object is present โœจ
  • **pauth-report=[none|warning|error] โ€” specifies how to report the missing GNU_PROPERTY_AARCH64_FEATURE_PAUTH property. none is the default, no report; otherwise warning or error ๐Ÿ”
  • force-bti โ€” force-enables AArch64 BTI instructions in the PLT, and warns if an input ELF file doesn't have the GNU_PROPERTY_AARCH64_FEATURE_1_BTI property ๐Ÿ’ช
  • force-ibt โ€” force-enables Intel Indirect Branch Tracking in the PLT, and warns if an input ELF file doesn't have the GNU_PROPERTY_X86_FEATURE_1_IBT property ๐Ÿ’ช
  • global โ€” sets the DF_1_GLOBAL flag in the DYNAMIC section. different loaders can decide how to handle this flag on their own ๐Ÿคทโ€โ™€๏ธ
  • ifunc-noplt โ€” doesn't emit PLT entries for ifunc symbols. instead, emits text relocations referencing the resolver. this is an experimental optimization and only suitable for standalone environments where text relocations don't have the usual drawbacks. this option must be combined with the -z notext option ๐Ÿงช
  • initfirst โ€” sets the DF_1_INITFIRST flag to indicate the module should be initialized first. main character energy ๐Ÿฅ‡
  • interpose โ€” sets the DF_1_INTERPOSE flag to tell the runtime linker that the object is an interposer. during symbol resolution, interposers are searched after the application but before other dependencies ๐ŸŽฏ
  • lrodata-after-bss โ€” places .lrodata after .bss. the way that ordering works ๐Ÿซถ
  • muldefs โ€” don't error out if a symbol is defined multiple times; the first definition wins. this is a synonym for --allow-multiple-definition ๐Ÿ‘ฏโ€โ™€๏ธ
  • nocombreloc โ€” disables combining and sorting multiple relocation sections ๐Ÿšซ
  • nocopyreloc โ€” disables the creation of copy relocations ๐Ÿšซ
  • nodefaultlib โ€” sets the DF_1_NODEFLIB flag to indicate that default library search paths should be ignored ๐Ÿ™…โ€โ™€๏ธ
  • nodelete โ€” sets the DF_1_NODELETE flag to indicate that the object can't be unloaded from a process. she stays ๐Ÿ“Œ
  • nodlopen โ€” sets the DF_1_NOOPEN flag to indicate that the object may not be opened by dlopen(3) ๐Ÿ”’
  • nognustack โ€” doesn't emit the PT_GNU_STACK segment ๐Ÿšซ
  • norelro โ€” doesn't indicate that portions of the object should be mapped read-only after initial relocation processing. the object will omit the PT_GNU_RELRO segment ๐Ÿšซ
  • nosectionheader โ€” doesn't generate the section header table ๐Ÿ™ˆ
  • notext โ€” allows relocations against read-only segments. sets the DT_TEXTREL flag in the DYNAMIC section โš ๏ธ
  • now โ€” sets the DF_BIND_NOW flag to indicate that the run-time loader should do all relocation processing as part of object initialization. by default, relocations may be performed on demand. she's impatient, periodt โฐ
  • origin โ€” sets the DF_ORIGIN flag to indicate that the object requires $ORIGIN processing ๐Ÿ 
  • pac-plt โ€” AArch64 only, use pointer authentication in the PLT ๐Ÿ”
  • pack-relative-relocs โ€” similar to -pack-dyn-relocs=relr, but it synthesizes the GLIBC_ABI_DT_RELR version dependency if there's a GLIBC_2.* version dependency. glibc ld.so rejects loading a dynamically linked object without the GLIBC_ABI_DT_RELR version dependency ๐Ÿ˜ค
  • rel โ€” use the REL format for dynamic relocations โœจ
  • rela โ€” use the RELA format for dynamic relocations โœจ
  • retpolineplt โ€” emits retpoline-format PLT entries as a mitigation for CVE-2017-5715 ๐Ÿ›ก๏ธ
  • rodynamic โ€” makes the .dynamic section read-only. the DT_DEBUG tag will not be emitted ๐Ÿ™…โ€โ™€๏ธ
  • separate-loadable-segments, separate-code, noseparate-code โ€” specify whether two adjacent PT_LOAD segments are allowed to overlap in pages. noseparate-code (default) allows overlap. separate-code allows overlap between two executable segments, or between two non-executable segments. separate-loadable-segments disallows overlap ๐Ÿ“
  • shstk โ€” x86 only, use shadow stack ๐Ÿงต
  • gcs=[implicit|never|always] โ€” specifies how the GNU_PROPERTY_AARCH64_FEATURE_1_GCS bit gets set in the output ELF file. when set, it indicates support for the Guarded Control Stack (GCS) security feature. implicit (default): the GCS feature bit is set if **all input relocatable files have a .note.gnu.property section containing the GNU_PROPERTY_AARCH64_FEATURE_1_GCS bit. never: clears the GCS feature bit regardless of input relocatable files' markings. always: sets the GCS feature bit regardless of input object markings ๐Ÿ›ก๏ธ
  • *stack-size=size* โ€” sets the main thread's stack size to size. the stack size is recorded as the size of the size. PT_GNU_STACK program segment ๐Ÿ“
  • start-stop-gc โ€” don't let __start_/__stop_ references retain the associated C identifier name sections (default) ๐Ÿงน
  • nostart-stop-gc โ€” makes those __start_/__stop_ references keep the C identifier name sections they point at, instead of GC-ing them away. bestie, this is the "pls don't delete my stuff" flag ๐Ÿ’…
  • text โ€” nope, no relocations against read-only segments, we don't do that here. this one is the default, so you're already living it โœจ
  • nobtcfi โ€” creates a PT_OPENBSD_NOBTCFI segment ๐Ÿฆ‹
  • wxneeded โ€” creates a PT_OPENBSD_WXNEEDED segment ๐ŸŒธ

๐Ÿ’– ENVIRONMENT VARIABLES ๐Ÿ’–

okay so ld.lld also reads your vibes from the environment, and by vibes i mean these two:

  • LLD_REPRODUCE โ€” create a reproduce tarball with the filename you give it. but heads up ๐Ÿ‘‘: if --reproduce is also specified, --reproduce takes precedence. the flag wins, the env var loses, that's the hierarchy, periodt.
  • LLD_VERSION โ€” ld.lld creates a section named .comment containing the LLD version string, and this env var lets you override that version string. why would you want that? because it eliminates differences in the binary caused by LLD version number differences ๐ŸŽ€ literally so useful if you're diffing builds, iykyk.

๐Ÿงš IMPLEMENTATION NOTES ๐Ÿงš

okay this is lowkey the juiciest part, stay with me ๐Ÿซถ

ld.lld's handling of archive files (the ones with a .a file extension) is different from traditional linkers used on Unix-like systems. not better, not worse, just built different, bestie.

๐Ÿ’€ how traditional linkers do it ๐Ÿ’€

Traditional linkers maintain a set of undefined symbols during linking. the linker processes each file in the order in which it appears on the command line, until the set of undefined symbols becomes empty. an object file is linked into the output object when it's encountered, with its undefined symbols added to the set. upon encountering an archive file, a traditional linker searches the objects contained therein, and processes those that satisfy symbols in the unresolved set.

and like... handling mutually dependent archives may be awkward when you're using a traditional linker ๐Ÿ˜ญ archive files may have to be specified multiple times, or the special command line options --start-group and --end-group may be used to have the linker loop over the files in the group until no new symbols are added to the set. yes, that's the lore. yes, it's annoying.

๐ŸŒˆ how ld.lld does it ๐ŸŒˆ

ld.lld records all symbols found in objects and archives as it iterates over command line arguments. so when ld.lld encounters an undefined symbol that can be resolved by an object file contained in a previously processed archive file, it immediately extracts and links it into the output object. no looping, no re-specifying, no drama โœจ

โš ๏ธ but fr fr: with certain archive inputs, ld.lld may produce different results compared to traditional linkers. in practice, large bodies of third party software have been linked with ld.lld without material issues, so like... it's giving "trust me bestie" and honestly it delivers ๐Ÿซถ

and if you wanna catch the sketchy cases? the --warn-backrefs option may be used to identify a linker invocation that may be incompatible with traditional Unix-like linker behavior. it's the vibe check for your link line ๐Ÿ‘‘


and that's the whole page, babe ๐ŸŒŸ you just read the entire LD.LLD(1) โ€” this one's a Debian joint, dated Jul 25, 2023, and it's literally iconic for it ๐Ÿ’œ go forth and link responsibly ๐Ÿ˜ญ๐Ÿซถ๐ŸŽ€

๐Ÿ“ฆ where this page comes from