mirror of
https://github.com/rui314/mold.git
synced 2024-09-11 13:06:59 +03:00
Compare commits
7 Commits
527128285f
...
40f865bfcf
Author | SHA1 | Date | |
---|---|---|---|
|
40f865bfcf | ||
|
18da5b654e | ||
|
97a1e218c5 | ||
|
f9e4cb1a7f | ||
|
60760a892a | ||
|
596ffa959a | ||
|
cd7287f0de |
@ -364,6 +364,7 @@ endforeach()
|
|||||||
# Add other non-template source files.
|
# Add other non-template source files.
|
||||||
target_sources(mold PRIVATE
|
target_sources(mold PRIVATE
|
||||||
common/compress.cc
|
common/compress.cc
|
||||||
|
common/crc32.cc
|
||||||
common/demangle.cc
|
common/demangle.cc
|
||||||
common/filepath.cc
|
common/filepath.cc
|
||||||
common/glob.cc
|
common/glob.cc
|
||||||
|
20
README.md
20
README.md
@ -187,6 +187,26 @@ GNU linker, especially when linking large programs.
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details><summary>If you are using Conan package manager</summary>
|
||||||
|
|
||||||
|
You can configure [Conan](https://github.com/conan-io) to download the latest
|
||||||
|
version of `mold` and use it as the linker when building your dependencies and
|
||||||
|
projects from source.
|
||||||
|
|
||||||
|
Add the following section to your _host_ profile targetting Linux:
|
||||||
|
|
||||||
|
```
|
||||||
|
[tool_requires]
|
||||||
|
*:mold/[*]
|
||||||
|
|
||||||
|
[conf]
|
||||||
|
# The following config will only work with clang or gcc >= 12
|
||||||
|
tools.build:exelinkflags=['-fuse-ld=mold']
|
||||||
|
tools.build:sharedlinkflags=['-fuse-ld=mold']
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
<details><summary>Verify that you are using mold</summary>
|
<details><summary>Verify that you are using mold</summary>
|
||||||
|
|
||||||
mold leaves its identification string in the `.comment` section of an output
|
mold leaves its identification string in the `.comment` section of an output
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "integers.h"
|
#include "integers.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <bit>
|
#include <bit>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
@ -899,6 +900,13 @@ std::optional<std::string_view> demangle_rust(std::string_view name);
|
|||||||
void acquire_global_lock();
|
void acquire_global_lock();
|
||||||
void release_global_lock();
|
void release_global_lock();
|
||||||
|
|
||||||
|
//
|
||||||
|
// crc32.cc
|
||||||
|
//
|
||||||
|
|
||||||
|
u32 compute_crc32(u32 crc, u8 *buf, i64 len);
|
||||||
|
std::vector<u8> crc32_solve(u32 current, u32 desired);
|
||||||
|
|
||||||
//
|
//
|
||||||
// compress.cc
|
// compress.cc
|
||||||
//
|
//
|
||||||
|
60
common/crc32.cc
Normal file
60
common/crc32.cc
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <tbb/parallel_for_each.h>
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
namespace mold {
|
||||||
|
|
||||||
|
// This function "forges" a CRC. That is, given the current and a desired
|
||||||
|
// CRC32 value, crc32_solve() returns a binary blob to add to the end of
|
||||||
|
// the original data to yield the desired CRC. Trailing garbage is ignored
|
||||||
|
// by many bianry file formats, so you can create a file with a desired
|
||||||
|
// CRC using crc32_solve(). We need it for --separate-debug-file.
|
||||||
|
std::vector<u8> crc32_solve(u32 current, u32 desired) {
|
||||||
|
constexpr u32 poly = 0xedb88320;
|
||||||
|
u32 x = ~desired;
|
||||||
|
|
||||||
|
// Each iteration computes x = (x * x^-1) mod poly.
|
||||||
|
for (i64 i = 0; i < 32; i++) {
|
||||||
|
x = std::rotl(x, 1);
|
||||||
|
x ^= (x & 1) * (poly << 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
x ^= ~current;
|
||||||
|
|
||||||
|
std::vector<u8> out(4);
|
||||||
|
out[0] = x;
|
||||||
|
out[1] = x >> 8;
|
||||||
|
out[2] = x >> 16;
|
||||||
|
out[3] = x >> 24;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute a CRC for given data in parallel
|
||||||
|
u32 compute_crc32(u32 crc, u8 *buf, i64 len) {
|
||||||
|
struct Shard {
|
||||||
|
u8 *buf;
|
||||||
|
i64 len;
|
||||||
|
u32 crc;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr i64 shard_size = 1024 * 1024; // 1 MiB
|
||||||
|
std::vector<Shard> shards;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
i64 sz = std::min(len, shard_size);
|
||||||
|
shards.push_back({buf, sz, 0});
|
||||||
|
buf += sz;
|
||||||
|
len -= sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbb::parallel_for_each(shards.begin(), shards.end(), [](Shard &shard) {
|
||||||
|
shard.crc = crc32_z(0, shard.buf, shard.len);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (Shard &shard : shards)
|
||||||
|
crc = crc32_combine(crc, shard.crc, shard.len);
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mold
|
@ -85,6 +85,8 @@ Options:
|
|||||||
--defsym=SYMBOL=VALUE Define a symbol alias
|
--defsym=SYMBOL=VALUE Define a symbol alias
|
||||||
--demangle Demangle C++ symbols in log messages (default)
|
--demangle Demangle C++ symbols in log messages (default)
|
||||||
--no-demangle
|
--no-demangle
|
||||||
|
--detach Create separate debug info file in the background (default)
|
||||||
|
--no-detach
|
||||||
--enable-new-dtags Emit DT_RUNPATH for --rpath (default)
|
--enable-new-dtags Emit DT_RUNPATH for --rpath (default)
|
||||||
--disable-new-dtags Emit DT_RPATH for --rpath
|
--disable-new-dtags Emit DT_RPATH for --rpath
|
||||||
--execute-only Make executable segments unreadable
|
--execute-only Make executable segments unreadable
|
||||||
@ -143,6 +145,8 @@ Options:
|
|||||||
--rpath-link DIR Ignored
|
--rpath-link DIR Ignored
|
||||||
--run COMMAND ARG... Run COMMAND with mold as /usr/bin/ld
|
--run COMMAND ARG... Run COMMAND with mold as /usr/bin/ld
|
||||||
--section-start=SECTION=ADDR Set address for section
|
--section-start=SECTION=ADDR Set address for section
|
||||||
|
--separate-debug-file[=FILE] Separate debug info to the specified file
|
||||||
|
--no-separate-debug-file
|
||||||
--shared, --Bshareable Create a shared library
|
--shared, --Bshareable Create a shared library
|
||||||
--shuffle-sections[=SEED] Randomize the output by shuffling input sections
|
--shuffle-sections[=SEED] Randomize the output by shuffling input sections
|
||||||
--sort-common Ignored
|
--sort-common Ignored
|
||||||
@ -526,6 +530,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
|||||||
std::optional<SeparateCodeKind> z_separate_code;
|
std::optional<SeparateCodeKind> z_separate_code;
|
||||||
std::optional<bool> report_undefined;
|
std::optional<bool> report_undefined;
|
||||||
std::optional<bool> z_relro;
|
std::optional<bool> z_relro;
|
||||||
|
std::optional<std::string> separate_debug_file;
|
||||||
std::optional<u64> shuffle_sections_seed;
|
std::optional<u64> shuffle_sections_seed;
|
||||||
std::unordered_set<std::string_view> rpaths;
|
std::unordered_set<std::string_view> rpaths;
|
||||||
|
|
||||||
@ -756,6 +761,10 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
|||||||
ctx.arg.demangle = true;
|
ctx.arg.demangle = true;
|
||||||
} else if (read_flag("no-demangle")) {
|
} else if (read_flag("no-demangle")) {
|
||||||
ctx.arg.demangle = false;
|
ctx.arg.demangle = false;
|
||||||
|
} else if (read_flag("detach")) {
|
||||||
|
ctx.arg.detach = true;
|
||||||
|
} else if (read_flag("no-detach")) {
|
||||||
|
ctx.arg.detach = false;
|
||||||
} else if (read_flag("default-symver")) {
|
} else if (read_flag("default-symver")) {
|
||||||
ctx.arg.default_symver = true;
|
ctx.arg.default_symver = true;
|
||||||
} else if (read_flag("noinhibit-exec")) {
|
} else if (read_flag("noinhibit-exec")) {
|
||||||
@ -1003,6 +1012,12 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
|||||||
ctx.arg.z_origin = true;
|
ctx.arg.z_origin = true;
|
||||||
} else if (read_z_flag("nodefaultlib")) {
|
} else if (read_z_flag("nodefaultlib")) {
|
||||||
ctx.arg.z_nodefaultlib = true;
|
ctx.arg.z_nodefaultlib = true;
|
||||||
|
} else if (read_eq("separate-debug-file")) {
|
||||||
|
separate_debug_file = arg;
|
||||||
|
} else if (read_flag("separate-debug-file")) {
|
||||||
|
separate_debug_file = "";
|
||||||
|
} else if (read_flag("no-separate-debug-file")) {
|
||||||
|
separate_debug_file.reset();
|
||||||
} else if (read_z_flag("separate-loadable-segments")) {
|
} else if (read_z_flag("separate-loadable-segments")) {
|
||||||
z_separate_code = SEPARATE_LOADABLE_SEGMENTS;
|
z_separate_code = SEPARATE_LOADABLE_SEGMENTS;
|
||||||
} else if (read_z_flag("separate-code")) {
|
} else if (read_z_flag("separate-code")) {
|
||||||
@ -1394,9 +1409,20 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
|||||||
ctx.default_version = VER_NDX_LAST_RESERVED + 1;
|
ctx.default_version = VER_NDX_LAST_RESERVED + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (separate_debug_file) {
|
||||||
|
if (separate_debug_file->empty())
|
||||||
|
ctx.arg.separate_debug_file = ctx.arg.output + ".dbg";
|
||||||
|
else
|
||||||
|
ctx.arg.separate_debug_file = *separate_debug_file;
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx.arg.shared && warn_shared_textrel)
|
if (ctx.arg.shared && warn_shared_textrel)
|
||||||
ctx.arg.warn_textrel = true;
|
ctx.arg.warn_textrel = true;
|
||||||
|
|
||||||
|
// We don't want the background process to write to stdout
|
||||||
|
if (ctx.arg.stats || ctx.arg.perf)
|
||||||
|
ctx.arg.detach = false;
|
||||||
|
|
||||||
ctx.arg.undefined.push_back(ctx.arg.entry);
|
ctx.arg.undefined.push_back(ctx.arg.entry);
|
||||||
|
|
||||||
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
|
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
|
||||||
|
23
elf/main.cc
23
elf/main.cc
@ -559,14 +559,17 @@ int elf_main(int argc, char **argv) {
|
|||||||
// Compute the is_weak bit for each imported symbol.
|
// Compute the is_weak bit for each imported symbol.
|
||||||
compute_imported_symbol_weakness(ctx);
|
compute_imported_symbol_weakness(ctx);
|
||||||
|
|
||||||
// Compute sizes of output sections while assigning offsets
|
|
||||||
// within an output section to input sections.
|
|
||||||
compute_section_sizes(ctx);
|
|
||||||
|
|
||||||
// Sort sections by section attributes so that we'll have to
|
// Sort sections by section attributes so that we'll have to
|
||||||
// create as few segments as possible.
|
// create as few segments as possible.
|
||||||
sort_output_sections(ctx);
|
sort_output_sections(ctx);
|
||||||
|
|
||||||
|
if (!ctx.arg.separate_debug_file.empty())
|
||||||
|
separate_debug_sections(ctx);
|
||||||
|
|
||||||
|
// Compute sizes of output sections while assigning offsets
|
||||||
|
// within an output section to input sections.
|
||||||
|
compute_section_sizes(ctx);
|
||||||
|
|
||||||
// If --packed_dyn_relocs=relr was given, base relocations are stored
|
// If --packed_dyn_relocs=relr was given, base relocations are stored
|
||||||
// to a .relr.dyn section in a compressed form. Construct a compressed
|
// to a .relr.dyn section in a compressed form. Construct a compressed
|
||||||
// relocations now so that we can fix section sizes and file layout.
|
// relocations now so that we can fix section sizes and file layout.
|
||||||
@ -659,9 +662,12 @@ int elf_main(int argc, char **argv) {
|
|||||||
// .gdb_index's contents cannot be constructed before applying
|
// .gdb_index's contents cannot be constructed before applying
|
||||||
// relocations to other debug sections. We have relocated debug
|
// relocations to other debug sections. We have relocated debug
|
||||||
// sections now, so write the .gdb_index section.
|
// sections now, so write the .gdb_index section.
|
||||||
if (ctx.gdb_index)
|
if (ctx.gdb_index && ctx.arg.separate_debug_file.empty())
|
||||||
write_gdb_index(ctx);
|
write_gdb_index(ctx);
|
||||||
|
|
||||||
|
if (!ctx.arg.separate_debug_file.empty())
|
||||||
|
write_gnu_debuglink(ctx);
|
||||||
|
|
||||||
t_copy.stop();
|
t_copy.stop();
|
||||||
ctx.checkpoint();
|
ctx.checkpoint();
|
||||||
|
|
||||||
@ -680,6 +686,9 @@ int elf_main(int argc, char **argv) {
|
|||||||
if (ctx.arg.print_map)
|
if (ctx.arg.print_map)
|
||||||
print_map(ctx);
|
print_map(ctx);
|
||||||
|
|
||||||
|
if (!ctx.arg.separate_debug_file.empty())
|
||||||
|
write_separate_debug_file(ctx);
|
||||||
|
|
||||||
// Show stats numbers
|
// Show stats numbers
|
||||||
if (ctx.arg.stats)
|
if (ctx.arg.stats)
|
||||||
show_stats(ctx);
|
show_stats(ctx);
|
||||||
@ -690,9 +699,7 @@ int elf_main(int argc, char **argv) {
|
|||||||
std::cout << std::flush;
|
std::cout << std::flush;
|
||||||
std::cerr << std::flush;
|
std::cerr << std::flush;
|
||||||
|
|
||||||
if (ctx.arg.fork)
|
notify_parent();
|
||||||
notify_parent();
|
|
||||||
|
|
||||||
release_global_lock();
|
release_global_lock();
|
||||||
|
|
||||||
if (ctx.arg.quick_exit)
|
if (ctx.arg.quick_exit)
|
||||||
|
25
elf/mold.h
25
elf/mold.h
@ -993,6 +993,22 @@ private:
|
|||||||
std::map<u32, u32> properties;
|
std::map<u32, u32> properties;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
class GnuDebuglinkSection : public Chunk<E> {
|
||||||
|
public:
|
||||||
|
GnuDebuglinkSection() {
|
||||||
|
this->name = ".gnu_debuglink";
|
||||||
|
this->shdr.sh_type = SHT_PROGBITS;
|
||||||
|
this->shdr.sh_addralign = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_shdr(Context<E> &ctx) override;
|
||||||
|
void copy_buf(Context<E> &ctx) override;
|
||||||
|
|
||||||
|
std::string filename;
|
||||||
|
u32 crc32 = 0;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
class GdbIndexSection : public Chunk<E> {
|
class GdbIndexSection : public Chunk<E> {
|
||||||
public:
|
public:
|
||||||
@ -1439,11 +1455,14 @@ template <typename E> void apply_version_script(Context<E> &);
|
|||||||
template <typename E> void parse_symbol_version(Context<E> &);
|
template <typename E> void parse_symbol_version(Context<E> &);
|
||||||
template <typename E> void compute_import_export(Context<E> &);
|
template <typename E> void compute_import_export(Context<E> &);
|
||||||
template <typename E> void compute_address_significance(Context<E> &);
|
template <typename E> void compute_address_significance(Context<E> &);
|
||||||
|
template <typename E> void separate_debug_sections(Context<E> &);
|
||||||
template <typename E> void compute_section_headers(Context<E> &);
|
template <typename E> void compute_section_headers(Context<E> &);
|
||||||
template <typename E> i64 set_osec_offsets(Context<E> &);
|
template <typename E> i64 set_osec_offsets(Context<E> &);
|
||||||
template <typename E> void fix_synthetic_symbols(Context<E> &);
|
template <typename E> void fix_synthetic_symbols(Context<E> &);
|
||||||
template <typename E> i64 compress_debug_sections(Context<E> &);
|
template <typename E> i64 compress_debug_sections(Context<E> &);
|
||||||
template <typename E> void write_build_id(Context<E> &);
|
template <typename E> void write_build_id(Context<E> &);
|
||||||
|
template <typename E> void write_gnu_debuglink(Context<E> &);
|
||||||
|
template <typename E> void write_separate_debug_file(Context<E> &ctx);
|
||||||
template <typename E> void write_dependency_file(Context<E> &);
|
template <typename E> void write_dependency_file(Context<E> &);
|
||||||
template <typename E> void show_stats(Context<E> &);
|
template <typename E> void show_stats(Context<E> &);
|
||||||
|
|
||||||
@ -1721,6 +1740,7 @@ struct Context {
|
|||||||
bool color_diagnostics = false;
|
bool color_diagnostics = false;
|
||||||
bool default_symver = false;
|
bool default_symver = false;
|
||||||
bool demangle = true;
|
bool demangle = true;
|
||||||
|
bool detach = true;
|
||||||
bool discard_all = false;
|
bool discard_all = false;
|
||||||
bool discard_locals = false;
|
bool discard_locals = false;
|
||||||
bool eh_frame_hdr = true;
|
bool eh_frame_hdr = true;
|
||||||
@ -1807,6 +1827,7 @@ struct Context {
|
|||||||
std::string package_metadata;
|
std::string package_metadata;
|
||||||
std::string plugin;
|
std::string plugin;
|
||||||
std::string rpaths;
|
std::string rpaths;
|
||||||
|
std::string separate_debug_file;
|
||||||
std::string soname;
|
std::string soname;
|
||||||
std::string sysroot;
|
std::string sysroot;
|
||||||
std::unique_ptr<std::unordered_set<std::string_view>> retain_symbols_file;
|
std::unique_ptr<std::unordered_set<std::string_view>> retain_symbols_file;
|
||||||
@ -1885,6 +1906,9 @@ struct Context {
|
|||||||
|
|
||||||
tbb::concurrent_hash_map<Symbol<E> *, std::vector<std::string>> undef_errors;
|
tbb::concurrent_hash_map<Symbol<E> *, std::vector<std::string>> undef_errors;
|
||||||
|
|
||||||
|
// For --separate-debug-file
|
||||||
|
std::vector<Chunk<E> *> debug_chunks;
|
||||||
|
|
||||||
// Output chunks
|
// Output chunks
|
||||||
OutputEhdr<E> *ehdr = nullptr;
|
OutputEhdr<E> *ehdr = nullptr;
|
||||||
OutputShdr<E> *shdr = nullptr;
|
OutputShdr<E> *shdr = nullptr;
|
||||||
@ -1900,6 +1924,7 @@ struct Context {
|
|||||||
DynstrSection<E> *dynstr = nullptr;
|
DynstrSection<E> *dynstr = nullptr;
|
||||||
HashSection<E> *hash = nullptr;
|
HashSection<E> *hash = nullptr;
|
||||||
GnuHashSection<E> *gnu_hash = nullptr;
|
GnuHashSection<E> *gnu_hash = nullptr;
|
||||||
|
GnuDebuglinkSection<E> *gnu_debuglink = nullptr;
|
||||||
ShstrtabSection<E> *shstrtab = nullptr;
|
ShstrtabSection<E> *shstrtab = nullptr;
|
||||||
PltSection<E> *plt = nullptr;
|
PltSection<E> *plt = nullptr;
|
||||||
PltGotSection<E> *pltgot = nullptr;
|
PltGotSection<E> *pltgot = nullptr;
|
||||||
|
@ -2948,6 +2948,20 @@ void ComdatGroupSection<E>::copy_buf(Context<E> &ctx) {
|
|||||||
*buf++ = chunk->shndx;
|
*buf++ = chunk->shndx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
void GnuDebuglinkSection<E>::update_shdr(Context<E> &ctx) {
|
||||||
|
filename = std::filesystem::path(ctx.arg.separate_debug_file).filename().string();
|
||||||
|
this->shdr.sh_size = align_to(filename.size() + 1, 4) + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename E>
|
||||||
|
void GnuDebuglinkSection<E>::copy_buf(Context<E> &ctx) {
|
||||||
|
u8 *buf = ctx.buf + this->shdr.sh_offset;
|
||||||
|
memset(buf, 0, this->shdr.sh_size);
|
||||||
|
write_string(buf, filename);
|
||||||
|
*(U32<E> *)(buf + this->shdr.sh_size - 4) = crc32;
|
||||||
|
}
|
||||||
|
|
||||||
using E = MOLD_TARGET;
|
using E = MOLD_TARGET;
|
||||||
|
|
||||||
template class Chunk<E>;
|
template class Chunk<E>;
|
||||||
@ -2986,6 +3000,7 @@ template class GdbIndexSection<E>;
|
|||||||
template class CompressedSection<E>;
|
template class CompressedSection<E>;
|
||||||
template class RelocSection<E>;
|
template class RelocSection<E>;
|
||||||
template class ComdatGroupSection<E>;
|
template class ComdatGroupSection<E>;
|
||||||
|
template class GnuDebuglinkSection<E>;
|
||||||
|
|
||||||
template OutputSection<E> *find_section(Context<E> &, u32);
|
template OutputSection<E> *find_section(Context<E> &, u32);
|
||||||
template OutputSection<E> *find_section(Context<E> &, std::string_view);
|
template OutputSection<E> *find_section(Context<E> &, std::string_view);
|
||||||
|
123
elf/passes.cc
123
elf/passes.cc
@ -156,6 +156,8 @@ void create_synthetic_sections(Context<E> &ctx) {
|
|||||||
ctx.verdef = push(new VerdefSection<E>);
|
ctx.verdef = push(new VerdefSection<E>);
|
||||||
if (ctx.arg.emit_relocs)
|
if (ctx.arg.emit_relocs)
|
||||||
ctx.eh_frame_reloc = push(new EhFrameRelocSection<E>);
|
ctx.eh_frame_reloc = push(new EhFrameRelocSection<E>);
|
||||||
|
if (!ctx.arg.separate_debug_file.empty())
|
||||||
|
ctx.gnu_debuglink = push(new GnuDebuglinkSection<E>);
|
||||||
|
|
||||||
if (ctx.arg.shared || !ctx.dsos.empty() || ctx.arg.pie) {
|
if (ctx.arg.shared || !ctx.dsos.empty() || ctx.arg.pie) {
|
||||||
ctx.dynamic = push(new DynamicSection<E>(ctx));
|
ctx.dynamic = push(new DynamicSection<E>(ctx));
|
||||||
@ -2602,6 +2604,24 @@ static i64 set_file_offsets(Context<E> &ctx) {
|
|||||||
return fileoff;
|
return fileoff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove debug sections from ctx.chunks and save them to ctx.debug_chunks.
|
||||||
|
// This is for --separate-debug-file.
|
||||||
|
template <typename E>
|
||||||
|
void separate_debug_sections(Context<E> &ctx) {
|
||||||
|
auto is_debug_section = [&](Chunk<E> *chunk) {
|
||||||
|
if (chunk->shdr.sh_flags & SHF_ALLOC)
|
||||||
|
return false;
|
||||||
|
return chunk == ctx.gdb_index || chunk == ctx.symtab || chunk == ctx.strtab ||
|
||||||
|
chunk->name.starts_with(".debug_");
|
||||||
|
};
|
||||||
|
|
||||||
|
auto mid = std::stable_partition(ctx.chunks.begin(), ctx.chunks.end(),
|
||||||
|
is_debug_section);
|
||||||
|
|
||||||
|
ctx.debug_chunks = {ctx.chunks.begin(), mid};
|
||||||
|
ctx.chunks.erase(ctx.chunks.begin(), mid);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename E>
|
template <typename E>
|
||||||
void compute_section_headers(Context<E> &ctx) {
|
void compute_section_headers(Context<E> &ctx) {
|
||||||
// Update sh_size for each chunk.
|
// Update sh_size for each chunk.
|
||||||
@ -2993,6 +3013,106 @@ void write_build_id(Context<E> &ctx) {
|
|||||||
ctx.buildid->copy_buf(ctx);
|
ctx.buildid->copy_buf(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A .gnu_debuglink section contains a filename and a CRC32 checksum of a
|
||||||
|
// debug info file. When we are writing a .gnu_debuglink, we don't know
|
||||||
|
// its CRC32 checksum because we haven't created a debug info file. So we
|
||||||
|
// write a dummy value instead.
|
||||||
|
//
|
||||||
|
// We can't choose a random value as a dummy value for build
|
||||||
|
// reproducibility. We also don't want to write a fixed value for all
|
||||||
|
// files because the CRC checksum is in this section to prevent using
|
||||||
|
// wrong file on debugging. gdb rejects a debug info file if its CRC
|
||||||
|
// doesn't match with the one in .gdb_debuglink.
|
||||||
|
//
|
||||||
|
// Therefore, we'll try to make our CRC checksum as unique as possible.
|
||||||
|
// We'll remember that checksum, and after creating a debug info file, add
|
||||||
|
// a few bytes of garbage at the end of it so that the debug info file's
|
||||||
|
// CRC checksum becomes the one that we have precomputed.
|
||||||
|
template <typename E>
|
||||||
|
void write_gnu_debuglink(Context<E> &ctx) {
|
||||||
|
Timer t(ctx, "write_gnu_debuglink");
|
||||||
|
u32 crc32;
|
||||||
|
|
||||||
|
if (ctx.buildid) {
|
||||||
|
crc32 = compute_crc32(0, ctx.buildid->contents.data(),
|
||||||
|
ctx.buildid->contents.size());
|
||||||
|
} else {
|
||||||
|
std::vector<std::span<u8>> shards = get_shards(ctx);
|
||||||
|
std::vector<U64<E>> hashes(shards.size());
|
||||||
|
|
||||||
|
tbb::parallel_for((i64)0, (i64)shards.size(), [&](i64 i) {
|
||||||
|
hashes[i] = hash_string({(char *)shards[i].data(), shards[i].size()});
|
||||||
|
});
|
||||||
|
crc32 = compute_crc32(0, (u8 *)hashes.data(), hashes.size() * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.gnu_debuglink->crc32 = crc32;
|
||||||
|
ctx.gnu_debuglink->copy_buf(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a separate debug file. This function is called after we finish
|
||||||
|
// writing to the usual output file.
|
||||||
|
template <typename E>
|
||||||
|
void write_separate_debug_file(Context<E> &ctx) {
|
||||||
|
Timer t(ctx, "write_separate_debug_file");
|
||||||
|
|
||||||
|
// We want to write to the debug info file in background so that the
|
||||||
|
// user doesn't have to wait for it to complete.
|
||||||
|
if (ctx.arg.detach)
|
||||||
|
notify_parent();
|
||||||
|
|
||||||
|
// A debug info file contains all sections as the original file, though
|
||||||
|
// most of them can be empty as if they were bss sections. We convert
|
||||||
|
// real sections into dummy sections here.
|
||||||
|
for (i64 i = 0; i < ctx.chunks.size(); i++) {
|
||||||
|
Chunk<E> *chunk = ctx.chunks[i];
|
||||||
|
if (chunk != ctx.ehdr && chunk != ctx.shdr && chunk != ctx.shstrtab &&
|
||||||
|
chunk->shdr.sh_type != SHT_NOTE) {
|
||||||
|
Chunk<E> *sec = new OutputSection<E>(chunk->name, SHT_NULL);
|
||||||
|
sec->shdr = chunk->shdr;
|
||||||
|
sec->shdr.sh_type = SHT_NOBITS;
|
||||||
|
|
||||||
|
ctx.chunks[i] = sec;
|
||||||
|
ctx.chunk_pool.emplace_back(sec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore debug info sections that had been set aside while we were
|
||||||
|
// creating the main file.
|
||||||
|
tbb::parallel_for_each(ctx.debug_chunks, [&](Chunk<E> *chunk) {
|
||||||
|
chunk->compute_section_size(ctx);
|
||||||
|
});
|
||||||
|
|
||||||
|
append(ctx.chunks, ctx.debug_chunks);
|
||||||
|
|
||||||
|
// Write to the debug info file as if it were a regular output file.
|
||||||
|
compute_section_headers(ctx);
|
||||||
|
i64 filesize = set_osec_offsets(ctx);
|
||||||
|
|
||||||
|
ctx.output_file =
|
||||||
|
OutputFile<Context<E>>::open(ctx, ctx.arg.separate_debug_file,
|
||||||
|
filesize, 0666);
|
||||||
|
ctx.buf = ctx.output_file->buf;
|
||||||
|
|
||||||
|
copy_chunks(ctx);
|
||||||
|
|
||||||
|
if (ctx.gdb_index)
|
||||||
|
write_gdb_index(ctx);
|
||||||
|
|
||||||
|
// Reverse-compute a CRC32 value so that the CRC32 checksum embedded to
|
||||||
|
// the .gnu_debuglink section in the main executable matches with the
|
||||||
|
// debug info file's CRC32 checksum.
|
||||||
|
u32 crc = compute_crc32(0, ctx.buf, filesize);
|
||||||
|
|
||||||
|
std::vector<u8> &buf2 = ctx.output_file->buf2;
|
||||||
|
if (!buf2.empty())
|
||||||
|
crc = compute_crc32(crc, buf2.data(), buf2.size());
|
||||||
|
|
||||||
|
std::vector<u8> trailer = crc32_solve(crc, ctx.gnu_debuglink->crc32);
|
||||||
|
append(ctx.output_file->buf2, trailer);
|
||||||
|
ctx.output_file->close(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
// Write Makefile-style dependency rules to a file specified by
|
// Write Makefile-style dependency rules to a file specified by
|
||||||
// --dependency-file. This is analogous to the compiler's -M flag.
|
// --dependency-file. This is analogous to the compiler's -M flag.
|
||||||
template <typename E>
|
template <typename E>
|
||||||
@ -3126,11 +3246,14 @@ template void apply_version_script(Context<E> &);
|
|||||||
template void parse_symbol_version(Context<E> &);
|
template void parse_symbol_version(Context<E> &);
|
||||||
template void compute_import_export(Context<E> &);
|
template void compute_import_export(Context<E> &);
|
||||||
template void compute_address_significance(Context<E> &);
|
template void compute_address_significance(Context<E> &);
|
||||||
|
template void separate_debug_sections(Context<E> &);
|
||||||
template void compute_section_headers(Context<E> &);
|
template void compute_section_headers(Context<E> &);
|
||||||
template i64 set_osec_offsets(Context<E> &);
|
template i64 set_osec_offsets(Context<E> &);
|
||||||
template void fix_synthetic_symbols(Context<E> &);
|
template void fix_synthetic_symbols(Context<E> &);
|
||||||
template i64 compress_debug_sections(Context<E> &);
|
template i64 compress_debug_sections(Context<E> &);
|
||||||
template void write_build_id(Context<E> &);
|
template void write_build_id(Context<E> &);
|
||||||
|
template void write_gnu_debuglink(Context<E> &);
|
||||||
|
template void write_separate_debug_file(Context<E> &);
|
||||||
template void write_dependency_file(Context<E> &);
|
template void write_dependency_file(Context<E> &);
|
||||||
template void show_stats(Context<E> &);
|
template void show_stats(Context<E> &);
|
||||||
|
|
||||||
|
26
test/elf/separate-debug-file.sh
Executable file
26
test/elf/separate-debug-file.sh
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
. $(dirname $0)/common.inc
|
||||||
|
|
||||||
|
on_qemu && skip
|
||||||
|
command -v gdb >& /dev/null || skip
|
||||||
|
command -v flock >& /dev/null || skip
|
||||||
|
|
||||||
|
cat <<EOF > $t/a.c
|
||||||
|
#include <stdio.h>
|
||||||
|
int main() {
|
||||||
|
printf("Hello world\n");
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
$CC -c -o $t/a.o $t/a.c -g
|
||||||
|
$CC -B. -o $t/exe1 $t/a.o -Wl,--separate-debug-file
|
||||||
|
readelf -SW $t/exe1 | grep -Fq .gnu_debuglink
|
||||||
|
|
||||||
|
$CC -c -o $t/a.o $t/a.c -g
|
||||||
|
$CC -B. -o $t/exe2 $t/a.o -Wl,--separate-debug-file -Wl,--no-build-id
|
||||||
|
readelf -SW $t/exe2 | grep -Fq .gnu_debuglink
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
gdb $t/exe1 -ex 'list main' -ex 'quit' | grep -Fq printf
|
||||||
|
gdb $t/exe2 -ex 'list main' -ex 'quit' | grep -Fq printf
|
@ -37,5 +37,5 @@ grep -Eq '.note.baz\s+NOTE.+000008 00 A 0 0 8' $t/log
|
|||||||
grep -Eq '.note.nonalloc\s+NOTE.+000008 00 0 0 1' $t/log
|
grep -Eq '.note.nonalloc\s+NOTE.+000008 00 0 0 1' $t/log
|
||||||
|
|
||||||
readelf --segments $t/exe > $t/log
|
readelf --segments $t/exe > $t/log
|
||||||
grep -Fq '01 .note.baz .note.foo .note.bar' $t/log
|
grep -Fq '01 .note.bar .note.baz .note.foo' $t/log
|
||||||
! grep -q 'NOTE.*0x0000000000000000 0x0000000000000000' $t/log || false
|
! grep -q 'NOTE.*0x0000000000000000 0x0000000000000000' $t/log || false
|
||||||
|
@ -29,4 +29,4 @@ EOF
|
|||||||
./mold -o $t/exe $t/a.o $t/b.o $t/c.o $t/d.o
|
./mold -o $t/exe $t/a.o $t/b.o $t/c.o $t/d.o
|
||||||
|
|
||||||
readelf --segments $t/exe > $t/log
|
readelf --segments $t/exe > $t/log
|
||||||
grep -Fq '01 .note.a .note.c .note.b' $t/log
|
grep -Fq '01 .note.a .note.b .note.c' $t/log
|
||||||
|
Loading…
Reference in New Issue
Block a user