Initial dsymutil tool commit.
[oota-llvm.git] / tools / dsymutil / MachODebugMapParser.cpp
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 "MachODebugMapParser.h"
11 #include "llvm/Support/Path.h"
12 #include "llvm/Support/raw_ostream.h"
13
14 using namespace llvm::object;
15
16 namespace llvm {
17
18 static void Warning(const Twine &Msg) { errs() << "warning: " + Msg + "\n"; }
19
20 static ErrorOr<OwningBinary<MachOObjectFile>> createMachOBinary(StringRef file) {
21   ErrorOr<OwningBinary<Binary>> BinaryOrErr = createBinary(file);
22   if (BinaryOrErr.getError())
23     return BinaryOrErr.getError();
24
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);
30
31   std::unique_ptr<MachOObjectFile> MachOFile(cast<MachOObjectFile>(Bin.release()));
32   return OwningBinary<MachOObjectFile>(std::move(MachOFile), std::move(Buf));
33 }
34
35 /// Reset the parser state coresponding to the current object
36 /// file. This is to be called after an object file is finished
37 /// processing.
38 void MachODebugMapParser::resetParserState() {
39   CurrentObjectFile = OwningBinary<object::MachOObjectFile>();
40   CurrentObjectAddresses.clear();
41   CurrentDebugMapObject = nullptr;
42 }
43
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) {
48   resetParserState();
49
50   std::string Path = Filename;
51   if (!PathPrefix.empty())
52     Path = PathPrefix + sys::path::get_separator().data() + Path;
53
54   auto MachOOrError = createMachOBinary(Path);
55   if (auto Error = MachOOrError.getError()) {
56     Warning(Twine("cannot open debug object \"") + Path + "\": "
57             + Error.message() + "\n");
58     return;
59   }
60
61   CurrentObjectFile = std::move(*MachOOrError);
62   loadCurrentObjectFileSymbols();
63   CurrentDebugMapObject = &Result->addDebugMapObject(Path);
64 }
65
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();
73
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));
81     else
82       handleStabDebugMapEntry(MainBinary.getSymbolTableEntry(DRI));
83   }
84
85   resetParserState();
86   return std::move(Result);
87 }
88
89 /// Interpret the STAB entries to fill the DebugMap.
90 void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex,
91                                                      uint8_t Type,
92                                                      uint8_t SectionIndex,
93                                                      uint16_t Flags,
94                                                      uint64_t Value) {
95   if (!(Type & MachO::N_STAB))
96     return;
97
98   const MachOObjectFile &MachOBinary = *MainOwningBinary.getBinary();
99   const char *Name = &MachOBinary.getStringTableData().data()[StringIndex];
100
101   // An N_OSO entry represents the start of a new object file description.
102   if (Type == MachO::N_OSO)
103     return switchToNewDebugMapObject(Name);
104
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)
109     return;
110
111   switch (Type) {
112   case MachO::N_GSYM:
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)
118       return;
119     break;
120   case MachO::N_FUN:
121     // Functions are scopes in STABS. They have an end marker that we
122     // need to ignore.
123     if (Name[0] == '\0')
124       return;
125     break;
126   case MachO::N_STSYM:
127     break;
128   default:
129     return;
130   }
131
132   auto ObjectSymIt = CurrentObjectAddresses.find(Name);
133   if (ObjectSymIt == CurrentObjectAddresses.end())
134     return Warning("could not find object file symbol for symbol " +
135                    Twine(Name));
136   if (!CurrentDebugMapObject->addSymbol(Name, ObjectSymIt->getValue(), Value))
137     return Warning(Twine("failed to insert symbol '") + Name + "' in the debug map.");
138 }
139
140 /// Load the current object file symbols into CurrentObjectAddresses.
141 void MachODebugMapParser::loadCurrentObjectFileSymbols() {
142   CurrentObjectAddresses.clear();
143   const auto &Binary = *CurrentObjectFile.getBinary();
144
145   for (auto Sym : Binary.symbols()) {
146     StringRef Name;
147     uint64_t Addr;
148     if (Sym.getAddress(Addr) || Addr == UnknownAddressOrSize ||
149         Sym.getName(Name))
150       continue;
151     CurrentObjectAddresses[Name] = Addr;
152   }
153 }
154
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();
161
162   auto Sym = MainBinarySymbolAddresses.find(Name);
163   if (Sym == MainBinarySymbolAddresses.end())
164     return UnknownAddressOrSize;
165   return Sym->second;
166 }
167
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))
178       continue;
179     StringRef Name;
180     uint64_t Addr;
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')
189       continue;
190     MainBinarySymbolAddresses[Name] = Addr;
191   }
192 }
193
194 }