1 //===- tools/dsymutil/MachODebugMapParser.cpp - Parse STABS debug maps ----===//
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "BinaryHolder.h"
13 #include "llvm/Object/MachO.h"
14 #include "llvm/Support/Path.h"
15 #include "llvm/Support/raw_ostream.h"
19 using namespace llvm::dsymutil;
20 using namespace llvm::object;
22 class MachODebugMapParser {
24 MachODebugMapParser(StringRef BinaryPath, StringRef PathPrefix = "",
26 : BinaryPath(BinaryPath), PathPrefix(PathPrefix),
27 MainBinaryHolder(Verbose), CurrentObjectHolder(Verbose),
28 CurrentDebugMapObject(nullptr) {}
30 /// \brief Parses and returns the DebugMaps of the input binary.
31 /// The binary contains multiple maps in case it is a universal
33 /// \returns an error in case the provided BinaryPath doesn't exist
34 /// or isn't of a supported type.
35 ErrorOr<std::vector<std::unique_ptr<DebugMap>>> parse();
38 std::string BinaryPath;
39 std::string PathPrefix;
41 /// Owns the MemoryBuffer for the main binary.
42 BinaryHolder MainBinaryHolder;
43 /// Map of the binary symbol addresses.
44 StringMap<uint64_t> MainBinarySymbolAddresses;
45 StringRef MainBinaryStrings;
46 /// The constructed DebugMap.
47 std::unique_ptr<DebugMap> Result;
49 /// Owns the MemoryBuffer for the currently handled object file.
50 BinaryHolder CurrentObjectHolder;
51 /// Map of the currently processed object file symbol addresses.
52 StringMap<uint64_t> CurrentObjectAddresses;
53 /// Element of the debug map corresponfing to the current object file.
54 DebugMapObject *CurrentDebugMapObject;
56 /// Holds function info while function scope processing.
57 const char *CurrentFunctionName;
58 uint64_t CurrentFunctionAddress;
60 std::unique_ptr<DebugMap> parseOneBinary(const MachOObjectFile &MainBinary,
61 StringRef BinaryPath);
63 void switchToNewDebugMapObject(StringRef Filename, sys::TimeValue Timestamp);
64 void resetParserState();
65 uint64_t getMainBinarySymbolAddress(StringRef Name);
66 void loadMainBinarySymbols(const MachOObjectFile &MainBinary);
67 void loadCurrentObjectFileSymbols(const object::MachOObjectFile &Obj);
68 void handleStabSymbolTableEntry(uint32_t StringIndex, uint8_t Type,
69 uint8_t SectionIndex, uint16_t Flags,
72 template <typename STEType> void handleStabDebugMapEntry(const STEType &STE) {
73 handleStabSymbolTableEntry(STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc,
78 static void Warning(const Twine &Msg) { errs() << "warning: " + Msg + "\n"; }
81 /// Reset the parser state coresponding to the current object
82 /// file. This is to be called after an object file is finished
84 void MachODebugMapParser::resetParserState() {
85 CurrentObjectAddresses.clear();
86 CurrentDebugMapObject = nullptr;
89 /// Create a new DebugMapObject. This function resets the state of the
90 /// parser that was referring to the last object file and sets
91 /// everything up to add symbols to the new one.
92 void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename,
93 sys::TimeValue Timestamp) {
96 SmallString<80> Path(PathPrefix);
97 sys::path::append(Path, Filename);
100 CurrentObjectHolder.GetFilesAs<MachOObjectFile>(Path, Timestamp);
101 if (auto Error = MachOOrError.getError()) {
102 Warning(Twine("cannot open debug object \"") + Path.str() + "\": " +
103 Error.message() + "\n");
108 CurrentObjectHolder.GetAs<MachOObjectFile>(Result->getTriple());
109 if (auto Err = ErrOrAchObj.getError()) {
110 return Warning(Twine("cannot open debug object \"") + Path.str() + "\": " +
111 Err.message() + "\n");
114 CurrentDebugMapObject = &Result->addDebugMapObject(Path, Timestamp);
115 loadCurrentObjectFileSymbols(*ErrOrAchObj);
118 std::unique_ptr<DebugMap>
119 MachODebugMapParser::parseOneBinary(const MachOObjectFile &MainBinary,
120 StringRef BinaryPath) {
121 loadMainBinarySymbols(MainBinary);
122 Result = make_unique<DebugMap>(BinaryHolder::getTriple(MainBinary));
123 MainBinaryStrings = MainBinary.getStringTableData();
124 for (const SymbolRef &Symbol : MainBinary.symbols()) {
125 const DataRefImpl &DRI = Symbol.getRawDataRefImpl();
126 if (MainBinary.is64Bit())
127 handleStabDebugMapEntry(MainBinary.getSymbol64TableEntry(DRI));
129 handleStabDebugMapEntry(MainBinary.getSymbolTableEntry(DRI));
133 return std::move(Result);
136 /// This main parsing routine tries to open the main binary and if
137 /// successful iterates over the STAB entries. The real parsing is
138 /// done in handleStabSymbolTableEntry.
139 ErrorOr<std::vector<std::unique_ptr<DebugMap>>> MachODebugMapParser::parse() {
140 auto MainBinOrError =
141 MainBinaryHolder.GetFilesAs<MachOObjectFile>(BinaryPath);
142 if (auto Error = MainBinOrError.getError())
145 std::vector<std::unique_ptr<DebugMap>> Results;
146 for (const auto *Binary : *MainBinOrError)
147 Results.push_back(parseOneBinary(*Binary, BinaryPath));
149 return std::move(Results);
152 /// Interpret the STAB entries to fill the DebugMap.
153 void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex,
155 uint8_t SectionIndex,
158 if (!(Type & MachO::N_STAB))
161 const char *Name = &MainBinaryStrings.data()[StringIndex];
163 // An N_OSO entry represents the start of a new object file description.
164 if (Type == MachO::N_OSO) {
165 sys::TimeValue Timestamp;
166 Timestamp.fromEpochTime(Value);
167 return switchToNewDebugMapObject(Name, Timestamp);
170 // If the last N_OSO object file wasn't found,
171 // CurrentDebugMapObject will be null. Do not update anything
172 // until we find the next valid N_OSO entry.
173 if (!CurrentDebugMapObject)
179 // This is a global variable. We need to query the main binary
180 // symbol table to find its address as it might not be in the
181 // debug map (for common symbols).
182 Value = getMainBinarySymbolAddress(Name);
185 // Functions are scopes in STABS. They have an end marker that
186 // contains the function size.
187 if (Name[0] == '\0') {
189 Value = CurrentFunctionAddress;
190 Name = CurrentFunctionName;
193 CurrentFunctionName = Name;
194 CurrentFunctionAddress = Value;
203 auto ObjectSymIt = CurrentObjectAddresses.find(Name);
204 if (ObjectSymIt == CurrentObjectAddresses.end())
205 return Warning("could not find object file symbol for symbol " +
207 if (!CurrentDebugMapObject->addSymbol(Name, ObjectSymIt->getValue(), Value,
209 return Warning(Twine("failed to insert symbol '") + Name +
210 "' in the debug map.");
213 /// Load the current object file symbols into CurrentObjectAddresses.
214 void MachODebugMapParser::loadCurrentObjectFileSymbols(
215 const object::MachOObjectFile &Obj) {
216 CurrentObjectAddresses.clear();
218 for (auto Sym : Obj.symbols()) {
219 uint64_t Addr = Sym.getValue();
220 ErrorOr<StringRef> Name = Sym.getName();
223 CurrentObjectAddresses[*Name] = Addr;
227 /// Lookup a symbol address in the main binary symbol table. The
228 /// parser only needs to query common symbols, thus not every symbol's
229 /// address is available through this function.
230 uint64_t MachODebugMapParser::getMainBinarySymbolAddress(StringRef Name) {
231 auto Sym = MainBinarySymbolAddresses.find(Name);
232 if (Sym == MainBinarySymbolAddresses.end())
237 /// Load the interesting main binary symbols' addresses into
238 /// MainBinarySymbolAddresses.
239 void MachODebugMapParser::loadMainBinarySymbols(
240 const MachOObjectFile &MainBinary) {
241 section_iterator Section = MainBinary.section_end();
242 MainBinarySymbolAddresses.clear();
243 for (const auto &Sym : MainBinary.symbols()) {
244 SymbolRef::Type Type = Sym.getType();
245 // Skip undefined and STAB entries.
246 if ((Type & SymbolRef::ST_Debug) || (Type & SymbolRef::ST_Unknown))
248 // The only symbols of interest are the global variables. These
249 // are the only ones that need to be queried because the address
250 // of common data won't be described in the debug map. All other
251 // addresses should be fetched for the debug map.
252 if (!(Sym.getFlags() & SymbolRef::SF_Global) || Sym.getSection(Section) ||
253 Section == MainBinary.section_end() || Section->isText())
255 uint64_t Addr = Sym.getValue();
256 ErrorOr<StringRef> NameOrErr = Sym.getName();
259 StringRef Name = *NameOrErr;
260 if (Name.size() == 0 || Name[0] == '\0')
262 MainBinarySymbolAddresses[Name] = Addr;
268 llvm::ErrorOr<std::vector<std::unique_ptr<DebugMap>>>
269 parseDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose,
272 MachODebugMapParser Parser(InputFile, PrependPath, Verbose);
273 return Parser.parse();
275 return DebugMap::parseYAMLDebugMap(InputFile, PrependPath, Verbose);