1
1
mirror of https://github.com/rui314/mold.git synced 2024-12-26 18:02:30 +03:00

Add --compress-debug-sections

This commit is contained in:
Rui Ueyama 2021-05-06 11:29:02 +09:00
parent 92a3e78ee0
commit 13003d2479
7 changed files with 133 additions and 17 deletions

View File

@ -352,6 +352,13 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.arg.warn_common = true; ctx.arg.warn_common = true;
} else if (read_flag(args, "no-warn-common")) { } else if (read_flag(args, "no-warn-common")) {
ctx.arg.warn_common = false; ctx.arg.warn_common = false;
} else if (read_arg(ctx, args, arg, "compress-debug-sections")) {
if (arg == "zlib" || arg == "zlib-gabi")
ctx.arg.compress_debug_sections = true;
else if (arg == "none")
ctx.arg.compress_debug_sections = false;
else
Fatal(ctx) << "invalid --compress-debug-sections argument: " << arg;
} else if (read_flag(args, "repro")) { } else if (read_flag(args, "repro")) {
ctx.arg.repro = true; ctx.arg.repro = true;
} else if (read_z_flag(args, "now")) { } else if (read_z_flag(args, "now")) {

View File

@ -3,19 +3,18 @@
#include <limits> #include <limits>
template <typename E> template <typename E>
void InputSection<E>::copy_buf(Context<E> &ctx) { void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
if (shdr.sh_type == SHT_NOBITS || shdr.sh_size == 0) if (shdr.sh_type == SHT_NOBITS || shdr.sh_size == 0)
return; return;
// Copy data // Copy data
u8 *base = ctx.buf + output_section->shdr.sh_offset + offset; memcpy(buf, contents.data(), contents.size());
memcpy(base, contents.data(), contents.size());
// Apply relocations // Apply relocations
if (shdr.sh_flags & SHF_ALLOC) if (shdr.sh_flags & SHF_ALLOC)
apply_reloc_alloc(ctx, base); apply_reloc_alloc(ctx, buf);
else else
apply_reloc_nonalloc(ctx, base); apply_reloc_nonalloc(ctx, buf);
} }
template <typename E> template <typename E>

11
main.cc
View File

@ -517,11 +517,18 @@ int do_main(int argc, char **argv) {
// Assign offsets to output sections // Assign offsets to output sections
i64 filesize = set_osec_offsets(ctx); i64 filesize = set_osec_offsets(ctx);
// At this point, file layout is fixed.
// Fix linker-synthesized symbol addresses. // Fix linker-synthesized symbol addresses.
fix_synthetic_symbols(ctx); fix_synthetic_symbols(ctx);
// If --compress-debug-sections is given, compress .debug_* sections
// using zlib.
if (ctx.arg.compress_debug_sections) {
compress_debug_sections(ctx);
filesize = set_osec_offsets(ctx);
}
// At this point, file layout is fixed.
// Beyond this, you can assume that symbol addresses including their // Beyond this, you can assume that symbol addresses including their
// GOT or PLT addresses have a correct final value. // GOT or PLT addresses have a correct final value.

19
mold.h
View File

@ -250,7 +250,7 @@ public:
void scan_relocations(Context<E> &ctx); void scan_relocations(Context<E> &ctx);
void report_undefined_symbols(); void report_undefined_symbols();
void copy_buf(Context<E> &ctx); void write_to(Context<E> &ctx, u8 *buf);
void apply_reloc_alloc(Context<E> &ctx, u8 *base); void apply_reloc_alloc(Context<E> &ctx, u8 *base);
void apply_reloc_nonalloc(Context<E> &ctx, u8 *base); void apply_reloc_nonalloc(Context<E> &ctx, u8 *base);
inline void kill(); inline void kill();
@ -325,7 +325,9 @@ public:
// - SYNTHETIC: linker-synthesized sections such as .got or .plt // - SYNTHETIC: linker-synthesized sections such as .got or .plt
enum Kind : u8 { HEADER, REGULAR, SYNTHETIC }; enum Kind : u8 { HEADER, REGULAR, SYNTHETIC };
virtual ~OutputChunk() = default;
virtual void copy_buf(Context<E> &ctx) {} virtual void copy_buf(Context<E> &ctx) {}
virtual void write_to(Context<E> &ctx, u8 *buf);
virtual void update_shdr(Context<E> &ctx) {} virtual void update_shdr(Context<E> &ctx) {}
std::string_view name; std::string_view name;
@ -398,6 +400,7 @@ public:
get_instance(Context<E> &ctx, std::string_view name, u64 type, u64 flags); get_instance(Context<E> &ctx, std::string_view name, u64 type, u64 flags);
void copy_buf(Context<E> &ctx) override; void copy_buf(Context<E> &ctx) override;
void write_to(Context<E> &ctx, u8 *buf) override;
std::vector<InputSection<E> *> members; std::vector<InputSection<E> *> members;
u32 idx; u32 idx;
@ -648,6 +651,7 @@ public:
SectionFragment<E> *insert(std::string_view data, i64 alignment); SectionFragment<E> *insert(std::string_view data, i64 alignment);
void assign_offsets(); void assign_offsets();
void copy_buf(Context<E> &ctx) override; void copy_buf(Context<E> &ctx) override;
void write_to(Context<E> &ctx, u8 *buf) override;
private: private:
using MapTy = using MapTy =
@ -801,6 +805,16 @@ public:
u32 features = 0; u32 features = 0;
}; };
template <typename E>
class CompressedSection : public OutputChunk<E> {
public:
CompressedSection(Context<E> &ctx, OutputChunk<E> &chunk);
void copy_buf(Context<E> &ctx) override;
private:
std::unique_ptr<u8[]> contents;
};
template <typename E> template <typename E>
class ReproSection : public OutputChunk<E> { class ReproSection : public OutputChunk<E> {
public: public:
@ -1279,6 +1293,7 @@ template <typename E> void clear_padding(Context<E> &);
template <typename E> i64 get_section_rank(Context<E> &, OutputChunk<E> *chunk); template <typename E> i64 get_section_rank(Context<E> &, OutputChunk<E> *chunk);
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> void compress_debug_sections(Context<E> &);
// //
// main.cc // main.cc
@ -1339,6 +1354,7 @@ struct Context {
bool Bsymbolic = false; bool Bsymbolic = false;
bool Bsymbolic_functions = false; bool Bsymbolic_functions = false;
bool allow_multiple_definition = false; bool allow_multiple_definition = false;
bool compress_debug_sections = false;
bool demangle = true; bool demangle = true;
bool discard_all = false; bool discard_all = false;
bool discard_locals = false; bool discard_locals = false;
@ -1425,6 +1441,7 @@ struct Context {
ConcurrentMap<ComdatGroup> comdat_groups; ConcurrentMap<ComdatGroup> comdat_groups;
tbb::concurrent_vector<std::unique_ptr<MergedSection<E>>> merged_sections; tbb::concurrent_vector<std::unique_ptr<MergedSection<E>>> merged_sections;
tbb::concurrent_vector<std::unique_ptr<OutputChunk<E>>> output_chunks;
std::vector<std::unique_ptr<OutputSection<E>>> output_sections; std::vector<std::unique_ptr<OutputSection<E>>> output_sections;
FileCache<E, ObjectFile<E>> obj_cache; FileCache<E, ObjectFile<E>> obj_cache;
FileCache<E, SharedFile<E>> dso_cache; FileCache<E, SharedFile<E>> dso_cache;

View File

@ -6,6 +6,12 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <tbb/parallel_for_each.h> #include <tbb/parallel_for_each.h>
#include <tbb/parallel_sort.h> #include <tbb/parallel_sort.h>
#include <zlib.h>
template <typename E>
void OutputChunk<E>::write_to(Context<E> &ctx, u8 *buf) {
Fatal(ctx) << name << ": write_to is called on an invalid section";
}
template <typename E> template <typename E>
void OutputEhdr<E>::copy_buf(Context<E> &ctx) { void OutputEhdr<E>::copy_buf(Context<E> &ctx) {
@ -599,19 +605,22 @@ OutputSection<E>::get_instance(Context<E> &ctx, std::string_view name,
template <typename E> template <typename E>
void OutputSection<E>::copy_buf(Context<E> &ctx) { void OutputSection<E>::copy_buf(Context<E> &ctx) {
if (this->shdr.sh_type == SHT_NOBITS) if (this->shdr.sh_type != SHT_NOBITS)
return; write_to(ctx, ctx.buf + this->shdr.sh_offset);
}
template <typename E>
void OutputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
tbb::parallel_for((i64)0, (i64)members.size(), [&](i64 i) { tbb::parallel_for((i64)0, (i64)members.size(), [&](i64 i) {
// Copy section contents to an output file // Copy section contents to an output file
InputSection<E> &isec = *members[i]; InputSection<E> &isec = *members[i];
isec.copy_buf(ctx); isec.write_to(ctx, buf + isec.offset);
// Zero-clear trailing padding // Zero-clear trailing padding
u64 this_end = isec.offset + isec.shdr.sh_size; u64 this_end = isec.offset + isec.shdr.sh_size;
u64 next_start = (i == members.size() - 1) ? u64 next_start = (i == members.size() - 1) ?
this->shdr.sh_size : members[i + 1]->offset; this->shdr.sh_size : members[i + 1]->offset;
memset(ctx.buf + this->shdr.sh_offset + this_end, 0, next_start - this_end); memset(buf + this_end, 0, next_start - this_end);
}); });
} }
@ -1150,14 +1159,16 @@ void MergedSection<E>::assign_offsets() {
template <typename E> template <typename E>
void MergedSection<E>::copy_buf(Context<E> &ctx) { void MergedSection<E>::copy_buf(Context<E> &ctx) {
u8 *base = ctx.buf + this->shdr.sh_offset; write_to(ctx, ctx.buf + this->shdr.sh_offset);
}
template <typename E>
void MergedSection<E>::write_to(Context<E> &ctx, u8 *buf) {
tbb::parallel_for((i64)0, NUM_SHARDS, [&](i64 i) { tbb::parallel_for((i64)0, NUM_SHARDS, [&](i64 i) {
memset(base + shard_offsets[i], 0, shard_offsets[i + 1] - shard_offsets[i]); memset(buf + shard_offsets[i], 0, shard_offsets[i + 1] - shard_offsets[i]);
for (auto it = maps[i].begin(); it != maps[i].end(); it++) for (auto it = maps[i].begin(); it != maps[i].end(); it++)
if (SectionFragment<E> &frag = it->second; frag.is_alive) if (SectionFragment<E> &frag = it->second; frag.is_alive)
memcpy(base + frag.offset, frag.data.data(), frag.data.size()); memcpy(buf + frag.offset, frag.data.data(), frag.data.size());
}); });
} }
@ -1614,6 +1625,37 @@ void NotePropertySection<E>::copy_buf(Context<E> &ctx) {
buf[6] = features; // Feature flags buf[6] = features; // Feature flags
} }
template <typename E>
CompressedSection<E>::CompressedSection(Context<E> &ctx, OutputChunk<E> &chunk)
: OutputChunk<E>(this->SYNTHETIC) {
assert(chunk.name.starts_with(".debug"));
this->name = chunk.name;
std::unique_ptr<u8[]> buf(new u8[chunk.shdr.sh_size]);
chunk.write_to(ctx, buf.get());
ElfChdr<E> hdr = {};
hdr.ch_type = ELFCOMPRESS_ZLIB;
hdr.ch_size = chunk.shdr.sh_size;
hdr.ch_addralign = chunk.shdr.sh_addralign;
unsigned long size = compressBound(chunk.shdr.sh_size);
contents.reset(new u8[sizeof(hdr) + size]);
memcpy(contents.get(), &hdr, sizeof(hdr));
int res = compress2(contents.get() + sizeof(hdr), &size, buf.get(),
chunk.shdr.sh_size, Z_DEFAULT_COMPRESSION);
this->shdr = chunk.shdr;
this->shdr.sh_flags |= SHF_COMPRESSED;
this->shdr.sh_addralign = 1;
this->shdr.sh_size = sizeof(hdr) + size;
}
template <typename E>
void CompressedSection<E>::copy_buf(Context<E> &ctx) {
memcpy(ctx.buf + this->shdr.sh_offset, contents.get(), this->shdr.sh_size);
}
template <typename E> template <typename E>
void ReproSection<E>::update_shdr(Context<E> &ctx) { void ReproSection<E>::update_shdr(Context<E> &ctx) {
if (tar) if (tar)
@ -1668,6 +1710,7 @@ void ReproSection<E>::copy_buf(Context<E> &ctx) {
template class VerdefSection<E>; \ template class VerdefSection<E>; \
template class BuildIdSection<E>; \ template class BuildIdSection<E>; \
template class NotePropertySection<E>; \ template class NotePropertySection<E>; \
template class CompressedSection<E>; \
template class ReproSection<E>; \ template class ReproSection<E>; \
template i64 BuildId::size(Context<E> &) const; \ template i64 BuildId::size(Context<E> &) const; \
template bool is_relro(Context<E> &, OutputChunk<E> *); \ template bool is_relro(Context<E> &, OutputChunk<E> *); \

View File

@ -783,6 +783,26 @@ void fix_synthetic_symbols(Context<E> &ctx) {
} }
} }
template <typename E>
void compress_debug_sections(Context<E> &ctx) {
Timer t(ctx, "compress_debug_sections");
tbb::parallel_for((i64)0, (i64)ctx.chunks.size(), [&](i64 i) {
OutputChunk<E> &chunk = *ctx.chunks[i];
if (!(chunk.shdr.sh_flags & SHF_ALLOC) &&
chunk.shdr.sh_size > 0 &&
chunk.name.starts_with(".debug")) {
CompressedSection<E> *comp = new CompressedSection<E>(ctx, chunk);
ctx.output_chunks.push_back(std::unique_ptr<OutputChunk<E>>(comp));
ctx.chunks[i] = comp;
}
});
ctx.ehdr->update_shdr(ctx);
ctx.shdr->update_shdr(ctx);
}
#define INSTANTIATE(E) \ #define INSTANTIATE(E) \
template void apply_exclude_libs(Context<E> &ctx); \ template void apply_exclude_libs(Context<E> &ctx); \
template void create_synthetic_sections(Context<E> &ctx); \ template void create_synthetic_sections(Context<E> &ctx); \
@ -805,7 +825,8 @@ void fix_synthetic_symbols(Context<E> &ctx) {
template void clear_padding(Context<E> &ctx); \ template void clear_padding(Context<E> &ctx); \
template i64 get_section_rank(Context<E> &ctx, OutputChunk<E> *chunk); \ template i64 get_section_rank(Context<E> &ctx, OutputChunk<E> *chunk); \
template i64 set_osec_offsets(Context<E> &ctx); \ template i64 set_osec_offsets(Context<E> &ctx); \
template void fix_synthetic_symbols(Context<E> &ctx); template void fix_synthetic_symbols(Context<E> &ctx); \
template void compress_debug_sections(Context<E> &ctx);
INSTANTIATE(X86_64); INSTANTIATE(X86_64);
INSTANTIATE(I386); INSTANTIATE(I386);

22
test/compress-debug-sections.sh Executable file
View File

@ -0,0 +1,22 @@
#!/bin/bash
set -e
cd $(dirname $0)
echo -n "Testing $(basename -s .sh $0) ... "
t=$(pwd)/tmp/$(basename -s .sh $0)
mkdir -p $t
cat <<EOF | clang -c -g -o $t/a.o -xc -
#include <stdio.h>
int main() {
printf("Hello world\n");
return 0;
}
EOF
clang -fuse-ld=`pwd`/../mold -o $t/exe $t/a.o -Wl,--compress-debug-sections=zlib
dwarfdump $t/exe > $t/log
grep -q '.debug_info SHF_COMPRESSED' $t/log
grep -q '.debug_str SHF_COMPRESSED' $t/log
echo OK