|
23 | 23 | #endif |
24 | 24 | #if IS_LINUX |
25 | 25 | #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); |
26 | 29 | #endif |
27 | 30 |
|
28 | 31 | #include <algorithm> |
@@ -67,7 +70,61 @@ namespace libdwarf { |
67 | 70 | explicit in_memory_dwarf_object(elf::object_data&& object_data_) |
68 | 71 | : object_data(std::move(object_data_)), |
69 | 72 | 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 | + } |
71 | 128 |
|
72 | 129 | template<typename T> |
73 | 130 | T byteswap_if_needed(T value) const { |
|
0 commit comments