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