rucc_sysroot/link.rs
1//! The start files, the libraries and the loader for one target's link.
2//!
3//! Design: `spec/cross-compile/08-sysroots.md` section 8.2 and `spec/cross-compile/11-linking.md`.
4//!
5//! What is here is what has to be linked, in what order, and which loader will start the result.
6//! How that is spelled for a particular linker is [`crate::argv`], which is the division
7//! `spec/cross-compile/11-linking.md` draws: the files are a fact about the target and the flags are
8//! a fact about the linker.
9//!
10//! # Why musl is first
11//!
12//! `spec/cross-compile/09-libc-stubs.md` section 9.3 is the argument. musl exercises the header
13//! tree, the search paths, the start files, the compiler runtime and the link line, and it does
14//! that without symbol versioning and without stub generation, which are the two hardest pieces of
15//! the glibc path. If a musl cross link works end to end then the pipeline is right and what is
16//! left for M9.5 is the glibc specific parts rather than the shape of the thing.
17//!
18//! # Why the line has three parts and not one
19//!
20//! `crtn.o` goes after the libraries and `crti.o` goes before them, because between them they open
21//! and close the `.init` and `.fini` sections and anything contributing to those has to land in the
22//! middle. A link line that is one list gets this wrong in a way that produces a binary which links,
23//! runs, and does not run its static constructors, so the three parts are three fields here rather
24//! than a comment on an ordering somebody has to preserve.
25
26use std::path::{Path, PathBuf};
27
28use rucc_tuple::{Abi, Arch, DataModel, Endian, Env, ObjectFormat, Os, TargetTuple, Version};
29
30use crate::layout::Sysroot;
31
32/// How the program is linked, which decides the first start file and the flags.
33///
34/// The glibc release that moved the `stat` family out of `libc_nonshared.a` and into `libc.so.6`.
35const STAT_IN_LIBC: Version = Version::new(2, 33);
36
37/// Five cases rather than two booleans for static and position independent, because the two are not
38/// independent and the start file is a different file in four of the five. A pair of flags would
39/// admit a sixth combination, a shared object that is not position independent, which is not a thing
40/// any of these linkers will produce.
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
42pub enum LinkMode {
43 /// Everything in the binary, no interpreter, no relocation at load. The default for musl, and
44 /// the mode `spec/cross-compile/02-the-goal.md`'s exit criterion names.
45 #[default]
46 Static,
47 /// Static, and position independent, so the loader may place it anywhere. A different first
48 /// start file, because the program has to relocate itself before `main` and `rcrt1.o` is what
49 /// does that.
50 StaticPie,
51 /// Against the shared libc, position independent, with the libc's loader named in the program
52 /// header. What every distribution builds today and what `-pie` asks for.
53 Dynamic,
54 /// Against the shared libc, at a fixed address, which is `-no-pie`.
55 ///
56 /// The same link as [`LinkMode::Dynamic`] with a different start file, because the reference to
57 /// `main` in `crt1.o` is an absolute one and the reference in `Scrt1.o` is not. Build systems
58 /// that pass `-no-pie` are usually doing it because something in them takes the address of a
59 /// function and compares it, and they get the file that matches.
60 DynamicNoPie,
61 /// A shared object rather than a program, which is `-shared`.
62 ///
63 /// No start file at all, since nothing starts a shared object and it has no `main` to be
64 /// started at, and no loader named either: the program that loads this one carries that.
65 Shared,
66}
67
68impl LinkMode {
69 /// Whether the result is linked against a shared libc, which decides whether a loader is named.
70 #[must_use]
71 pub const fn is_dynamic(self) -> bool {
72 matches!(self, LinkMode::Dynamic | LinkMode::DynamicNoPie | LinkMode::Shared)
73 }
74
75 /// Whether the result may be placed anywhere in memory.
76 #[must_use]
77 pub const fn is_pie(self) -> bool {
78 matches!(self, LinkMode::StaticPie | LinkMode::Dynamic | LinkMode::Shared)
79 }
80}
81
82/// What a produced sysroot holds for a target's C library.
83///
84/// Four cases, from `spec/cross-compile/08-sysroots.md` section 8.2's table, and the line differs
85/// between them in what goes on it rather than in how it is spelled. The table has seven rows and two
86/// of those are legal walls rather than technical ones, so what is left is these four.
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub enum Libc {
89 /// Nothing, which is the freestanding row: the nine compiler headers and no link inputs at all.
90 /// Our runtime is still there, because an architecture without a division instruction needs it
91 /// whether there is a libc or not.
92 None,
93 /// A real static archive, which today means musl built from source. The only row where the code
94 /// behind the names is present, so the only row a static link can use.
95 Archive,
96 /// A generated stub shared object: the names the platform's libc exports and none of the code.
97 /// glibc is the row this was written for, and bionic, the BSDs and illumos take the same shape
98 /// for the same reason, which is that their libc is a shared object on the target machine and a
99 /// list of names is enough to link against one.
100 Stub,
101 /// A set of import libraries, which is what the same idea is called in COFF.
102 ///
103 /// Windows is the row this is, and it is a separate case from [`Libc::Stub`] rather than a
104 /// spelling of it, for two reasons that both show up on the line. The container is different: a
105 /// Windows program links against an archive of tiny objects per DLL rather than against one
106 /// shared object, which is `spec/cross-compile/09-libc-stubs.md` section 9.4 and what
107 /// `rucc_stub::coff` writes. And the C library is not one file: the msvcrt import library,
108 /// mingw-w64's own `libmingwex.a` and `libmoldname.a`, and the Win32 libraries a CRT calls into
109 /// are all on the line, where a glibc line has one `libc.so` on it.
110 ///
111 /// A static link against this is not refused, which is the other difference. On Windows the C
112 /// library is a DLL on every machine and always has been, so `-static` there is a statement
113 /// about our libraries and mingw-w64's rather than about the CRT, and a program linked that way
114 /// runs. That is why the refusal in [`crate::argv::argv`] is about [`Libc::Stub`] by name.
115 Import,
116}
117
118/// Which of the four cases this target is.
119///
120/// Asked in two places, which is why it is a function rather than a `match` in each: [`LinkLine`]
121/// uses it to pick the files and [`crate::argv::argv`] uses it to refuse a static link against a
122/// stub. Two copies of this rule would be two rules.
123///
124/// The format is asked before the environment, because what holds a libc's names is a property of
125/// the object format and `Env::Gnu` means mingw-w64 on a Windows target and glibc on a Linux one.
126#[must_use]
127pub fn libc(target: TargetTuple) -> Libc {
128 match (target.os(), target.env()) {
129 (Os::None, _) => Libc::None,
130 _ if target.object_format() == ObjectFormat::Coff => Libc::Import,
131 // wasi-libc is musl with the system interface replaced by WASI calls, and it is a real
132 // static archive in the same way. There is no shared form of it to stub.
133 (Os::Wasi, _) => Libc::Archive,
134 (_, Env::Musl) => Libc::Archive,
135 _ => Libc::Stub,
136 }
137}
138
139/// Which of Microsoft's two C runtimes a program in the MSVC environment is linked against.
140///
141/// `cl.exe` spells the choice `/MT` and `/MD`, and here it is `-fms-runtime-lib=static` and
142/// `-fms-runtime-lib=dll`, which is clang's spelling. Two sets of libraries and two macros: the
143/// headers read `_MT` for both and `_DLL` only for the second, and that is how a header knows its
144/// functions are imported from a DLL rather than linked into the program.
145///
146/// The static one is the default, which is the opposite of `cl.exe`, and the reason is the machine
147/// the program is copied to. The universal CRT is part of Windows 10 and later, but the Visual C++
148/// runtime is not: `vcruntime140.dll` arrives with the redistributable, and a program linked `/MD`
149/// on a machine that never installed it does not start. A program linked `/MT` carries both and
150/// starts anywhere, which is what the mingw-w64 rows give without being asked.
151#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
152pub enum Crt {
153 /// `/MT`: `libcmt.lib`, `libucrt.lib` and `libvcruntime.lib`, all three linked into the
154 /// program.
155 #[default]
156 Static,
157 /// `/MD`: `msvcrt.lib`, `ucrt.lib` and `vcruntime.lib`, which are import libraries for
158 /// `ucrtbase.dll` and `vcruntime140.dll` with the startup code in the first of them.
159 Dll,
160}
161
162/// Our own runtime library, which every one of the three lines below carries.
163///
164/// Named once because three callers ask about it by name: the line that puts it on,
165/// [`crate::argv::argv`] when `-fno-builtins-lib` asks for it to be left off, and the driver that
166/// goes looking for the file. A second spelling of the name anywhere is a flag that stops working
167/// the day the first one is renamed.
168///
169/// Where the file is, is not this crate's answer and used to be. Every line below named it inside
170/// the sysroot, as `sysroot.lib().join(BUILTINS)`, and nothing ever put it there: it is this
171/// compiler's own output for the target rather than anything the platform ships, `cargo xtask
172/// builtins` writes it beside the compiler, and a sysroot fetched from a release will never hold
173/// it. So the lines take the path from whoever built them, which is the driver, and this constant
174/// is the name alone. tamnd/rucc#1514.
175pub const BUILTINS: &str = "librucc_builtins.a";
176
177/// Our runtime as a list, which is what every line below puts at the end of its libraries.
178///
179/// One function rather than the same `into_iter` at four call sites, and it takes the whole answer
180/// rather than a path so that a line reads the same whether the file was found or not.
181fn ours(builtins: Option<&Path>) -> Vec<PathBuf> {
182 builtins.map(Path::to_path_buf).into_iter().collect()
183}
184
185/// The inputs to a link, in the three groups a linker needs them in.
186///
187/// Paths rather than strings, and no flags at all, because
188/// `spec/cross-compile/11-linking.md` owns which linker is invoked and how its arguments are
189/// spelled and [`crate::argv`] is where that happens. What is here is what has to be linked and in
190/// what order, which is a target fact and the same fact whichever linker reads it.
191#[derive(Debug, Clone, PartialEq, Eq)]
192pub struct LinkLine {
193 /// The start files, before the user's objects.
194 pub start: Vec<PathBuf>,
195 /// The libraries, after the user's objects.
196 pub libraries: Vec<PathBuf>,
197 /// The end files, after the libraries.
198 pub end: Vec<PathBuf>,
199}
200
201impl LinkLine {
202 /// The line for a link against this sysroot, whichever libc the target names.
203 ///
204 /// The dispatch rather than the line, and it is [`libc`] that decides: a real archive, a stub
205 /// shared object, or nothing. The three methods below are the three answers. A target whose
206 /// sysroot we do not produce yet still gets the right shape, because the shape follows from
207 /// whether the libc on the target machine is an archive or a shared object and that is known
208 /// before any of it is built.
209 ///
210 /// The runtime is a path from the caller rather than a name joined onto the sysroot, and
211 /// [`None`] means it is not on this machine and the line goes without it. Whether that is worth
212 /// refusing over is the driver's question, since the driver is what knows whether it looked.
213 #[must_use]
214 pub fn for_target(sysroot: &Sysroot, mode: LinkMode, builtins: Option<&Path>) -> Self {
215 match libc(sysroot.target()) {
216 Libc::None => LinkLine::freestanding(builtins),
217 Libc::Archive if sysroot.target().os() == Os::Wasi => {
218 LinkLine::wasi(sysroot, false, builtins)
219 }
220 Libc::Archive => LinkLine::musl(sysroot, mode, builtins),
221 Libc::Stub => LinkLine::glibc(sysroot, mode, builtins),
222 Libc::Import if sysroot.target().env() == Env::Msvc => {
223 LinkLine::msvc(Crt::Static, builtins)
224 }
225 Libc::Import => LinkLine::mingw(sysroot, mode, builtins),
226 }
227 }
228
229 /// The line for a freestanding link against this sysroot, which is our runtime and nothing else.
230 ///
231 /// Section 8.2's first row: nine compiler headers and no link inputs. There is no `crt1.o`,
232 /// because nothing here decides what runs before `main` or whether there is a `main` at all, and
233 /// no `crti.o` or `crtn.o`, because those come from a libc too. A kernel or a bootloader brings
234 /// its own start file and says so with `-nostartfiles`, which it would have to pass anyway.
235 ///
236 /// `librucc_builtins.a` stays, because it is ours rather than the platform's.
237 /// `spec/cross-compile/10-runtime.md` is the argument: an architecture with no division
238 /// instruction needs `__divti3` whether there is a libc in the picture or not, and freestanding
239 /// code that does 64-bit arithmetic on a 32-bit target reaches it without asking.
240 ///
241 /// The mode is not a parameter because it changes nothing here. Every difference between the
242 /// modes is a start file and there are none. Neither is the sysroot, now that the one file on
243 /// this line is not in it: a freestanding link reads headers out of a sysroot and links nothing
244 /// out of one.
245 #[must_use]
246 pub fn freestanding(builtins: Option<&Path>) -> Self {
247 LinkLine { start: Vec::new(), libraries: ours(builtins), end: Vec::new() }
248 }
249
250 /// The line for a musl link against this sysroot.
251 ///
252 /// `crt1.o` runs before `main` and calls it. `crti.o` and `crtn.o` are the prologue and the
253 /// epilogue of the `.init` and `.fini` sections, which is why one is at the front and the other
254 /// is at the very back. `libc.a` carries musl's whole C library, and `librucc_builtins.a`
255 /// carries the operations the architecture does not have an instruction for, which
256 /// `spec/cross-compile/10-runtime.md` says has to be ours rather than the platform's.
257 ///
258 /// The builtins go after `libc.a` because musl calls some of them, and an archive that is
259 /// searched before the thing that needs it contributes nothing.
260 ///
261 /// `libc.a` on every mode including the dynamic ones, because what a musl sysroot here holds is
262 /// musl built static: section 9.3 takes musl first precisely because one tarball built one way
263 /// exercises the whole pipeline, and a shared musl is a second build of it that buys nothing
264 /// until somebody asks for a dynamically linked musl program.
265 #[must_use]
266 pub fn musl(sysroot: &Sysroot, mode: LinkMode, builtins: Option<&Path>) -> Self {
267 let lib = sysroot.lib();
268 let mut libraries = vec![lib.join("libc.a")];
269 libraries.extend(ours(builtins));
270 LinkLine { start: start_files(&lib, mode), libraries, end: vec![lib.join("crtn.o")] }
271 }
272
273 /// The line for a WASI link against this sysroot, which is a command unless `reactor` says
274 /// that it is a reactor.
275 ///
276 /// One start file and no end file. A wasm module has no `.init` section for `crti.o` and
277 /// `crtn.o` to open and close: the linker writes `__wasm_call_ctors` from the init functions of
278 /// the objects, and the start file calls it. `crt1-command.o` has `_start`, which runs the
279 /// constructors, then `main` through `__main_void`, then the destructors, and then exits with
280 /// the status. `crt1-reactor.o` has `_initialize`, which runs the constructors and nothing
281 /// more, for a module whose host calls its exports after that. The mode does not change the
282 /// line, because a wasm module is one kind of image: there is no loader, no interpreter and no
283 /// address to place it at.
284 ///
285 /// `libc.a` is wasi-libc whole, `libm` included, as on musl. Our runtime goes after it for the
286 /// same reason as on musl, which is that wasi-libc calls `__multf3` and its friends for `long
287 /// double`. tamnd/rucc#2864.
288 ///
289 /// `libsetjmp.a` comes before `libc.a`. It has `__wasm_setjmp`, `__wasm_setjmp_test`,
290 /// `__wasm_longjmp` and the tag `__c_longjmp`, which the code that rucc writes for a call of
291 /// `setjmp` or `longjmp` refers to. wasm-ld takes a member of an archive only for a symbol that
292 /// is undefined, so a program with no `setjmp` gets nothing from it, and it is always on the
293 /// line. clang puts it there only for `-lsetjmp`. tamnd/rucc#2865.
294 #[must_use]
295 pub fn wasi(sysroot: &Sysroot, reactor: bool, builtins: Option<&Path>) -> Self {
296 let lib = sysroot.lib();
297 let start = if reactor { "crt1-reactor.o" } else { "crt1-command.o" };
298 let mut libraries = vec![lib.join("libsetjmp.a"), lib.join("libc.a")];
299 libraries.extend(ours(builtins));
300 LinkLine { start: vec![lib.join(start)], libraries, end: Vec::new() }
301 }
302
303 /// The line for a link against a generated stub, which is glibc and every other hosted libc that
304 /// is not musl.
305 ///
306 /// Named for glibc because glibc is the case `spec/cross-compile/09-libc-stubs.md` is written
307 /// about and the hard one. bionic, the BSDs and illumos reach the same line for the same reason:
308 /// their libc is a shared object on the target machine, so a list of the names it exports is
309 /// enough to link against it, and that is what `rucc-stub` produces. The paragraphs below about
310 /// `libc_nonshared.a` and `libm` are glibc's own.
311 ///
312 /// The same start files as musl's and different libraries. A stub libc is linked against
313 /// dynamically, so what goes on the line is the `libc.so` `rucc-stub` wrote into
314 /// [`Sysroot::stubs`] rather than an archive, and the version nodes in it are what make a
315 /// program built here run on an older machine. The driver writes it before the link, cut at
316 /// the release the tuple names, and the directory is on the line as a `-L` too so that `-lm`
317 /// and `-lpthread` find their stubs there.
318 ///
319 /// `libc_nonshared.a` comes next and out of the sysroot. glibc's own `libc.so` is a linker
320 /// script naming `libc.so.6`, `libc_nonshared.a` and the loader as a group, and that archive
321 /// holds real compiled objects: `atexit`, `__stack_chk_fail_local` on i386, and the `stat`
322 /// family on releases before 2.33. None of it can be written from a description, because a
323 /// stub is a list of names and these are bodies, so it is built from glibc's sources and
324 /// fetched with the start files. It goes after the stub because what is in it calls into
325 /// libc, which is the order glibc's script gives them. It is glibc's alone, so bionic and the
326 /// BSDs get the stub and nothing between it and our runtime.
327 ///
328 /// The archive in the sysroot is 2.44's, and 2.44's has no `stat` in it, because 2.33 moved the
329 /// family into `libc.so.6`. A target pinned before 2.33 gets `libc_nonshared_stat.a` after it,
330 /// which rucc-cross builds the way 2.32 built those ten functions, each a call to `__xstat` or
331 /// one of its siblings. Without it a program that calls `stat` links at -O2, where the old
332 /// headers inline the call, and not at -O0. A target with no pin is the newest release and
333 /// does not get it.
334 ///
335 /// The loader comes last, as in glibc's script. `ld.so` in the stubs is a script with the
336 /// loader's stub inside `AS_NEEDED`, so a program gets the loader in `DT_NEEDED` only when it
337 /// uses one of its names. On AArch64, Arm, RISC-V and LoongArch the stack protector reads
338 /// `__stack_chk_guard`, and the loader exports it there.
339 ///
340 /// `libm.so` is not on the line, and that is a decision. glibc's `libm` is real code and a
341 /// program that wants it passes `-lm`, which every build system that does arithmetic already
342 /// does, so putting it on every line would record a dependency the program does not have.
343 ///
344 /// A static glibc link is not one of these: there is no `libc.a` in a sysroot whose libc is a
345 /// stub, because a stub is a list of names and a static link needs bodies.
346 /// [`crate::argv::argv`] refuses that combination by name rather than producing this line with
347 /// `-static` in front of it.
348 #[must_use]
349 pub fn glibc(sysroot: &Sysroot, mode: LinkMode, builtins: Option<&Path>) -> Self {
350 let lib = sysroot.lib();
351 let mut libraries = vec![sysroot.stubs().join("libc.so")];
352 if sysroot.target().env() == Env::Gnu {
353 libraries.push(lib.join("libc_nonshared.a"));
354 let version = sysroot.target().env_version();
355 if version.is_some_and(|version| !version.at_least(STAT_IN_LIBC)) {
356 libraries.push(lib.join("libc_nonshared_stat.a"));
357 }
358 libraries.push(sysroot.stubs().join("ld.so"));
359 }
360 libraries.extend(ours(builtins));
361 LinkLine { start: start_files(&lib, mode), libraries, end: vec![lib.join("crtn.o")] }
362 }
363
364 /// The line for a mingw-w64 link against this sysroot.
365 ///
366 /// One start file and no end file, which is the first thing that is different from every ELF
367 /// line above. `crt2.o` runs before `main` and calls it, `dllcrt2.o` is its counterpart for a
368 /// DLL, and there is no `crti.o` and no `crtn.o` because PE has no `.init` and `.fini` sections
369 /// for a pair of files to open and close. What those two bracket on ELF is done on Windows by a
370 /// table of pointers in the `.CRT$XC` sections, which the linker sorts by section name, so the
371 /// ordering problem the three groups exist for does not arise here.
372 ///
373 /// `crtbegin.o` and `crtend.o` are deliberately absent. They are GCC's files rather than
374 /// mingw-w64's, they bracket GCC's own list of constructors, and a toolchain that is not GCC
375 /// writes that list the way the platform writes it instead. Ours is not written yet: a mingw
376 /// link runs `main` and does not run a file scope constructor, which is a known gap that belongs
377 /// with the sysroot build rather than with the line, and the gap is in the codegen for the
378 /// format rather than here.
379 ///
380 /// The libraries are a set rather than one file, because the C library on Windows is several
381 /// DLLs and the CRT calls into the system ones. `libmingw32.a` holds the start code `crt2.o`
382 /// calls, `libmoldname.a` is the layer that gives the old unprefixed spellings of the names
383 /// Microsoft deprecated, `libmingwex.a` is everything C requires that the CRT does not have, and
384 /// `libmsvcrt.a` is the import library for the CRT itself. Then the four Win32 libraries that
385 /// mingw-w64's own code calls into, which are on the line for the same reason they are on gcc's:
386 /// a program that uses none of them directly still reaches `kernel32` through `malloc`.
387 ///
388 /// `libmsvcrt.a` is the right name for either CRT, which is why nothing here asks the sysroot
389 /// which one it has. mingw-w64 installs it as a copy of the default runtime's import library, so
390 /// in the UCRT sysroot this compiler fetches it is `libucrt.a` and names the `api-ms-win-crt-*`
391 /// API sets, and in a `-msvcrt` sysroot it names `msvcrt.dll`. gcc's spec says `-lmsvcrt` on a
392 /// UCRT toolchain for the same reason, so the line follows the sysroot without being told.
393 ///
394 /// `-pthread` adds `-lpthread` after the objects, as on every other target, and the sysroot's
395 /// `libpthread.a` is winpthreads, built static so the program needs no `libwinpthread-1.dll`.
396 /// It is not on this line otherwise. A gcc built with the posix thread model lists it for every
397 /// program, and a program that asked for no threads has nothing to take from it.
398 ///
399 /// The order is the one a single pass linker needs, which is GNU ld's PE port: a library after
400 /// everything that calls into it. `librucc_builtins.a` is last for the reason it is last on the
401 /// musl line, which is that the things before it call it and it calls none of them. lld's COFF
402 /// linker resolves archives to a fixed point and does not care about any of this, and writing
403 /// the line for the stricter of the two is what makes one line serve both.
404 #[must_use]
405 pub fn mingw(sysroot: &Sysroot, mode: LinkMode, builtins: Option<&Path>) -> Self {
406 let lib = sysroot.lib();
407 let start = match mode {
408 LinkMode::Shared => "dllcrt2.o",
409 _ => "crt2.o",
410 };
411 let theirs = [
412 "libmingw32.a",
413 "libmoldname.a",
414 "libmingwex.a",
415 "libmsvcrt.a",
416 "libadvapi32.a",
417 "libshell32.a",
418 "libuser32.a",
419 "libkernel32.a",
420 ];
421 let mut libraries: Vec<PathBuf> = theirs.iter().map(|name| lib.join(name)).collect();
422 libraries.extend(ours(builtins));
423 LinkLine { start: vec![lib.join(start)], libraries, end: Vec::new() }
424 }
425
426 /// The line for a program in Microsoft's environment, linked against the CRT `crt` names.
427 ///
428 /// No start file, because the one Microsoft ships is not a file of its own. `mainCRTStartup` is
429 /// a member of `libcmt.lib`, or of `msvcrt.lib` for the other runtime, and the linker picks it
430 /// as the entry point because the program defines `main`, so the start of the program is left
431 /// to the CRT the way `cl.exe` leaves it. The mode is not a parameter for the same reason: a
432 /// DLL is started by `_DllMainCRTStartup`, which is in the same libraries.
433 ///
434 /// Names rather than paths, which is where this differs from every other line here. The tree
435 /// keeps the CRT in `crt/lib` and the SDK's libraries in two directories of `sdk/lib`, and
436 /// [`crate::argv`] names those directories to the linker, which is how `lld-link` and
437 /// `link.exe` both look for a library and how a `-L` of the user's own gets in front of ours.
438 ///
439 /// `kernel32.lib` because the CRT calls into it and says so only in a directive, and
440 /// `oldnames.lib` because it is what makes `open` and `strdup` mean `_open` and `_strdup`,
441 /// which a C program written for anything but Windows calls by the old names. Ours goes last,
442 /// the same as on the mingw-w64 line, so that the CRT answers for everything it has.
443 #[must_use]
444 pub fn msvc(crt: Crt, builtins: Option<&Path>) -> Self {
445 let theirs = match crt {
446 Crt::Static => ["libcmt.lib", "libucrt.lib", "libvcruntime.lib"],
447 Crt::Dll => ["msvcrt.lib", "ucrt.lib", "vcruntime.lib"],
448 };
449 let mut libraries: Vec<PathBuf> = theirs.iter().map(PathBuf::from).collect();
450 libraries.push(PathBuf::from("kernel32.lib"));
451 libraries.push(PathBuf::from("oldnames.lib"));
452 libraries.extend(ours(builtins));
453 LinkLine { start: Vec::new(), libraries, end: Vec::new() }
454 }
455
456 /// Every input, in the order they reach the linker, with the caller's objects in the middle.
457 ///
458 /// The one function that knows the whole order, so that a caller cannot assemble the three
459 /// groups in the wrong sequence.
460 #[must_use]
461 pub fn with_objects(&self, objects: &[PathBuf]) -> Vec<PathBuf> {
462 let mut all = self.start.clone();
463 all.extend_from_slice(objects);
464 all.extend(self.libraries.iter().cloned());
465 all.extend(self.end.iter().cloned());
466 all
467 }
468}
469
470/// The start files for one mode, in the order they go on the line.
471///
472/// Two files, and which the first one is says how the reference to `main` inside it is written.
473/// `crt1.o` refers to it absolutely, `Scrt1.o` through the global offset table so that a loader may
474/// place the program anywhere, and `rcrt1.o` does that and relocates the program itself before
475/// `main` runs, which is what a static position independent executable needs because there is no
476/// loader to do it. A shared object has none of them.
477///
478/// `crti.o` is always second and `crtn.o` is always last, which is [`LinkLine`]'s three groups
479/// rather than anything here.
480fn start_files(lib: &Path, mode: LinkMode) -> Vec<PathBuf> {
481 let first = match mode {
482 LinkMode::Static | LinkMode::DynamicNoPie => Some("crt1.o"),
483 LinkMode::StaticPie => Some("rcrt1.o"),
484 LinkMode::Dynamic => Some("Scrt1.o"),
485 LinkMode::Shared => None,
486 };
487 first.map(|name| lib.join(name)).into_iter().chain([lib.join("crti.o")]).collect()
488}
489
490/// The absolute path the target's loader is installed at, or [`None`] for a target that has none.
491///
492/// The libc picks the table and the architecture picks the row. [`None`] is the right answer for
493/// three different reasons: a freestanding target has no libc, WASI has no loader of this kind at
494/// all, and Darwin and Windows have one whose path is not written on the link line.
495#[must_use]
496pub fn loader(target: TargetTuple) -> Option<&'static str> {
497 match (target.os(), target.env()) {
498 (Os::Linux, Env::Musl) => Some(musl_loader(target)),
499 (Os::Linux, Env::Gnu) => Some(glibc_loader(target)),
500 // Bionic's is one path per word size and not one per architecture, because Android fixes
501 // the filesystem layout rather than leaving it to the port.
502 (Os::Linux, Env::Android) => Some(match target.pointer_width() {
503 64 => "/system/bin/linker64",
504 _ => "/system/bin/linker",
505 }),
506 _ => None,
507 }
508}
509
510/// The absolute path musl's loader is installed at on the target.
511///
512/// It goes in the program header of a dynamically linked binary, so it is a string about the target
513/// machine's filesystem and not about ours, and it has to be right without anything to check it
514/// against at link time. A wrong one produces a binary that the kernel refuses to start with a
515/// message about a missing file that is on nobody's disk.
516///
517/// 32-bit ARM is the row with two answers, because musl names the hard float and soft float builds
518/// differently and they are not interchangeable. PowerPC is the other row with two, and there the
519/// endianness picks, because musl treats the two byte orders as separate ports.
520#[must_use]
521pub fn musl_loader(target: TargetTuple) -> &'static str {
522 match target.arch() {
523 Arch::X86_64 => match target.data_model() {
524 DataModel::Ilp32On64 => "/lib/ld-musl-x32.so.1",
525 _ => "/lib/ld-musl-x86_64.so.1",
526 },
527 Arch::X86 => "/lib/ld-musl-i386.so.1",
528 Arch::Aarch64 | Arch::Arm64Ec => "/lib/ld-musl-aarch64.so.1",
529 Arch::Arm => match target.resolved_abi() {
530 Abi::DoubleFloat => "/lib/ld-musl-armhf.so.1",
531 _ => "/lib/ld-musl-arm.so.1",
532 },
533 Arch::Riscv64 => "/lib/ld-musl-riscv64.so.1",
534 Arch::Riscv32 => "/lib/ld-musl-riscv32.so.1",
535 Arch::S390x => "/lib/ld-musl-s390x.so.1",
536 Arch::PowerPc64 => match target.endian() {
537 Endian::Little => "/lib/ld-musl-powerpc64le.so.1",
538 Endian::Big => "/lib/ld-musl-powerpc64.so.1",
539 },
540 Arch::LoongArch64 => "/lib/ld-musl-loongarch64.so.1",
541 // musl has no wasm port and wasm has no loader. The caller that gets here asked for a
542 // dynamic musl link on a target with neither, which is a driver bug rather than a user
543 // one, and a path that cannot exist is a better report than a plausible wrong one.
544 Arch::Wasm32 => "/lib/ld-musl-none.so.1",
545 }
546}
547
548/// The absolute path glibc's loader is installed at on the target.
549///
550/// A different table from musl's and not a different spelling of it. musl names every loader after
551/// the architecture in one directory; glibc's names come from each port's history, so three of them
552/// are called `ld64.so` with a number that means something different per architecture, two are in
553/// `/lib64` rather than `/lib`, and i386's carries no architecture in its name at all because it was
554/// the only one when it was named.
555///
556/// The rows with more than one answer are the ones where the loader and the program have to agree
557/// about register usage. 32-bit ARM has the hard float and soft float split, RISC-V and LoongArch
558/// spell the float ABI and the data model into the name, and AArch64 has a byte order in it.
559/// Getting one wrong produces a binary the kernel will not start, with a message about a missing
560/// file, and it is a string nothing at link time can check.
561#[must_use]
562pub fn glibc_loader(target: TargetTuple) -> &'static str {
563 let narrow = target.data_model() == DataModel::Ilp32On64;
564 let hard = matches!(target.resolved_abi(), Abi::DoubleFloat);
565 match target.arch() {
566 Arch::X86_64 if narrow => "/libx32/ld-linux-x32.so.2",
567 Arch::X86_64 => "/lib64/ld-linux-x86-64.so.2",
568 Arch::X86 => "/lib/ld-linux.so.2",
569 Arch::Aarch64 | Arch::Arm64Ec => match (target.endian(), narrow) {
570 (Endian::Little, false) => "/lib/ld-linux-aarch64.so.1",
571 (Endian::Little, true) => "/lib/ld-linux-aarch64_ilp32.so.1",
572 (Endian::Big, false) => "/lib/ld-linux-aarch64_be.so.1",
573 (Endian::Big, true) => "/lib/ld-linux-aarch64_be_ilp32.so.1",
574 },
575 // The one row where the number differs rather than the name. ARM's loader went to 3 when
576 // EABI replaced OABI, and the hard float build is a separate file because passing a double
577 // in a float register is not compatible with passing it in a pair of integer ones.
578 Arch::Arm if hard => "/lib/ld-linux-armhf.so.3",
579 Arch::Arm => "/lib/ld-linux.so.3",
580 Arch::Riscv64 if hard => "/lib/ld-linux-riscv64-lp64d.so.1",
581 Arch::Riscv64 => "/lib/ld-linux-riscv64-lp64.so.1",
582 Arch::Riscv32 if hard => "/lib/ld-linux-riscv32-ilp32d.so.1",
583 Arch::Riscv32 => "/lib/ld-linux-riscv32-ilp32.so.1",
584 // `ld64` here means 64-bit z/Architecture and the 1 is glibc's ABI version for the port,
585 // which is not the 2 on PowerPC's file of the same name. It is in `/lib` and PowerPC's is in
586 // `/lib64`, so the two rows have nothing in common but the stem.
587 Arch::S390x => "/lib/ld64.so.1",
588 // ELFv2, both byte orders, which is the only PowerPC ABI
589 // `spec/cross-compile/06-abis.md` admits. The ELFv1 big-endian world uses `ld64.so.1` and
590 // is out of scope, so a wrong answer here is impossible rather than merely unlikely.
591 Arch::PowerPc64 => "/lib64/ld64.so.2",
592 Arch::LoongArch64 if hard => "/lib64/ld-linux-loongarch-lp64d.so.1",
593 Arch::LoongArch64 => "/lib64/ld-linux-loongarch-lp64s.so.1",
594 // There is no glibc for wasm and no loader for it either. Same reasoning as the musl table
595 // above: a path nothing will ever open beats a plausible one.
596 Arch::Wasm32 => "/lib/ld-linux-wasm32.so.1",
597 }
598}