Parse all filename tables from the `__llvm_covmap` section. Each block starts with a 16-byte header followed by FilenamesSize bytes of compressed (or uncompressed) filename data. Header layout (all uint32_t, little-endian): NRecords (always 0 for LLVM coverage format V4+) FilenamesSize CoverageSize (always 0 for V4+) Version (3 = V4, 4 = V5, 5 = V6, …) The filename blob format (ULEB128
| 83 | /// Uncompressed content for V5+ (i.e. format version 4+): |
| 84 | /// For each filename: ULEB128 length followed by that many bytes |
| 85 | std::vector<FilenameTable> parseCovMapFilenames( |
| 86 | const uint8_t * covmap_data, |
| 87 | size_t covmap_size) |
| 88 | { |
| 89 | std::vector<FilenameTable> tables; |
| 90 | |
| 91 | const uint8_t * p = covmap_data; |
| 92 | const uint8_t * const section_end = covmap_data + covmap_size; |
| 93 | |
| 94 | while (p < section_end) |
| 95 | { |
| 96 | /// Align start of each block to 8 bytes within the section. |
| 97 | { |
| 98 | const uintptr_t off = static_cast<uintptr_t>(p - covmap_data); |
| 99 | if (off % 8 != 0) |
| 100 | p += 8 - (off % 8); |
| 101 | } |
| 102 | if (p + 16 > section_end) |
| 103 | break; |
| 104 | |
| 105 | uint32_t n_records; |
| 106 | uint32_t filenames_size; |
| 107 | uint32_t coverage_size; |
| 108 | uint32_t version; |
| 109 | memcpy(&n_records, p, 4); |
| 110 | memcpy(&filenames_size, p + 4, 4); |
| 111 | memcpy(&coverage_size, p + 8, 4); |
| 112 | memcpy(&version, p + 12, 4); |
| 113 | |
| 114 | p += 16; |
| 115 | |
| 116 | if (p + filenames_size > section_end) |
| 117 | break; |
| 118 | |
| 119 | const uint8_t * const fnames_start = p; |
| 120 | const uint8_t * const fnames_end = p + filenames_size; |
| 121 | p = fnames_end; |
| 122 | |
| 123 | /// Compute MD5Hash of the raw filename blob — this is exactly what LLVM stores as FilenamesRef. |
| 124 | const uint64_t block_hash = computeMD5Hash(fnames_start, filenames_size); |
| 125 | |
| 126 | /// Decode the filename blob. |
| 127 | const uint8_t * fp = fnames_start; |
| 128 | bool ok = true; |
| 129 | |
| 130 | const uint64_t num_filenames = readULEB128(fp, fnames_end, ok); |
| 131 | const uint64_t uncompressed_len = readULEB128(fp, fnames_end, ok); |
| 132 | const uint64_t compressed_len = readULEB128(fp, fnames_end, ok); |
| 133 | if (!ok || num_filenames == 0) |
| 134 | continue; |
| 135 | |
| 136 | /// Sanity-check the uncompressed size before allocating to avoid OOM on corrupt data. |
| 137 | /// 256 MB is a generous upper bound; also require it is not implausibly large |
| 138 | /// relative to the compressed/raw input (a 16× expansion factor is already very liberal). |
| 139 | static constexpr uint64_t kMaxUncompressedLen = 256u * 1024u * 1024u; |
| 140 | if (uncompressed_len > kMaxUncompressedLen |
| 141 | || uncompressed_len > static_cast<uint64_t>(filenames_size) * 16) |
| 142 | continue; |
no test coverage detected