Skip to content

Commit c028765

Browse files
authored
Merge pull request #1569 from pierluigilenoci/feat/zstd-compression-level
Add --compress-debug-sections=zstd:N for configurable zstd level
2 parents 84c411b + 0420959 commit c028765

7 files changed

Lines changed: 54 additions & 13 deletions

File tree

docs/mold.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -506,9 +506,12 @@ but as `-o magic`.
506506
* `--no-build-id`:
507507
Synonym for `--build-id=none`.
508508

509-
* `--compress-debug-sections`=[ `zlib` | `zlib-gabi` | `zstd` | `none` ]:
509+
* `--compress-debug-sections`=[ `zlib` | `zlib-gabi` | `zstd` | `zstd:`_N_ | `none` ]:
510510
Compress DWARF debug info (`.debug_*` sections) using the zlib or zstd
511-
compression algorithm. `zlib-gabi` is an alias for `zlib`.
511+
compression algorithm. `zlib-gabi` is an alias for `zlib`. `zstd:`_N_
512+
selects the zstd compression level, where _N_ is between 1 and 22.
513+
Higher levels achieve better compression ratios but are slower.
514+
`zstd` is equivalent to `zstd:3`.
512515

513516
* `--defsym`=_symbol_=_value_:
514517
Define _symbol_ as an alias for _value_.

lib/compress.cc

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,22 +139,21 @@ void ZlibCompressor::write_to(u8 *buf) {
139139
*(ub32 *)(end - 4) = checksum;
140140
}
141141

142-
static std::span<u8> zstd_compress(std::span<u8> input) {
142+
static std::span<u8> zstd_compress(std::span<u8> input, int level) {
143143
i64 bufsize = ZSTD_COMPRESSBOUND(input.size());
144144
u8 *buf = new u8[bufsize];
145-
int level = 3; // compression level; must be between 1 to 22
146145
size_t sz = ZSTD_compress(buf, bufsize, input.data(), input.size(), level);
147146
assert(!ZSTD_isError(sz));
148147
return {buf, sz};
149148
}
150149

151-
ZstdCompressor::ZstdCompressor(u8 *buf, i64 size) {
150+
ZstdCompressor::ZstdCompressor(u8 *buf, i64 size, i64 level) {
152151
std::vector<std::span<u8>> inputs = split(std::span(buf, size));
153152
shards.resize(inputs.size());
154153

155154
// Compress each shard
156155
tbb::parallel_for((i64)0, (i64)inputs.size(), [&](i64 i) {
157-
shards[i] = zstd_compress(inputs[i]);
156+
shards[i] = zstd_compress(inputs[i], level);
158157
});
159158

160159
compressed_size = 0;

lib/lib.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ class ZlibCompressor : public Compressor {
674674

675675
class ZstdCompressor : public Compressor {
676676
public:
677-
ZstdCompressor(u8 *buf, i64 size);
677+
ZstdCompressor(u8 *buf, i64 size, i64 level = 3);
678678
void write_to(u8 *buf) override;
679679
};
680680

src/cmdline.cc

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ static const char helpmsg[] = R"(
8989
--color-diagnostics=[auto,always,never]
9090
Use colors in diagnostics
9191
--color-diagnostics Alias for --color-diagnostics=always
92-
--compress-debug-sections [none,zlib,zlib-gabi,zstd]
92+
--compress-debug-sections [none,zlib,zlib-gabi,zstd,zstd:1,...,zstd:22]
9393
Compress .debug_* sections
9494
--dc Ignored
9595
--dependency-file=FILE Write Makefile-style dependency rules to FILE
@@ -1039,14 +1039,22 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
10391039
} else if (read_flag("zero-to-bss")) {
10401040
ctx.arg.zero_to_bss = true;
10411041
} else if (read_arg("compress-debug-sections")) {
1042-
if (arg == "zlib" || arg == "zlib-gabi")
1042+
if (arg == "zlib" || arg == "zlib-gabi") {
10431043
ctx.arg.compress_debug_sections = ELFCOMPRESS_ZLIB;
1044-
else if (arg == "zstd")
1044+
} else if (arg == "zstd") {
10451045
ctx.arg.compress_debug_sections = ELFCOMPRESS_ZSTD;
1046-
else if (arg == "none")
1046+
} else if (arg.starts_with("zstd:")) {
1047+
ctx.arg.compress_debug_sections = ELFCOMPRESS_ZSTD;
1048+
i64 level = parse_number(ctx, "compress-debug-sections", arg.substr(5));
1049+
if (level < 1 || level > 22)
1050+
Fatal(ctx) << "invalid --compress-debug-sections argument: " << arg
1051+
<< " (zstd level must be between 1 and 22)";
1052+
ctx.arg.compress_debug_sections_level = level;
1053+
} else if (arg == "none") {
10471054
ctx.arg.compress_debug_sections = ELFCOMPRESS_NONE;
1048-
else
1055+
} else {
10491056
Fatal(ctx) << "invalid --compress-debug-sections argument: " << arg;
1057+
}
10501058
} else if (read_arg("wrap")) {
10511059
ctx.arg.wrap.insert(arg);
10521060
} else if (read_flag("omagic") || read_flag("N")) {

src/mold.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2420,6 +2420,7 @@ struct Context {
24202420
bool z_text = false;
24212421
bool zero_to_bss = false;
24222422
i64 compress_debug_sections = ELFCOMPRESS_NONE;
2423+
i64 compress_debug_sections_level = 3;
24232424
i64 filler = -1;
24242425
i64 spare_dynamic_tags = 5;
24252426
i64 spare_program_headers = 0;

src/output-chunks.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2863,7 +2863,8 @@ CompressedSection<E>::CompressedSection(Context<E> &ctx, Chunk<E> &chunk) {
28632863
if (ctx.arg.compress_debug_sections == ELFCOMPRESS_ZLIB)
28642864
compressor.reset(new ZlibCompressor(buf.get(), chunk.shdr.sh_size));
28652865
else
2866-
compressor.reset(new ZstdCompressor(buf.get(), chunk.shdr.sh_size));
2866+
compressor.reset(new ZstdCompressor(buf.get(), chunk.shdr.sh_size,
2867+
ctx.arg.compress_debug_sections_level));
28672868

28682869
// Compute header field values
28692870
chdr.ch_type = ctx.arg.compress_debug_sections;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#!/bin/bash
2+
. $(dirname $0)/common.inc
3+
4+
# arm-linux-gnueabihf-objcopy crashes on x86-64
5+
[[ $MACHINE = arm* ]] && skip
6+
[ $MACHINE = riscv32 ] && skip
7+
8+
command -v zstdcat >& /dev/null || skip
9+
10+
cat <<EOF | $CC -c -g -o $t/a.o -xc -
11+
#include <stdio.h>
12+
13+
int main() {
14+
printf("Hello world\n");
15+
return 0;
16+
}
17+
EOF
18+
19+
# Test zstd:1 (lowest level)
20+
$CC -B. -o $t/exe1 $t/a.o -Wl,--compress-debug-sections=zstd:1
21+
$OBJCOPY --dump-section .debug_info=$t/debug_info1 $t/exe1
22+
dd if=$t/debug_info1 of=$t/debug_info1.zstd bs=24 skip=1 status=none
23+
zstdcat $t/debug_info1.zstd > /dev/null
24+
25+
# Test zstd:19 (high level)
26+
$CC -B. -o $t/exe19 $t/a.o -Wl,--compress-debug-sections=zstd:19
27+
$OBJCOPY --dump-section .debug_info=$t/debug_info19 $t/exe19
28+
dd if=$t/debug_info19 of=$t/debug_info19.zstd bs=24 skip=1 status=none
29+
zstdcat $t/debug_info19.zstd > /dev/null

0 commit comments

Comments
 (0)