mirror of
https://github.com/rui314/mold.git
synced 2026-11-09 04:49:33 -10:00
Limit implementation module visibility and remove stale explanations
This commit is contained in:
1 parent
4658ec473a
commit
8a56434bae
6 files changed
+61
-140
No files matched your search
+2
-3
@@ -218,9 +218,8 @@ pub trait Arch: Layout {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
/// Rewrites the output image once every section is copied: big-endian
|
||||
/// ARM32 converts code to BE8 form here, and x86-64 removes unneeded
|
||||
/// ENDBR instructions.
|
||||
/// Rewrites the output image once every section is copied. Big-endian
|
||||
/// ARM32 converts code to BE8 form here.
|
||||
fn finish_output(_ctx: &Context<Self>, _buf: &mut [u8]) {}
|
||||
|
||||
/// Writes an addend into a relocated location, for REL-type targets
|
||||
|
||||
+1
-1
@@ -216,7 +216,7 @@ pub fn new_ehdr<E: Arch>(sh_flags: u64) -> ChunkHeader<E> {
|
||||
hdr
|
||||
}
|
||||
|
||||
// OutputShdr represents the section header. The section header is usually
|
||||
// The section header table is usually
|
||||
// located at the end of an ELF file and is optional for executables.
|
||||
// Executables work without it because the runtime only reads the program
|
||||
// header. Section header is significant only in object files and not
|
||||
|
||||
+26
-26
@@ -1,36 +1,36 @@
|
||||
//! A high-performance ELF linker.
|
||||
|
||||
pub mod arch;
|
||||
pub mod archive_file;
|
||||
pub mod chunks;
|
||||
pub mod cmdline;
|
||||
pub mod context;
|
||||
pub(crate) mod archive_file;
|
||||
pub(crate) mod chunks;
|
||||
pub(crate) mod cmdline;
|
||||
pub(crate) mod context;
|
||||
pub mod driver;
|
||||
pub mod elf;
|
||||
mod elf_consts;
|
||||
pub mod error;
|
||||
pub mod filetype;
|
||||
pub mod gc_sections;
|
||||
pub mod gdb_index;
|
||||
pub mod icf;
|
||||
pub mod input_files;
|
||||
pub mod input_sections;
|
||||
pub mod jobs;
|
||||
pub mod linker_script;
|
||||
pub(crate) mod error;
|
||||
pub(crate) mod filetype;
|
||||
pub(crate) mod gc_sections;
|
||||
pub(crate) mod gdb_index;
|
||||
pub(crate) mod icf;
|
||||
pub(crate) mod input_files;
|
||||
pub(crate) mod input_sections;
|
||||
pub(crate) mod jobs;
|
||||
pub(crate) mod linker_script;
|
||||
#[cfg(not(all(target_os = "windows", target_env = "msvc")))]
|
||||
pub mod lto;
|
||||
pub(crate) mod lto;
|
||||
#[cfg(all(target_os = "windows", target_env = "msvc"))]
|
||||
#[path = "lto_win32.rs"]
|
||||
pub mod lto;
|
||||
pub mod mapfile;
|
||||
pub mod mapped_file;
|
||||
pub mod output_file;
|
||||
pub mod passes;
|
||||
pub mod reader;
|
||||
pub mod relocatable;
|
||||
pub mod shrink_sections;
|
||||
pub mod subprocess;
|
||||
pub mod symbol;
|
||||
pub mod thunks;
|
||||
pub mod tls;
|
||||
pub(crate) mod lto;
|
||||
pub(crate) mod mapfile;
|
||||
pub(crate) mod mapped_file;
|
||||
pub(crate) mod output_file;
|
||||
pub(crate) mod passes;
|
||||
pub(crate) mod reader;
|
||||
pub(crate) mod relocatable;
|
||||
pub(crate) mod shrink_sections;
|
||||
pub(crate) mod subprocess;
|
||||
pub(crate) mod symbol;
|
||||
pub(crate) mod thunks;
|
||||
pub(crate) mod tls;
|
||||
pub mod util;
|
||||
+3
-81
@@ -1,83 +1,3 @@
|
||||
//! This file handles the linker plugin to support LTO (Link-Time
|
||||
//! Optimization).
|
||||
//!
|
||||
//! LTO is a technique to do whole-program optimization to a program. Since
|
||||
//! a linker sees the whole program as opposed to a single compilation
|
||||
//! unit, it in theory can do some optimizations that cannot be done in the
|
||||
//! usual separate compilation model. For example, LTO should be able to
|
||||
//! inline functions that are defined in other compilation unit.
|
||||
//!
|
||||
//! In GCC and Clang, all you have to do to enable LTO is adding the
|
||||
//! `-flto` flag to the compiler and the linker command lines. If `-flto`
|
||||
//! is given, the compiler generates a file that contains not machine code
|
||||
//! but the compiler's IR (intermediate representation). In GCC, the output
|
||||
//! is an ELF file which wraps GCC's IR. In LLVM, it's not even an ELF file
|
||||
//! but just a raw LLVM IR file.
|
||||
//!
|
||||
//! Here is what we have to do if at least one input file is not a usual
|
||||
//! ELF file but an IR object file:
|
||||
//!
|
||||
//! 1. Read symbols both from usual ELF files and from IR object files and
|
||||
//! resolve symbols as usual.
|
||||
//!
|
||||
//! 2. Pass all IR objects to the compiler backend. The compiler backend
|
||||
//! compiles the IRs and returns a few big ELF object files as a
|
||||
//! result.
|
||||
//!
|
||||
//! 3. Parse the returned ELF files and overwrite IR object symbols with
|
||||
//! the returned ones, discarding IR object files.
|
||||
//!
|
||||
//! 4. Continue the rest of the linking process as usual.
|
||||
//!
|
||||
//! When gcc or clang inovkes ld, they pass `-plugin /path/to/linker-plugin.so`
|
||||
//! to the linker. The given .so file provides a way to call the compiler
|
||||
//! backend.
|
||||
//!
|
||||
//! The linker plugin API is documented at
|
||||
//! https://gcc.gnu.org/wiki/whopr/driver, though the document is a bit
|
||||
//! outdated.
|
||||
//!
|
||||
//! Frankly, the linker plugin API is peculiar and is not very easy to use.
|
||||
//! For some reason, the API functions don't return the result of a
|
||||
//! function call as a return value but instead calls other function with
|
||||
//! the result as its argument to "return" the result.
|
||||
//!
|
||||
//! For example, the first thing you need to do after dlopen()'ing a linker
|
||||
//! plugin .so is to call `onload` function with a list of callback
|
||||
//! functions. `onload` calls callbacks to notify about the pointers to
|
||||
//! other functions the linker plugin provides. I don't know why `onload`
|
||||
//! can't just return a list of functions or why the linker plugin can't
|
||||
//! define not only `onload` but other functions, but that's what it is.
|
||||
//!
|
||||
//! Here is the steps to use the linker plugin:
|
||||
//!
|
||||
//! 1. dlopen() the linker plugin .so and call `onload` to obtain pointers
|
||||
//! to other functions provided by the plugin.
|
||||
//!
|
||||
//! 2. Call `claim_file_hook` with an IR object file to read its symbol
|
||||
//! table. `claim_file_hook` calls the `add_symbols` callback to
|
||||
//! "return" a list of symbols.
|
||||
//!
|
||||
//! 3. `claim_file_hook` returns LDPT_OK only when the plugin wants to
|
||||
//! handle a given file. Since we pass only IR object files to the
|
||||
//! plugin in mold, it always returns LDPT_OK in our case.
|
||||
//!
|
||||
//! 4. Once we made a decision as to which object file to include into the
|
||||
//! output file, we call `all_symbols_read_hook` to compile IR objects
|
||||
//! into a few big ELF files. That function calls the `get_symbols`
|
||||
//! callback to ask us about the symbol resolution results. (The
|
||||
//! compiler backend needs to know whether an undefined symbol in an IR
|
||||
//! object was resolved to a regular object file or a shared object to
|
||||
//! do whole program optimization, for example.)
|
||||
//!
|
||||
//! 5. `all_symbols_read_hook` "returns" the result by calling the
|
||||
//! `add_input_file` callback. The callback is called with a path to an
|
||||
//! LTO'ed ELF file. We parse that ELF file and override symbols
|
||||
//! defined by IR objects with the ELF file's ones.
|
||||
//!
|
||||
//! 6. Lastly, we call `cleanup_hook` to remove temporary files created by
|
||||
//! the compiler backend.
|
||||
//!
|
||||
//! Link-time optimization through the linker plugin interface.
|
||||
//!
|
||||
//! With `-flto`, compilers emit files holding their intermediate
|
||||
@@ -840,7 +760,9 @@ fn plugin_input_file(mf: &'static MappedFile) -> (PluginInputFile, File) {
|
||||
let file = File::open(&container.name)
|
||||
.unwrap_or_else(|e| fatal!("cannot open {}: {e}", container.name.display()));
|
||||
let input = PluginInputFile {
|
||||
name: CString::new(container.name.as_os_str().as_encoded_bytes()).unwrap().into_raw(),
|
||||
name: CString::new(container.name.as_os_str().as_encoded_bytes())
|
||||
.unwrap()
|
||||
.into_raw(),
|
||||
#[cfg(not(windows))]
|
||||
fd: file.as_raw_fd(),
|
||||
#[cfg(windows)]
|
||||
|
||||
+2
-2
@@ -815,13 +815,13 @@ pub fn resolve_symbols<E: Arch>(ctx: &mut Context<E>) {
|
||||
resolve_symbols_pass(ctx, &files, true);
|
||||
}
|
||||
|
||||
/// Drops files that didn't make it into the link and renumbers the rest.
|
||||
/// Removes unreachable files from the live file lists.
|
||||
pub fn remove_unreachable_files<E: Arch>(ctx: &mut Context<E>) {
|
||||
remove_objects(ctx, |file| !file.base.is_reachable());
|
||||
remove_unreachable_dsos(ctx);
|
||||
}
|
||||
|
||||
/// Drops the object files `remove` selects and renumbers the rest.
|
||||
/// Removes selected objects from the live list, retaining their backing storage.
|
||||
fn remove_objects<E: Arch>(ctx: &mut Context<E>, remove: impl Fn(&ObjectFile<E>) -> bool) {
|
||||
ctx.objs.retain(|file| !remove(file));
|
||||
}
|
||||
|
||||
+27
-27
@@ -2,17 +2,17 @@
|
||||
|
||||
// Utility functions
|
||||
|
||||
pub mod compress;
|
||||
pub mod concurrent_map;
|
||||
pub mod demangle;
|
||||
pub(crate) mod compress;
|
||||
pub(crate) mod concurrent_map;
|
||||
pub(crate) mod demangle;
|
||||
pub mod endian;
|
||||
pub mod glob;
|
||||
pub mod hyperloglog;
|
||||
pub(crate) mod glob;
|
||||
pub(crate) mod hyperloglog;
|
||||
pub(crate) mod parallel;
|
||||
pub mod perf;
|
||||
pub(crate) mod perf;
|
||||
mod prefetch;
|
||||
pub(crate) mod siphash;
|
||||
pub mod tar;
|
||||
pub(crate) mod tar;
|
||||
pub(crate) mod worker_local;
|
||||
|
||||
pub(crate) use prefetch::prefetch;
|
||||
@@ -90,7 +90,7 @@ pub(crate) unsafe fn madvise_hugepage_interior(_data: *const u8, _size: usize) {
|
||||
/// Rounds `value` up to a multiple of `align`, which must be zero or a power
|
||||
/// of two. Zero means "no alignment".
|
||||
#[inline]
|
||||
pub fn align_to(value: u64, align: u64) -> u64 {
|
||||
pub(crate) fn align_to(value: u64, align: u64) -> u64 {
|
||||
if align == 0 {
|
||||
return value;
|
||||
}
|
||||
@@ -99,35 +99,35 @@ pub fn align_to(value: u64, align: u64) -> u64 {
|
||||
}
|
||||
|
||||
/// Rounds `value` down to a multiple of `align`, which must be a power of two.
|
||||
pub fn align_down(value: u64, align: u64) -> u64 {
|
||||
pub(crate) fn align_down(value: u64, align: u64) -> u64 {
|
||||
debug_assert!(align.is_power_of_two());
|
||||
value & !(align - 1)
|
||||
}
|
||||
|
||||
/// Returns bit `pos` of `value`.
|
||||
pub fn bit(value: u64, pos: u32) -> u64 {
|
||||
pub(crate) fn bit(value: u64, pos: u32) -> u64 {
|
||||
(value >> pos) & 1
|
||||
}
|
||||
|
||||
// Returns [hi:lo] bits of val.
|
||||
#[inline]
|
||||
pub fn bits(value: u64, hi: u32, lo: u32) -> u64 {
|
||||
pub(crate) fn bits(value: u64, hi: u32, lo: u32) -> u64 {
|
||||
(value >> lo) & ((1u64 << (hi - lo + 1)) - 1)
|
||||
}
|
||||
|
||||
// Cast val to a signed N bit integer.
|
||||
// For example, sign_extend(x, 32) == (i32)x for any integer x.
|
||||
pub fn sign_extend(value: u64, n: u32) -> i64 {
|
||||
pub(crate) fn sign_extend(value: u64, n: u32) -> i64 {
|
||||
((value << (64 - n)) as i64) >> (64 - n)
|
||||
}
|
||||
|
||||
/// Whether `value` is representable as a signed `n`-bit integer.
|
||||
pub fn is_int(value: i64, n: u32) -> bool {
|
||||
pub(crate) fn is_int(value: i64, n: u32) -> bool {
|
||||
sign_extend(value as u64, n) == value
|
||||
}
|
||||
|
||||
/// Writes a NUL-terminated string and returns the number of bytes written.
|
||||
pub fn write_cstr(buf: &mut [u8], s: &[u8]) -> usize {
|
||||
pub(crate) fn write_cstr(buf: &mut [u8], s: &[u8]) -> usize {
|
||||
buf[..s.len()].copy_from_slice(s);
|
||||
buf[s.len()] = 0;
|
||||
s.len() + 1
|
||||
@@ -137,14 +137,14 @@ pub fn write_cstr(buf: &mut [u8], s: &[u8]) -> usize {
|
||||
/// The result excludes the terminator. A missing terminator yields the rest
|
||||
/// of the table.
|
||||
#[inline]
|
||||
pub fn cstr_at(table: &[u8], offset: usize) -> &[u8] {
|
||||
pub(crate) fn cstr_at(table: &[u8], offset: usize) -> &[u8] {
|
||||
let rest = table.get(offset..).unwrap_or(&[]);
|
||||
let end = memchr::memchr(0, rest).unwrap_or(rest.len());
|
||||
&rest[..end]
|
||||
}
|
||||
|
||||
/// Appends `value` in unsigned LEB128 encoding.
|
||||
pub fn encode_uleb(out: &mut Vec<u8>, mut value: u64) {
|
||||
pub(crate) fn encode_uleb(out: &mut Vec<u8>, mut value: u64) {
|
||||
loop {
|
||||
let byte = (value & 0x7f) as u8;
|
||||
value >>= 7;
|
||||
@@ -157,7 +157,7 @@ pub fn encode_uleb(out: &mut Vec<u8>, mut value: u64) {
|
||||
}
|
||||
|
||||
/// Appends `value` in signed LEB128 encoding.
|
||||
pub fn encode_sleb(out: &mut Vec<u8>, mut value: i64) {
|
||||
pub(crate) fn encode_sleb(out: &mut Vec<u8>, mut value: i64) {
|
||||
loop {
|
||||
let byte = (value & 0x7f) as u8;
|
||||
value >>= 7;
|
||||
@@ -171,7 +171,7 @@ pub fn encode_sleb(out: &mut Vec<u8>, mut value: i64) {
|
||||
}
|
||||
|
||||
/// Overwrites an existing unsigned LEB128 value in place, keeping its length.
|
||||
pub fn overwrite_uleb(buf: &mut [u8], mut value: u64) {
|
||||
pub(crate) fn overwrite_uleb(buf: &mut [u8], mut value: u64) {
|
||||
let mut i = 0;
|
||||
while buf[i] & 0x80 != 0 {
|
||||
buf[i] = 0x80 | (value & 0x7f) as u8;
|
||||
@@ -183,7 +183,7 @@ pub fn overwrite_uleb(buf: &mut [u8], mut value: u64) {
|
||||
|
||||
/// Reads an unsigned LEB128 value, advancing `bytes` past it.
|
||||
#[inline]
|
||||
pub fn read_uleb(bytes: &mut &[u8]) -> u64 {
|
||||
pub(crate) fn read_uleb(bytes: &mut &[u8]) -> u64 {
|
||||
let mut value = 0;
|
||||
let mut shift = 0;
|
||||
loop {
|
||||
@@ -201,7 +201,7 @@ pub fn read_uleb(bytes: &mut &[u8]) -> u64 {
|
||||
|
||||
/// Reads a signed LEB128 value, advancing `bytes` past it.
|
||||
#[inline]
|
||||
pub fn read_sleb(bytes: &mut &[u8]) -> i64 {
|
||||
pub(crate) fn read_sleb(bytes: &mut &[u8]) -> i64 {
|
||||
let mut value = 0u64;
|
||||
let mut shift = 0;
|
||||
loop {
|
||||
@@ -222,7 +222,7 @@ pub fn read_sleb(bytes: &mut &[u8]) -> i64 {
|
||||
}
|
||||
|
||||
/// Fills `buf` with random bytes from the operating system.
|
||||
pub fn random_bytes(buf: &mut [u8]) {
|
||||
pub(crate) fn random_bytes(buf: &mut [u8]) {
|
||||
getrandom::fill(buf).unwrap_or_else(|err| crate::fatal!("cannot get random bytes: {err}"));
|
||||
}
|
||||
|
||||
@@ -231,18 +231,18 @@ pub fn random_bytes(buf: &mut [u8]) {
|
||||
/// Input files, symbol names and a few other objects must outlive every
|
||||
/// data structure of a link, and the process exits as soon as the link is
|
||||
/// done, so never freeing them is both simplest and cheapest.
|
||||
pub fn leak<T>(value: T) -> &'static T {
|
||||
pub(crate) fn leak<T>(value: T) -> &'static T {
|
||||
Box::leak(Box::new(value))
|
||||
}
|
||||
|
||||
/// Leaks a byte string for the rest of the process's lifetime.
|
||||
pub fn leak_bytes(bytes: Vec<u8>) -> &'static [u8] {
|
||||
pub(crate) fn leak_bytes(bytes: Vec<u8>) -> &'static [u8] {
|
||||
Vec::leak(bytes)
|
||||
}
|
||||
|
||||
/// Normalizes a path lexically, resolving `.` and `..` components without
|
||||
/// consulting the file system.
|
||||
pub fn path_clean(path: &str) -> String {
|
||||
pub(crate) fn path_clean(path: &str) -> String {
|
||||
clean_path(std::path::Path::new(path))
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
@@ -250,7 +250,7 @@ pub fn path_clean(path: &str) -> String {
|
||||
|
||||
/// Converts bytes from a response file or linker script to an OS string.
|
||||
/// Unix paths can contain arbitrary non-NUL bytes.
|
||||
pub fn os_str(bytes: &[u8]) -> &std::ffi::OsStr {
|
||||
pub(crate) fn os_str(bytes: &[u8]) -> &std::ffi::OsStr {
|
||||
use bstr::ByteSlice;
|
||||
bytes
|
||||
.to_os_str()
|
||||
@@ -258,7 +258,7 @@ pub fn os_str(bytes: &[u8]) -> &std::ffi::OsStr {
|
||||
}
|
||||
|
||||
/// Normalizes an OS path without resolving symlinks.
|
||||
pub fn clean_path(path: &std::path::Path) -> std::path::PathBuf {
|
||||
pub(crate) fn clean_path(path: &std::path::Path) -> std::path::PathBuf {
|
||||
use std::path::{Component, PathBuf};
|
||||
let mut out = PathBuf::new();
|
||||
for component in path.components() {
|
||||
@@ -282,7 +282,7 @@ pub fn clean_path(path: &std::path::Path) -> std::path::PathBuf {
|
||||
}
|
||||
|
||||
/// Formats a byte string for diagnostics, replacing invalid UTF-8.
|
||||
pub fn display(bytes: &[u8]) -> std::borrow::Cow<'_, str> {
|
||||
pub(crate) fn display(bytes: &[u8]) -> std::borrow::Cow<'_, str> {
|
||||
String::from_utf8_lossy(bytes)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user