Skip to content

Commit 33a8348

Browse files
committed
Improve compressed dwarf offset handling
1 parent 50e4298 commit 33a8348

1 file changed

Lines changed: 58 additions & 1 deletion

File tree

‎src/symbols/dwarf/dwarf_resolver.cpp‎

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
#endif
2424
#if IS_LINUX
2525
#include <elf.h>
26+
#include <stddef.h>
27+
#include <zlib.h>
28+
extern "C" size_t ZSTD_decompress(void*, size_t, const void*, size_t);
2629
#endif
2730

2831
#include <algorithm>
@@ -67,7 +70,61 @@ namespace libdwarf {
6770
explicit in_memory_dwarf_object(elf::object_data&& object_data_)
6871
: object_data(std::move(object_data_)),
6972
relocated_sections(object_data.sections.size(), false),
70-
interface{this, &methods} {}
73+
interface{this, &methods} {
74+
decompress_compressed_sections();
75+
}
76+
77+
// libdwarf decompresses SHF_COMPRESSED sections itself, but only after load_section, and it then
78+
// asks us to relocate its decompressed copy, which we can't reach. Relocations would instead land
79+
// on the compressed bytes, leaving the DWARF unrelocated. Handing libdwarf plain sections avoids it.
80+
void decompress_compressed_sections() {
81+
constexpr std::uint64_t shf_compressed = 0x800;
82+
for(auto& section : object_data.sections) {
83+
if(!(section.flags & shf_compressed) || section.type == SHT_NOBITS) {
84+
continue;
85+
}
86+
const auto header_size = object_data.is_64_bit ? sizeof(Elf64_Chdr) : sizeof(Elf32_Chdr);
87+
if(section.data.size() < header_size) {
88+
continue;
89+
}
90+
std::uint32_t type = 0;
91+
std::uint64_t uncompressed_size = 0;
92+
if(object_data.is_64_bit) {
93+
Elf64_Chdr header;
94+
std::memcpy(&header, section.data.data(), sizeof(header));
95+
type = byteswap_if_needed(header.ch_type);
96+
uncompressed_size = byteswap_if_needed(header.ch_size);
97+
} else {
98+
Elf32_Chdr header;
99+
std::memcpy(&header, section.data.data(), sizeof(header));
100+
type = byteswap_if_needed(header.ch_type);
101+
uncompressed_size = byteswap_if_needed(header.ch_size);
102+
}
103+
if(uncompressed_size > (std::uint64_t(1) << 32)) {
104+
continue;
105+
}
106+
std::vector<char> output(static_cast<std::size_t>(uncompressed_size));
107+
const auto* input = reinterpret_cast<const unsigned char*>(section.data.data()) + header_size;
108+
const auto input_size = section.data.size() - header_size;
109+
bool ok = false;
110+
if(type == 1) { // ELFCOMPRESS_ZLIB
111+
uLongf out_size = static_cast<uLongf>(output.size());
112+
ok = ::uncompress(
113+
reinterpret_cast<Bytef*>(output.data()), &out_size, input, static_cast<uLong>(input_size)
114+
) == Z_OK && out_size == output.size();
115+
} else if(type == 2) { // ELFCOMPRESS_ZSTD
116+
const auto result = ZSTD_decompress(output.data(), output.size(), input, input_size);
117+
ok = result == output.size();
118+
}
119+
if(!ok) {
120+
log::warn("Failed to decompress DWARF section {}", section.name);
121+
continue;
122+
}
123+
section.data = std::move(output);
124+
section.size = section.data.size();
125+
section.flags &= ~shf_compressed;
126+
}
127+
}
71128

72129
template<typename T>
73130
T byteswap_if_needed(T value) const {

0 commit comments

Comments
 (0)