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:
parent
92a3e78ee0
commit
13003d2479
@ -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")) {
|
||||||
|
@ -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
11
main.cc
@ -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
19
mold.h
@ -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;
|
||||||
|
@ -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> *); \
|
||||||
|
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) \
|
#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
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