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 "MachODebugMapParser.h"
11 #include "llvm/Support/Path.h"
12 #include "llvm/Support/raw_ostream.h"
14 using namespace llvm::object;
18 static void Warning(const Twine &Msg) { errs() << "warning: " + Msg + "\n"; }
20 static ErrorOr<OwningBinary<MachOObjectFile>> createMachOBinary(StringRef file) {
21 ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(file);
22 if (BinaryOrErr.getError())
23 return BinaryOrErr.getError();
25 std::unique_ptr<Binary> Bin;
26 std::unique_ptr<MemoryBuffer> Buf;
27 std::tie(Bin, Buf) = BinaryOrErr->takeBinary();
28 if (!isa<MachOObjectFile>(Bin.get()))
29 return make_error_code(object_error::invalid_file_type);
31 std::unique_ptr<MachOObjectFile> MachOFile(cast<MachOObjectFile>(Bin.release()));
32 return OwningBinary<MachOObjectFile>(std::move(MachOFile), std::move(Buf));
35 /// Reset the parser state coresponding to the current object
36 /// file. This is to be called after an object file is finished
38 void MachODebugMapParser::resetParserState() {
39 CurrentObjectFile = OwningBinary<object::MachOObjectFile>();
40 CurrentObjectAddresses.clear();
41 CurrentDebugMapObject = nullptr;
44 /// Create a new DebugMapObject. This function resets the state of the
45 /// parser that was referring to the last object file and sets
46 /// everything up to add symbols to the new one.
47 void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename) {
50 std::string Path = Filename;
51 if (!PathPrefix.empty())
52 Path = PathPrefix + sys::path::get_separator().data() + Path;
54 auto MachOOrError = createMachOBinary(Path);
55 if (auto Error = MachOOrError.getError()) {
56 Warning(Twine("cannot open debug object \"") + Path + "\": "
57 + Error.message() + "\n");
61 CurrentObjectFile = std::move(*MachOOrError);
62 loadCurrentObjectFileSymbols();
63 CurrentDebugMapObject = &Result->addDebugMapObject(Path);
66 /// This main parsing routine tries to open the main binary and if
67 /// successful iterates over the STAB entries. The real parsing is
68 /// done in handleStabSymbolTableEntry.
69 ErrorOr<std::unique_ptr<DebugMap>> MachODebugMapParser::parse() {
70 auto MainBinaryOrError = createMachOBinary(BinaryPath);
71 if (MainBinaryOrError.getError())
72 return MainBinaryOrError.getError();
74 MainOwningBinary = std::move(*MainBinaryOrError);
75 Result = make_unique<DebugMap>();
76 const auto &MainBinary = *MainOwningBinary.getBinary();
77 for (const SymbolRef &Symbol : MainBinary.symbols()) {
78 const DataRefImpl &DRI = Symbol.getRawDataRefImpl();
79 if (MainBinary.is64Bit())
80 handleStabDebugMapEntry(MainBinary.getSymbol64TableEntry(DRI));
82 handleStabDebugMapEntry(MainBinary.getSymbolTableEntry(DRI));
86 return std::move(Result);
89 /// Interpret the STAB entries to fill the DebugMap.
90 void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex,
95 if (!(Type & MachO::N_STAB))
98 const MachOObjectFile &MachOBinary = *MainOwningBinary.getBinary();
99 const char *Name = &MachOBinary.getStringTableData().data()[StringIndex];
101 // An N_OSO entry represents the start of a new object file description.
102 if (Type == MachO::N_OSO)
103 return switchToNewDebugMapObject(Name);
105 // If the last N_OSO object file wasn't found,
106 // CurrentDebugMapObject will be null. Do not update anything
107 // until we find the next valid N_OSO entry.
108 if (!CurrentDebugMapObject)
113 // This is a global variable. We need to query the main binary
114 // symbol table to find its address as it might not be in the
115 // debug map (for common symbols).
116 Value = getMainBinarySymbolAddress(Name);
117 if (Value == UnknownAddressOrSize)
121 // Functions are scopes in STABS. They have an end marker that we
132 auto ObjectSymIt = CurrentObjectAddresses.find(Name);
133 if (ObjectSymIt == CurrentObjectAddresses.end())
134 return Warning("could not find object file symbol for symbol " +
136 if (!CurrentDebugMapObject->addSymbol(Name, ObjectSymIt->getValue(), Value))
137 return Warning(Twine("failed to insert symbol '") + Name + "' in the debug map.");
140 /// Load the current object file symbols into CurrentObjectAddresses.
141 void MachODebugMapParser::loadCurrentObjectFileSymbols() {
142 CurrentObjectAddresses.clear();
143 const auto &Binary = *CurrentObjectFile.getBinary();
145 for (auto Sym : Binary.symbols()) {
148 if (Sym.getAddress(Addr) || Addr == UnknownAddressOrSize ||
151 CurrentObjectAddresses[Name] = Addr;
155 /// Lookup a symbol address in the main binary symbol table. The
156 /// parser only needs to query common symbols, thus not every symbol's
157 /// address is available through this function.
158 uint64_t MachODebugMapParser::getMainBinarySymbolAddress(StringRef Name) {
159 if (MainBinarySymbolAddresses.empty())
160 loadMainBinarySymbols();
162 auto Sym = MainBinarySymbolAddresses.find(Name);
163 if (Sym == MainBinarySymbolAddresses.end())
164 return UnknownAddressOrSize;
168 /// Load the interesting main binary symbols' addresses into
169 /// MainBinarySymbolAddresses.
170 void MachODebugMapParser::loadMainBinarySymbols() {
171 const MachOObjectFile &Binary = *MainOwningBinary.getBinary();
172 section_iterator Section = Binary.section_end();
173 for (const auto &Sym : Binary.symbols()) {
174 SymbolRef::Type Type;
175 // Skip undefined and STAB entries.
176 if (Sym.getType(Type) || (Type & SymbolRef::ST_Debug) ||
177 (Type & SymbolRef::ST_Unknown))
181 // The only symbols of interest are the global variables. These
182 // are the only ones that need to be queried because the address
183 // of common data won't be described in the debug map. All other
184 // addresses should be fetched for the debug map.
185 if (Sym.getAddress(Addr) || Addr == UnknownAddressOrSize ||
186 !(Sym.getFlags() & SymbolRef::SF_Global) ||
187 Sym.getSection(Section) || Section->isText() || Sym.getName(Name) ||
188 Name.size() == 0 || Name[0] == '\0')
190 MainBinarySymbolAddresses[Name] = Addr;