Skip to content

Commit b7994f9

Browse files
committed
linker: Reorder linker arguments
- Combine all native library arguments together, to simplify potential support for library deduplication and similar things - Split arguments into order-independent and order-dependent, to define more precisely what (pre,post,late)-link-args mean
1 parent dca3acb commit b7994f9

File tree

2 files changed

+151
-146
lines changed

2 files changed

+151
-146
lines changed

compiler/rustc_codegen_ssa/src/back/link.rs

+147-117
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
10581058
}
10591059
}
10601060

1061-
fn link_sanitizers(sess: &Session, crate_type: CrateType, linker: &mut dyn Linker) {
1061+
fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut dyn Linker) {
10621062
// On macOS the runtimes are distributed as dylibs which should be linked to
10631063
// both executables and dynamic shared objects. Everywhere else the runtimes
10641064
// are currently distributed as static liraries which should be linked to
@@ -1738,12 +1738,13 @@ fn add_rpath_args(
17381738
}
17391739

17401740
/// Produce the linker command line containing linker path and arguments.
1741-
/// `NO-OPT-OUT` marks the arguments that cannot be removed from the command line
1742-
/// by the user without creating a custom target specification.
1743-
/// `OBJECT-FILES` specify whether the arguments can add object files.
1744-
/// `CUSTOMIZATION-POINT` means that arbitrary arguments defined by the user
1745-
/// or by the target spec can be inserted here.
1746-
/// `AUDIT-ORDER` - need to figure out whether the option is order-dependent or not.
1741+
///
1742+
/// When comments in the function say "order-(in)dependent" they mean order-dependence between
1743+
/// options and libraries/object files. For example `--whole-archive` (order-dependent) applies
1744+
/// to specific libraries passed after it, and `-o` (output file, order-independent) applies
1745+
/// to the linking process as a whole.
1746+
/// Order-independent options may still override each other in order-dependent fashion,
1747+
/// e.g `--foo=yes --foo=no` may be equivalent to `--foo=no`.
17471748
fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
17481749
path: &Path,
17491750
flavor: LinkerFlavor,
@@ -1761,16 +1762,138 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
17611762
let cmd = &mut *codegen_results.linker_info.to_linker(base_cmd, &sess, flavor);
17621763
let link_output_kind = link_output_kind(sess, crate_type);
17631764

1764-
// NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
1765+
// ------------ Early order-dependent options ------------
1766+
1767+
// Avoid linking to dynamic libraries unless they satisfy some undefined symbols
1768+
// at the point at which they are specified on the command line.
1769+
// Must be passed before any (dynamic) libraries to have effect on them.
1770+
cmd.add_as_needed();
1771+
1772+
// If we're building something like a dynamic library then some platforms
1773+
// need to make sure that all symbols are exported correctly from the
1774+
// dynamic library.
1775+
// Must be passed before any libraries to prevent the symbols to export from being thrown away,
1776+
// at least on some platforms (e.g. windows-gnu).
1777+
cmd.export_symbols(tmpdir, crate_type);
1778+
1779+
// Can be used for adding custom CRT objects or overriding order-dependent options above.
1780+
// FIXME: In practice built-in target specs use this for arbitrary order-independent options,
1781+
// introduce a target spec option for order-independent linker options and migrate built-in
1782+
// specs to it.
17651783
add_pre_link_args(cmd, sess, flavor);
17661784

1767-
// NO-OPT-OUT, OBJECT-FILES-NO
1785+
// ------------ Object code and libraries, order-dependent ------------
1786+
1787+
// Pre-link CRT objects.
1788+
add_pre_link_objects(cmd, sess, link_output_kind, crt_objects_fallback);
1789+
1790+
// Sanitizer libraries.
1791+
add_sanitizer_libraries(sess, crate_type, cmd);
1792+
1793+
// Object code from the current crate.
1794+
// Take careful note of the ordering of the arguments we pass to the linker
1795+
// here. Linkers will assume that things on the left depend on things to the
1796+
// right. Things on the right cannot depend on things on the left. This is
1797+
// all formally implemented in terms of resolving symbols (libs on the right
1798+
// resolve unknown symbols of libs on the left, but not vice versa).
1799+
//
1800+
// For this reason, we have organized the arguments we pass to the linker as
1801+
// such:
1802+
//
1803+
// 1. The local object that LLVM just generated
1804+
// 2. Upstream rust libraries
1805+
// 3. Local native libraries
1806+
// 4. Upstream native libraries
1807+
//
1808+
// The rationale behind this ordering is that those items lower down in the
1809+
// list can't depend on items higher up in the list. For example nothing can
1810+
// depend on what we just generated (e.g., that'd be a circular dependency).
1811+
// Upstream rust libraries are not allowed to depend on our local native
1812+
// libraries as that would violate the structure of the DAG, in that
1813+
// scenario they are required to link to them as well in a shared fashion.
1814+
//
1815+
// Note that upstream rust libraries may contain native dependencies as
1816+
// well, but they also can't depend on what we just started to add to the
1817+
// link line. And finally upstream native libraries can't depend on anything
1818+
// in this DAG so far because they can only depend on other native libraries
1819+
// and such dependencies are also required to be specified.
1820+
add_local_crate_regular_objects(cmd, codegen_results);
1821+
add_local_crate_metadata_objects(cmd, crate_type, codegen_results);
1822+
add_local_crate_allocator_objects(cmd, codegen_results);
1823+
1824+
// Rust libraries.
1825+
add_upstream_rust_crates::<B>(cmd, sess, codegen_results, crate_type, tmpdir);
1826+
1827+
// Native libraries linked with `#[link]` attributes at and `-l` command line options.
1828+
// If -Zlink-native-libraries=false is set, then the assumption is that an
1829+
// external build system already has the native dependencies defined, and it
1830+
// will provide them to the linker itself.
1831+
if sess.opts.debugging_opts.link_native_libraries {
1832+
add_local_native_libraries(cmd, sess, codegen_results);
1833+
add_upstream_native_libraries(cmd, sess, codegen_results, crate_type);
1834+
}
1835+
1836+
// Library linking above uses some global state for things like `-Bstatic`/`-Bdynamic` to make
1837+
// command line shorter, reset it to default here before adding more libraries.
1838+
cmd.reset_per_library_state();
1839+
1840+
// FIXME: Built-in target specs occasionally use this for linking system libraries,
1841+
// eliminate all such uses by migrating them to `#[link]` attributes in `lib(std,c,unwind)`
1842+
// and remove the option.
1843+
add_late_link_args(cmd, sess, flavor, crate_type, codegen_results);
1844+
1845+
// ------------ Arbitrary order-independent options ------------
1846+
1847+
// Add order-independent options determined by rustc from its compiler options,
1848+
// target properties and source code.
1849+
add_order_independent_options(
1850+
cmd,
1851+
sess,
1852+
link_output_kind,
1853+
crt_objects_fallback,
1854+
flavor,
1855+
crate_type,
1856+
codegen_results,
1857+
out_filename,
1858+
tmpdir,
1859+
);
1860+
1861+
// Can be used for arbitrary order-independent options.
1862+
// In practice may also be occasionally used for linking native libraries.
1863+
// Passed after compiler-generated options to support manual overriding when necessary.
1864+
add_user_defined_link_args(cmd, sess);
1865+
1866+
// ------------ Object code and libraries, order-dependent ------------
1867+
1868+
// Post-link CRT objects.
1869+
add_post_link_objects(cmd, sess, link_output_kind, crt_objects_fallback);
1870+
1871+
// ------------ Late order-dependent options ------------
1872+
1873+
// Doesn't really make sense.
1874+
// FIXME: In practice built-in target specs use this for arbitrary order-independent options,
1875+
// introduce a target spec option for order-independent linker options, migrate built-in specs
1876+
// to it and remove the option.
1877+
add_post_link_args(cmd, sess, flavor);
1878+
1879+
cmd.take_cmd()
1880+
}
1881+
1882+
fn add_order_independent_options(
1883+
cmd: &mut dyn Linker,
1884+
sess: &Session,
1885+
link_output_kind: LinkOutputKind,
1886+
crt_objects_fallback: bool,
1887+
flavor: LinkerFlavor,
1888+
crate_type: CrateType,
1889+
codegen_results: &CodegenResults,
1890+
out_filename: &Path,
1891+
tmpdir: &Path,
1892+
) {
17681893
add_apple_sdk(cmd, sess, flavor);
17691894

1770-
// NO-OPT-OUT
17711895
add_link_script(cmd, sess, tmpdir, crate_type);
17721896

1773-
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
17741897
if sess.target.is_like_fuchsia && crate_type == CrateType::Executable {
17751898
let prefix = if sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::ADDRESS) {
17761899
"asan/"
@@ -1780,36 +1903,17 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
17801903
cmd.arg(format!("--dynamic-linker={}ld.so.1", prefix));
17811904
}
17821905

1783-
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
17841906
if sess.target.eh_frame_header {
17851907
cmd.add_eh_frame_header();
17861908
}
17871909

1788-
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
17891910
// Make the binary compatible with data execution prevention schemes.
17901911
cmd.add_no_exec();
17911912

1792-
// OBJECT-FILES-YES
1793-
add_local_crate_metadata_objects(cmd, crate_type, codegen_results);
1794-
1795-
// NO-OPT-OUT, OBJECT-FILES-NO
1796-
// Avoid linking to dynamic libraries unless they satisfy some undefined symbols
1797-
// at the point at which they are specified on the command line.
1798-
// Must be passed before any dynamic libraries.
1799-
// On solaris-like systems, this also will ignore unreferenced ELF sections
1800-
// from relocatable objects. For that reason, we move the metadata objects
1801-
// to before this flag as they would otherwise be removed.
1802-
cmd.add_as_needed();
1803-
1804-
// NO-OPT-OUT, OBJECT-FILES-NO
18051913
if crt_objects_fallback {
18061914
cmd.no_crt_objects();
18071915
}
18081916

1809-
// NO-OPT-OUT, OBJECT-FILES-YES
1810-
add_pre_link_objects(cmd, sess, link_output_kind, crt_objects_fallback);
1811-
1812-
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
18131917
if sess.target.is_like_emscripten {
18141918
cmd.arg("-s");
18151919
cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort {
@@ -1819,138 +1923,64 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
18191923
});
18201924
}
18211925

1822-
// OBJECT-FILES-YES, AUDIT-ORDER
1823-
link_sanitizers(sess, crate_type, cmd);
1926+
if flavor == LinkerFlavor::PtxLinker {
1927+
// Provide the linker with fallback to internal `target-cpu`.
1928+
cmd.arg("--fallback-arch");
1929+
cmd.arg(&codegen_results.linker_info.target_cpu);
1930+
} else if flavor == LinkerFlavor::BpfLinker {
1931+
cmd.arg("--cpu");
1932+
cmd.arg(&codegen_results.linker_info.target_cpu);
1933+
cmd.arg("--cpu-features");
1934+
cmd.arg(match &sess.opts.cg.target_feature {
1935+
feat if !feat.is_empty() => feat,
1936+
_ => &sess.target.options.features,
1937+
});
1938+
}
18241939

1825-
// OBJECT-FILES-NO, AUDIT-ORDER
1826-
// Linker plugins should be specified early in the list of arguments
1827-
// FIXME: How "early" exactly?
18281940
cmd.linker_plugin_lto();
18291941

1830-
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
1831-
// FIXME: Order-dependent, at least relatively to other args adding searh directories.
18321942
add_library_search_dirs(cmd, sess, crt_objects_fallback);
18331943

1834-
// OBJECT-FILES-YES
1835-
add_local_crate_regular_objects(cmd, codegen_results);
1836-
1837-
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
18381944
cmd.output_filename(out_filename);
18391945

1840-
// OBJECT-FILES-NO, AUDIT-ORDER
18411946
if crate_type == CrateType::Executable && sess.target.is_like_windows {
18421947
if let Some(ref s) = codegen_results.windows_subsystem {
18431948
cmd.subsystem(s);
18441949
}
18451950
}
18461951

1847-
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
1848-
// If we're building something like a dynamic library then some platforms
1849-
// need to make sure that all symbols are exported correctly from the
1850-
// dynamic library.
1851-
cmd.export_symbols(tmpdir, crate_type);
1852-
1853-
// OBJECT-FILES-YES
1854-
add_local_crate_allocator_objects(cmd, codegen_results);
1855-
1856-
// OBJECT-FILES-NO, AUDIT-ORDER
1857-
// FIXME: Order dependent, applies to the following objects. Where should it be placed?
18581952
// Try to strip as much out of the generated object by removing unused
18591953
// sections if possible. See more comments in linker.rs
18601954
if !sess.link_dead_code() {
18611955
let keep_metadata = crate_type == CrateType::Dylib;
18621956
cmd.gc_sections(keep_metadata);
18631957
}
18641958

1865-
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
18661959
cmd.set_output_kind(link_output_kind, out_filename);
18671960

1868-
// OBJECT-FILES-NO, AUDIT-ORDER
18691961
add_relro_args(cmd, sess);
18701962

1871-
// OBJECT-FILES-NO, AUDIT-ORDER
18721963
// Pass optimization flags down to the linker.
18731964
cmd.optimize();
18741965

1875-
// OBJECT-FILES-NO, AUDIT-ORDER
18761966
// Pass debuginfo and strip flags down to the linker.
18771967
cmd.debuginfo(sess.opts.debugging_opts.strip);
18781968

1879-
// OBJECT-FILES-NO, AUDIT-ORDER
18801969
// We want to prevent the compiler from accidentally leaking in any system libraries,
18811970
// so by default we tell linkers not to link to any default libraries.
18821971
if !sess.opts.cg.default_linker_libraries && sess.target.no_default_libraries {
18831972
cmd.no_default_libraries();
18841973
}
18851974

1886-
// OBJECT-FILES-NO, AUDIT-ORDER
1887-
// Take careful note of the ordering of the arguments we pass to the linker
1888-
// here. Linkers will assume that things on the left depend on things to the
1889-
// right. Things on the right cannot depend on things on the left. This is
1890-
// all formally implemented in terms of resolving symbols (libs on the right
1891-
// resolve unknown symbols of libs on the left, but not vice versa).
1892-
//
1893-
// For this reason, we have organized the arguments we pass to the linker as
1894-
// such:
1895-
//
1896-
// 1. The local object that LLVM just generated
1897-
// 2. Local native libraries
1898-
// 3. Upstream rust libraries
1899-
// 4. Upstream native libraries
1900-
//
1901-
// The rationale behind this ordering is that those items lower down in the
1902-
// list can't depend on items higher up in the list. For example nothing can
1903-
// depend on what we just generated (e.g., that'd be a circular dependency).
1904-
// Upstream rust libraries are not allowed to depend on our local native
1905-
// libraries as that would violate the structure of the DAG, in that
1906-
// scenario they are required to link to them as well in a shared fashion.
1907-
//
1908-
// Note that upstream rust libraries may contain native dependencies as
1909-
// well, but they also can't depend on what we just started to add to the
1910-
// link line. And finally upstream native libraries can't depend on anything
1911-
// in this DAG so far because they're only dylibs and dylibs can only depend
1912-
// on other dylibs (e.g., other native deps).
1913-
//
1914-
// If -Zlink-native-libraries=false is set, then the assumption is that an
1915-
// external build system already has the native dependencies defined, and it
1916-
// will provide them to the linker itself.
1917-
if sess.opts.debugging_opts.link_native_libraries {
1918-
add_local_native_libraries(cmd, sess, codegen_results);
1919-
}
1920-
add_upstream_rust_crates::<B>(cmd, sess, codegen_results, crate_type, tmpdir);
1921-
if sess.opts.debugging_opts.link_native_libraries {
1922-
add_upstream_native_libraries(cmd, sess, codegen_results, crate_type);
1923-
}
1924-
1925-
// OBJECT-FILES-NO, AUDIT-ORDER
19261975
if sess.opts.cg.profile_generate.enabled() || sess.instrument_coverage() {
19271976
cmd.pgo_gen();
19281977
}
19291978

1930-
// OBJECT-FILES-NO, AUDIT-ORDER
19311979
if sess.opts.cg.control_flow_guard != CFGuard::Disabled {
19321980
cmd.control_flow_guard();
19331981
}
19341982

1935-
// OBJECT-FILES-NO, AUDIT-ORDER
19361983
add_rpath_args(cmd, sess, codegen_results, out_filename);
1937-
1938-
// OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
1939-
add_user_defined_link_args(cmd, sess);
1940-
1941-
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
1942-
cmd.finalize();
1943-
1944-
// NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
1945-
add_late_link_args(cmd, sess, flavor, crate_type, codegen_results);
1946-
1947-
// NO-OPT-OUT, OBJECT-FILES-YES
1948-
add_post_link_objects(cmd, sess, link_output_kind, crt_objects_fallback);
1949-
1950-
// NO-OPT-OUT, OBJECT-FILES-MAYBE, CUSTOMIZATION-POINT
1951-
add_post_link_args(cmd, sess, flavor);
1952-
1953-
cmd.take_cmd()
19541984
}
19551985

19561986
/// # Native library linking

0 commit comments

Comments
 (0)