]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
1 | //===- tools/dsymutil/MachODebugMapParser.cpp - Parse STABS debug maps ----===// |
2 | // | |
3 | // The LLVM Linker | |
4 | // | |
5 | // This file is distributed under the University of Illinois Open Source | |
6 | // License. See LICENSE.TXT for details. | |
7 | // | |
8 | //===----------------------------------------------------------------------===// | |
9 | ||
10 | #include "BinaryHolder.h" | |
11 | #include "DebugMap.h" | |
12 | #include "dsymutil.h" | |
13 | #include "llvm/Object/MachO.h" | |
14 | #include "llvm/Support/Path.h" | |
15 | #include "llvm/Support/raw_ostream.h" | |
16 | ||
17 | namespace { | |
18 | using namespace llvm; | |
19 | using namespace llvm::dsymutil; | |
20 | using namespace llvm::object; | |
21 | ||
22 | class MachODebugMapParser { | |
23 | public: | |
24 | MachODebugMapParser(StringRef BinaryPath, StringRef PathPrefix = "", | |
25 | bool Verbose = false) | |
26 | : BinaryPath(BinaryPath), PathPrefix(PathPrefix), | |
27 | MainBinaryHolder(Verbose), CurrentObjectHolder(Verbose), | |
28 | CurrentDebugMapObject(nullptr) {} | |
29 | ||
30 | /// \brief Parses and returns the DebugMap of the input binary. | |
31 | /// \returns an error in case the provided BinaryPath doesn't exist | |
32 | /// or isn't of a supported type. | |
33 | ErrorOr<std::unique_ptr<DebugMap>> parse(); | |
34 | ||
35 | private: | |
36 | std::string BinaryPath; | |
37 | std::string PathPrefix; | |
38 | ||
39 | /// Owns the MemoryBuffer for the main binary. | |
40 | BinaryHolder MainBinaryHolder; | |
41 | /// Map of the binary symbol addresses. | |
42 | StringMap<uint64_t> MainBinarySymbolAddresses; | |
43 | StringRef MainBinaryStrings; | |
44 | /// The constructed DebugMap. | |
45 | std::unique_ptr<DebugMap> Result; | |
46 | ||
47 | /// Owns the MemoryBuffer for the currently handled object file. | |
48 | BinaryHolder CurrentObjectHolder; | |
49 | /// Map of the currently processed object file symbol addresses. | |
50 | StringMap<uint64_t> CurrentObjectAddresses; | |
51 | /// Element of the debug map corresponfing to the current object file. | |
52 | DebugMapObject *CurrentDebugMapObject; | |
53 | ||
54 | void switchToNewDebugMapObject(StringRef Filename); | |
55 | void resetParserState(); | |
56 | uint64_t getMainBinarySymbolAddress(StringRef Name); | |
57 | void loadMainBinarySymbols(); | |
58 | void loadCurrentObjectFileSymbols(); | |
59 | void handleStabSymbolTableEntry(uint32_t StringIndex, uint8_t Type, | |
60 | uint8_t SectionIndex, uint16_t Flags, | |
61 | uint64_t Value); | |
62 | ||
63 | template <typename STEType> void handleStabDebugMapEntry(const STEType &STE) { | |
64 | handleStabSymbolTableEntry(STE.n_strx, STE.n_type, STE.n_sect, STE.n_desc, | |
65 | STE.n_value); | |
66 | } | |
67 | }; | |
68 | ||
69 | static void Warning(const Twine &Msg) { errs() << "warning: " + Msg + "\n"; } | |
70 | } | |
71 | ||
72 | /// Reset the parser state coresponding to the current object | |
73 | /// file. This is to be called after an object file is finished | |
74 | /// processing. | |
75 | void MachODebugMapParser::resetParserState() { | |
76 | CurrentObjectAddresses.clear(); | |
77 | CurrentDebugMapObject = nullptr; | |
78 | } | |
79 | ||
80 | /// Create a new DebugMapObject. This function resets the state of the | |
81 | /// parser that was referring to the last object file and sets | |
82 | /// everything up to add symbols to the new one. | |
83 | void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename) { | |
84 | resetParserState(); | |
85 | ||
86 | SmallString<80> Path(PathPrefix); | |
87 | sys::path::append(Path, Filename); | |
88 | ||
89 | auto MachOOrError = CurrentObjectHolder.GetFileAs<MachOObjectFile>(Path); | |
90 | if (auto Error = MachOOrError.getError()) { | |
91 | Warning(Twine("cannot open debug object \"") + Path.str() + "\": " + | |
92 | Error.message() + "\n"); | |
93 | return; | |
94 | } | |
95 | ||
96 | loadCurrentObjectFileSymbols(); | |
97 | CurrentDebugMapObject = &Result->addDebugMapObject(Path); | |
98 | } | |
99 | ||
100 | /// This main parsing routine tries to open the main binary and if | |
101 | /// successful iterates over the STAB entries. The real parsing is | |
102 | /// done in handleStabSymbolTableEntry. | |
103 | ErrorOr<std::unique_ptr<DebugMap>> MachODebugMapParser::parse() { | |
104 | auto MainBinOrError = MainBinaryHolder.GetFileAs<MachOObjectFile>(BinaryPath); | |
105 | if (auto Error = MainBinOrError.getError()) | |
106 | return Error; | |
107 | ||
108 | const MachOObjectFile &MainBinary = *MainBinOrError; | |
109 | loadMainBinarySymbols(); | |
110 | Result = make_unique<DebugMap>(); | |
111 | MainBinaryStrings = MainBinary.getStringTableData(); | |
112 | for (const SymbolRef &Symbol : MainBinary.symbols()) { | |
113 | const DataRefImpl &DRI = Symbol.getRawDataRefImpl(); | |
114 | if (MainBinary.is64Bit()) | |
115 | handleStabDebugMapEntry(MainBinary.getSymbol64TableEntry(DRI)); | |
116 | else | |
117 | handleStabDebugMapEntry(MainBinary.getSymbolTableEntry(DRI)); | |
118 | } | |
119 | ||
120 | resetParserState(); | |
121 | return std::move(Result); | |
122 | } | |
123 | ||
124 | /// Interpret the STAB entries to fill the DebugMap. | |
125 | void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex, | |
126 | uint8_t Type, | |
127 | uint8_t SectionIndex, | |
128 | uint16_t Flags, | |
129 | uint64_t Value) { | |
130 | if (!(Type & MachO::N_STAB)) | |
131 | return; | |
132 | ||
133 | const char *Name = &MainBinaryStrings.data()[StringIndex]; | |
134 | ||
135 | // An N_OSO entry represents the start of a new object file description. | |
136 | if (Type == MachO::N_OSO) | |
137 | return switchToNewDebugMapObject(Name); | |
138 | ||
139 | // If the last N_OSO object file wasn't found, | |
140 | // CurrentDebugMapObject will be null. Do not update anything | |
141 | // until we find the next valid N_OSO entry. | |
142 | if (!CurrentDebugMapObject) | |
143 | return; | |
144 | ||
145 | switch (Type) { | |
146 | case MachO::N_GSYM: | |
147 | // This is a global variable. We need to query the main binary | |
148 | // symbol table to find its address as it might not be in the | |
149 | // debug map (for common symbols). | |
150 | Value = getMainBinarySymbolAddress(Name); | |
151 | if (Value == UnknownAddressOrSize) | |
152 | return; | |
153 | break; | |
154 | case MachO::N_FUN: | |
155 | // Functions are scopes in STABS. They have an end marker that we | |
156 | // need to ignore. | |
157 | if (Name[0] == '\0') | |
158 | return; | |
159 | break; | |
160 | case MachO::N_STSYM: | |
161 | break; | |
162 | default: | |
163 | return; | |
164 | } | |
165 | ||
166 | auto ObjectSymIt = CurrentObjectAddresses.find(Name); | |
167 | if (ObjectSymIt == CurrentObjectAddresses.end()) | |
168 | return Warning("could not find object file symbol for symbol " + | |
169 | Twine(Name)); | |
170 | if (!CurrentDebugMapObject->addSymbol(Name, ObjectSymIt->getValue(), Value)) | |
171 | return Warning(Twine("failed to insert symbol '") + Name + | |
172 | "' in the debug map."); | |
173 | } | |
174 | ||
175 | /// Load the current object file symbols into CurrentObjectAddresses. | |
176 | void MachODebugMapParser::loadCurrentObjectFileSymbols() { | |
177 | CurrentObjectAddresses.clear(); | |
178 | ||
179 | for (auto Sym : CurrentObjectHolder.Get().symbols()) { | |
180 | StringRef Name; | |
181 | uint64_t Addr; | |
182 | if (Sym.getAddress(Addr) || Addr == UnknownAddressOrSize || | |
183 | Sym.getName(Name)) | |
184 | continue; | |
185 | CurrentObjectAddresses[Name] = Addr; | |
186 | } | |
187 | } | |
188 | ||
189 | /// Lookup a symbol address in the main binary symbol table. The | |
190 | /// parser only needs to query common symbols, thus not every symbol's | |
191 | /// address is available through this function. | |
192 | uint64_t MachODebugMapParser::getMainBinarySymbolAddress(StringRef Name) { | |
193 | auto Sym = MainBinarySymbolAddresses.find(Name); | |
194 | if (Sym == MainBinarySymbolAddresses.end()) | |
195 | return UnknownAddressOrSize; | |
196 | return Sym->second; | |
197 | } | |
198 | ||
199 | /// Load the interesting main binary symbols' addresses into | |
200 | /// MainBinarySymbolAddresses. | |
201 | void MachODebugMapParser::loadMainBinarySymbols() { | |
202 | const MachOObjectFile &MainBinary = MainBinaryHolder.GetAs<MachOObjectFile>(); | |
203 | section_iterator Section = MainBinary.section_end(); | |
204 | for (const auto &Sym : MainBinary.symbols()) { | |
205 | SymbolRef::Type Type; | |
206 | // Skip undefined and STAB entries. | |
207 | if (Sym.getType(Type) || (Type & SymbolRef::ST_Debug) || | |
208 | (Type & SymbolRef::ST_Unknown)) | |
209 | continue; | |
210 | StringRef Name; | |
211 | uint64_t Addr; | |
212 | // The only symbols of interest are the global variables. These | |
213 | // are the only ones that need to be queried because the address | |
214 | // of common data won't be described in the debug map. All other | |
215 | // addresses should be fetched for the debug map. | |
216 | if (Sym.getAddress(Addr) || Addr == UnknownAddressOrSize || | |
217 | !(Sym.getFlags() & SymbolRef::SF_Global) || Sym.getSection(Section) || | |
218 | Section->isText() || Sym.getName(Name) || Name.size() == 0 || | |
219 | Name[0] == '\0') | |
220 | continue; | |
221 | MainBinarySymbolAddresses[Name] = Addr; | |
222 | } | |
223 | } | |
224 | ||
225 | namespace llvm { | |
226 | namespace dsymutil { | |
227 | llvm::ErrorOr<std::unique_ptr<DebugMap>> parseDebugMap(StringRef InputFile, | |
228 | StringRef PrependPath, | |
229 | bool Verbose) { | |
230 | MachODebugMapParser Parser(InputFile, PrependPath, Verbose); | |
231 | return Parser.parse(); | |
232 | } | |
233 | } | |
234 | } |