mirror of
https://github.com/rui314/mold.git
synced 2024-12-26 01:44:29 +03:00
Add --compress-debug-sections
This commit is contained in:
parent
92a3e78ee0
commit
13003d2479
@ -352,6 +352,13 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
ctx.arg.warn_common = true;
|
||||
} else if (read_flag(args, "no-warn-common")) {
|
||||
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")) {
|
||||
ctx.arg.repro = true;
|
||||
} else if (read_z_flag(args, "now")) {
|
||||
|
@ -3,19 +3,18 @@
|
||||
#include <limits>
|
||||
|
||||
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)
|
||||
return;
|
||||
|
||||
// Copy data
|
||||
u8 *base = ctx.buf + output_section->shdr.sh_offset + offset;
|
||||
memcpy(base, contents.data(), contents.size());
|
||||
memcpy(buf, contents.data(), contents.size());
|
||||
|
||||
// Apply relocations
|
||||
if (shdr.sh_flags & SHF_ALLOC)
|
||||
apply_reloc_alloc(ctx, base);
|
||||
apply_reloc_alloc(ctx, buf);
|
||||
else
|
||||
apply_reloc_nonalloc(ctx, base);
|
||||
apply_reloc_nonalloc(ctx, buf);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
|
11
main.cc
11
main.cc
@ -517,11 +517,18 @@ int do_main(int argc, char **argv) {
|
||||
// Assign offsets to output sections
|
||||
i64 filesize = set_osec_offsets(ctx);
|
||||
|
||||
// At this point, file layout is fixed.
|
||||
|
||||
// Fix linker-synthesized symbol addresses.
|
||||
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
|
||||
// GOT or PLT addresses have a correct final value.
|
||||
|
||||
|
19
mold.h
19
mold.h
@ -250,7 +250,7 @@ public:
|
||||
|
||||
void scan_relocations(Context<E> &ctx);
|
||||
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_nonalloc(Context<E> &ctx, u8 *base);
|
||||
inline void kill();
|
||||
@ -325,7 +325,9 @@ public:
|
||||
// - SYNTHETIC: linker-synthesized sections such as .got or .plt
|
||||
enum Kind : u8 { HEADER, REGULAR, SYNTHETIC };
|
||||
|
||||
virtual ~OutputChunk() = default;
|
||||
virtual void copy_buf(Context<E> &ctx) {}
|
||||
virtual void write_to(Context<E> &ctx, u8 *buf);
|
||||
virtual void update_shdr(Context<E> &ctx) {}
|
||||
|
||||
std::string_view name;
|
||||
@ -398,6 +400,7 @@ public:
|
||||
get_instance(Context<E> &ctx, std::string_view name, u64 type, u64 flags);
|
||||
|
||||
void copy_buf(Context<E> &ctx) override;
|
||||
void write_to(Context<E> &ctx, u8 *buf) override;
|
||||
|
||||
std::vector<InputSection<E> *> members;
|
||||
u32 idx;
|
||||
@ -648,6 +651,7 @@ public:
|
||||
SectionFragment<E> *insert(std::string_view data, i64 alignment);
|
||||
void assign_offsets();
|
||||
void copy_buf(Context<E> &ctx) override;
|
||||
void write_to(Context<E> &ctx, u8 *buf) override;
|
||||
|
||||
private:
|
||||
using MapTy =
|
||||
@ -801,6 +805,16 @@ public:
|
||||
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>
|
||||
class ReproSection : public OutputChunk<E> {
|
||||
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 set_osec_offsets(Context<E> &);
|
||||
template <typename E> void fix_synthetic_symbols(Context<E> &);
|
||||
template <typename E> void compress_debug_sections(Context<E> &);
|
||||
|
||||
//
|
||||
// main.cc
|
||||
@ -1339,6 +1354,7 @@ struct Context {
|
||||
bool Bsymbolic = false;
|
||||
bool Bsymbolic_functions = false;
|
||||
bool allow_multiple_definition = false;
|
||||
bool compress_debug_sections = false;
|
||||
bool demangle = true;
|
||||
bool discard_all = false;
|
||||
bool discard_locals = false;
|
||||
@ -1425,6 +1441,7 @@ struct Context {
|
||||
|
||||
ConcurrentMap<ComdatGroup> comdat_groups;
|
||||
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;
|
||||
FileCache<E, ObjectFile<E>> obj_cache;
|
||||
FileCache<E, SharedFile<E>> dso_cache;
|
||||
|
@ -6,6 +6,12 @@
|
||||
#include <sys/mman.h>
|
||||
#include <tbb/parallel_for_each.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>
|
||||
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>
|
||||
void OutputSection<E>::copy_buf(Context<E> &ctx) {
|
||||
if (this->shdr.sh_type == SHT_NOBITS)
|
||||
return;
|
||||
if (this->shdr.sh_type != SHT_NOBITS)
|
||||
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) {
|
||||
// Copy section contents to an output file
|
||||
InputSection<E> &isec = *members[i];
|
||||
isec.copy_buf(ctx);
|
||||
isec.write_to(ctx, buf + isec.offset);
|
||||
|
||||
// Zero-clear trailing padding
|
||||
u64 this_end = isec.offset + isec.shdr.sh_size;
|
||||
u64 next_start = (i == members.size() - 1) ?
|
||||
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>
|
||||
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) {
|
||||
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++)
|
||||
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
|
||||
}
|
||||
|
||||
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>
|
||||
void ReproSection<E>::update_shdr(Context<E> &ctx) {
|
||||
if (tar)
|
||||
@ -1668,6 +1710,7 @@ void ReproSection<E>::copy_buf(Context<E> &ctx) {
|
||||
template class VerdefSection<E>; \
|
||||
template class BuildIdSection<E>; \
|
||||
template class NotePropertySection<E>; \
|
||||
template class CompressedSection<E>; \
|
||||
template class ReproSection<E>; \
|
||||
template i64 BuildId::size(Context<E> &) const; \
|
||||
template bool is_relro(Context<E> &, OutputChunk<E> *); \
|
||||
|
23
passes.cc
23
passes.cc
@ -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) \
|
||||
template void apply_exclude_libs(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 i64 get_section_rank(Context<E> &ctx, OutputChunk<E> *chunk); \
|
||||
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(I386);
|
||||
|
22
test/compress-debug-sections.sh
Executable file
22
test/compress-debug-sections.sh
Executable 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
|
Loading…
Reference in New Issue
Block a user