1 //===- GsymCreator.cpp ----------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 //===----------------------------------------------------------------------===// 7 8 #include "llvm/DebugInfo/GSYM/GsymCreator.h" 9 #include "llvm/DebugInfo/GSYM/FileWriter.h" 10 #include "llvm/DebugInfo/GSYM/Header.h" 11 #include "llvm/DebugInfo/GSYM/LineTable.h" 12 #include "llvm/MC/StringTableBuilder.h" 13 #include "llvm/Support/raw_ostream.h" 14 15 #include <algorithm> 16 #include <cassert> 17 #include <functional> 18 #include <vector> 19 20 using namespace llvm; 21 using namespace gsym; 22 23 24 GsymCreator::GsymCreator() : StrTab(StringTableBuilder::ELF) { 25 insertFile(StringRef()); 26 } 27 28 uint32_t GsymCreator::insertFile(StringRef Path, 29 llvm::sys::path::Style Style) { 30 llvm::StringRef directory = llvm::sys::path::parent_path(Path, Style); 31 llvm::StringRef filename = llvm::sys::path::filename(Path, Style); 32 FileEntry FE(insertString(directory), insertString(filename)); 33 34 std::lock_guard<std::recursive_mutex> Guard(Mutex); 35 const auto NextIndex = Files.size(); 36 // Find FE in hash map and insert if not present. 37 auto R = FileEntryToIndex.insert(std::make_pair(FE, NextIndex)); 38 if (R.second) 39 Files.emplace_back(FE); 40 return R.first->second; 41 } 42 43 llvm::Error GsymCreator::save(StringRef Path, 44 llvm::support::endianness ByteOrder) const { 45 std::error_code EC; 46 raw_fd_ostream OutStrm(Path, EC); 47 if (EC) 48 return llvm::errorCodeToError(EC); 49 FileWriter O(OutStrm, ByteOrder); 50 return encode(O); 51 } 52 53 llvm::Error GsymCreator::encode(FileWriter &O) const { 54 std::lock_guard<std::recursive_mutex> Guard(Mutex); 55 if (Funcs.empty()) 56 return createStringError(std::errc::invalid_argument, 57 "no functions to encode"); 58 if (!Finalized) 59 return createStringError(std::errc::invalid_argument, 60 "GsymCreator wasn't finalized prior to encoding"); 61 62 if (Funcs.size() > UINT32_MAX) 63 return createStringError(std::errc::invalid_argument, 64 "too many FunctionInfos"); 65 66 const uint64_t MinAddr = BaseAddress ? *BaseAddress : Funcs.front().startAddress(); 67 const uint64_t MaxAddr = Funcs.back().startAddress(); 68 const uint64_t AddrDelta = MaxAddr - MinAddr; 69 Header Hdr; 70 Hdr.Magic = GSYM_MAGIC; 71 Hdr.Version = GSYM_VERSION; 72 Hdr.AddrOffSize = 0; 73 Hdr.UUIDSize = static_cast<uint8_t>(UUID.size()); 74 Hdr.BaseAddress = MinAddr; 75 Hdr.NumAddresses = static_cast<uint32_t>(Funcs.size()); 76 Hdr.StrtabOffset = 0; // We will fix this up later. 77 Hdr.StrtabOffset = 0; // We will fix this up later. 78 memset(Hdr.UUID, 0, sizeof(Hdr.UUID)); 79 if (UUID.size() > sizeof(Hdr.UUID)) 80 return createStringError(std::errc::invalid_argument, 81 "invalid UUID size %u", (uint32_t)UUID.size()); 82 // Set the address offset size correctly in the GSYM header. 83 if (AddrDelta <= UINT8_MAX) 84 Hdr.AddrOffSize = 1; 85 else if (AddrDelta <= UINT16_MAX) 86 Hdr.AddrOffSize = 2; 87 else if (AddrDelta <= UINT32_MAX) 88 Hdr.AddrOffSize = 4; 89 else 90 Hdr.AddrOffSize = 8; 91 // Copy the UUID value if we have one. 92 if (UUID.size() > 0) 93 memcpy(Hdr.UUID, UUID.data(), UUID.size()); 94 // Write out the header. 95 llvm::Error Err = Hdr.encode(O); 96 if (Err) 97 return Err; 98 99 // Write out the address offsets. 100 O.alignTo(Hdr.AddrOffSize); 101 for (const auto &FuncInfo : Funcs) { 102 uint64_t AddrOffset = FuncInfo.startAddress() - Hdr.BaseAddress; 103 switch(Hdr.AddrOffSize) { 104 case 1: O.writeU8(static_cast<uint8_t>(AddrOffset)); break; 105 case 2: O.writeU16(static_cast<uint16_t>(AddrOffset)); break; 106 case 4: O.writeU32(static_cast<uint32_t>(AddrOffset)); break; 107 case 8: O.writeU64(AddrOffset); break; 108 } 109 } 110 111 // Write out all zeros for the AddrInfoOffsets. 112 O.alignTo(4); 113 const off_t AddrInfoOffsetsOffset = O.tell(); 114 for (size_t i = 0, n = Funcs.size(); i < n; ++i) 115 O.writeU32(0); 116 117 // Write out the file table 118 O.alignTo(4); 119 assert(!Files.empty()); 120 assert(Files[0].Dir == 0); 121 assert(Files[0].Base == 0); 122 size_t NumFiles = Files.size(); 123 if (NumFiles > UINT32_MAX) 124 return createStringError(std::errc::invalid_argument, 125 "too many files"); 126 O.writeU32(static_cast<uint32_t>(NumFiles)); 127 for (auto File: Files) { 128 O.writeU32(File.Dir); 129 O.writeU32(File.Base); 130 } 131 132 // Write out the sting table. 133 const off_t StrtabOffset = O.tell(); 134 StrTab.write(O.get_stream()); 135 const off_t StrtabSize = O.tell() - StrtabOffset; 136 std::vector<uint32_t> AddrInfoOffsets; 137 138 // Write out the address infos for each function info. 139 for (const auto &FuncInfo : Funcs) { 140 if (Expected<uint64_t> OffsetOrErr = FuncInfo.encode(O)) 141 AddrInfoOffsets.push_back(OffsetOrErr.get()); 142 else 143 return OffsetOrErr.takeError(); 144 } 145 // Fixup the string table offset and size in the header 146 O.fixup32((uint32_t)StrtabOffset, offsetof(Header, StrtabOffset)); 147 O.fixup32((uint32_t)StrtabSize, offsetof(Header, StrtabSize)); 148 149 // Fixup all address info offsets 150 uint64_t Offset = 0; 151 for (auto AddrInfoOffset: AddrInfoOffsets) { 152 O.fixup32(AddrInfoOffset, AddrInfoOffsetsOffset + Offset); 153 Offset += 4; 154 } 155 return ErrorSuccess(); 156 } 157 158 llvm::Error GsymCreator::finalize(llvm::raw_ostream &OS) { 159 std::lock_guard<std::recursive_mutex> Guard(Mutex); 160 if (Finalized) 161 return createStringError(std::errc::invalid_argument, 162 "already finalized"); 163 Finalized = true; 164 165 // Sort function infos so we can emit sorted functions. 166 llvm::sort(Funcs.begin(), Funcs.end()); 167 168 // Don't let the string table indexes change by finalizing in order. 169 StrTab.finalizeInOrder(); 170 171 // Remove duplicates function infos that have both entries from debug info 172 // (DWARF or Breakpad) and entries from the SymbolTable. 173 // 174 // Also handle overlapping function. Usually there shouldn't be any, but they 175 // can and do happen in some rare cases. 176 // 177 // (a) (b) (c) 178 // ^ ^ ^ ^ 179 // |X |Y |X ^ |X 180 // | | | |Y | ^ 181 // | | | v v |Y 182 // v v v v 183 // 184 // In (a) and (b), Y is ignored and X will be reported for the full range. 185 // In (c), both functions will be included in the result and lookups for an 186 // address in the intersection will return Y because of binary search. 187 // 188 // Note that in case of (b), we cannot include Y in the result because then 189 // we wouldn't find any function for range (end of Y, end of X) 190 // with binary search 191 auto NumBefore = Funcs.size(); 192 auto Curr = Funcs.begin(); 193 auto Prev = Funcs.end(); 194 while (Curr != Funcs.end()) { 195 // Can't check for overlaps or same address ranges if we don't have a 196 // previous entry 197 if (Prev != Funcs.end()) { 198 if (Prev->Range.intersects(Curr->Range)) { 199 // Overlapping address ranges. 200 if (Prev->Range == Curr->Range) { 201 // Same address range. Check if one is from debug info and the other 202 // is from a symbol table. If so, then keep the one with debug info. 203 // Our sorting guarantees that entries with matching address ranges 204 // that have debug info are last in the sort. 205 if (*Prev == *Curr) { 206 // FunctionInfo entries match exactly (range, lines, inlines) 207 OS << "warning: duplicate function info entries for range: " 208 << Curr->Range << '\n'; 209 Curr = Funcs.erase(Prev); 210 } else { 211 if (!Prev->hasRichInfo() && Curr->hasRichInfo()) { 212 // Same address range, one with no debug info (symbol) and the 213 // next with debug info. Keep the latter. 214 Curr = Funcs.erase(Prev); 215 } else { 216 OS << "warning: same address range contains different debug " 217 << "info. Removing:\n" 218 << *Prev << "\nIn favor of this one:\n" 219 << *Curr << "\n"; 220 Curr = Funcs.erase(Prev); 221 } 222 } 223 } else { 224 // print warnings about overlaps 225 OS << "warning: function ranges overlap:\n" 226 << *Prev << "\n" 227 << *Curr << "\n"; 228 } 229 } else if (Prev->Range.size() == 0 && 230 Curr->Range.contains(Prev->Range.Start)) { 231 OS << "warning: removing symbol:\n" 232 << *Prev << "\nKeeping:\n" 233 << *Curr << "\n"; 234 Curr = Funcs.erase(Prev); 235 } 236 } 237 if (Curr == Funcs.end()) 238 break; 239 Prev = Curr++; 240 } 241 242 // If our last function info entry doesn't have a size and if we have valid 243 // text ranges, we should set the size of the last entry since any search for 244 // a high address might match our last entry. By fixing up this size, we can 245 // help ensure we don't cause lookups to always return the last symbol that 246 // has no size when doing lookups. 247 if (!Funcs.empty() && Funcs.back().Range.size() == 0 && ValidTextRanges) { 248 if (auto Range = ValidTextRanges->getRangeThatContains( 249 Funcs.back().Range.Start)) { 250 Funcs.back().Range.End = Range->End; 251 } 252 } 253 OS << "Pruned " << NumBefore - Funcs.size() << " functions, ended with " 254 << Funcs.size() << " total\n"; 255 return Error::success(); 256 } 257 258 uint32_t GsymCreator::insertString(StringRef S, bool Copy) { 259 if (S.empty()) 260 return 0; 261 std::lock_guard<std::recursive_mutex> Guard(Mutex); 262 if (Copy) { 263 // We need to provide backing storage for the string if requested 264 // since StringTableBuilder stores references to strings. Any string 265 // that comes from a section in an object file doesn't need to be 266 // copied, but any string created by code will need to be copied. 267 // This allows GsymCreator to be really fast when parsing DWARF and 268 // other object files as most strings don't need to be copied. 269 CachedHashStringRef CHStr(S); 270 if (!StrTab.contains(CHStr)) 271 S = StringStorage.insert(S).first->getKey(); 272 } 273 return StrTab.add(S); 274 } 275 276 void GsymCreator::addFunctionInfo(FunctionInfo &&FI) { 277 std::lock_guard<std::recursive_mutex> Guard(Mutex); 278 Ranges.insert(FI.Range); 279 Funcs.emplace_back(FI); 280 } 281 282 void GsymCreator::forEachFunctionInfo( 283 std::function<bool(FunctionInfo &)> const &Callback) { 284 std::lock_guard<std::recursive_mutex> Guard(Mutex); 285 for (auto &FI : Funcs) { 286 if (!Callback(FI)) 287 break; 288 } 289 } 290 291 void GsymCreator::forEachFunctionInfo( 292 std::function<bool(const FunctionInfo &)> const &Callback) const { 293 std::lock_guard<std::recursive_mutex> Guard(Mutex); 294 for (const auto &FI : Funcs) { 295 if (!Callback(FI)) 296 break; 297 } 298 } 299 300 size_t GsymCreator::getNumFunctionInfos() const{ 301 std::lock_guard<std::recursive_mutex> Guard(Mutex); 302 return Funcs.size(); 303 } 304 305 bool GsymCreator::IsValidTextAddress(uint64_t Addr) const { 306 if (ValidTextRanges) 307 return ValidTextRanges->contains(Addr); 308 return true; // No valid text ranges has been set, so accept all ranges. 309 } 310 311 bool GsymCreator::hasFunctionInfoForAddress(uint64_t Addr) const { 312 std::lock_guard<std::recursive_mutex> Guard(Mutex); 313 return Ranges.contains(Addr); 314 } 315