9
9
#include " BinaryHolder.h"
10
10
#include " DebugMap.h"
11
11
#include " MachOUtils.h"
12
- #include " llvm/ADT/Optional .h"
12
+ #include " llvm/ADT/DenseSet .h"
13
13
#include " llvm/ADT/SmallSet.h"
14
14
#include " llvm/Object/MachO.h"
15
+ #include " llvm/Support/Chrono.h"
15
16
#include " llvm/Support/Path.h"
16
17
#include " llvm/Support/WithColor.h"
17
18
#include " llvm/Support/raw_ostream.h"
@@ -31,7 +32,7 @@ class MachODebugMapParser {
31
32
: BinaryPath(std::string(BinaryPath)), Archs(Archs.begin(), Archs.end()),
32
33
PathPrefix (std::string(PathPrefix)),
33
34
PaperTrailWarnings(PaperTrailWarnings), BinHolder(VFS, Verbose),
34
- CurrentDebugMapObject(nullptr ) {}
35
+ CurrentDebugMapObject(nullptr ), SkipDebugMapObject( false ) {}
35
36
36
37
// / Parses and returns the DebugMaps of the input binary. The binary contains
37
38
// / multiple maps in case it is a universal binary.
@@ -42,6 +43,8 @@ class MachODebugMapParser {
42
43
// / Walk the symbol table and dump it.
43
44
bool dumpStab ();
44
45
46
+ using OSO = std::pair<llvm::StringRef, uint64_t >;
47
+
45
48
private:
46
49
std::string BinaryPath;
47
50
SmallVector<StringRef, 1 > Archs;
@@ -70,12 +73,18 @@ class MachODebugMapParser {
70
73
// / Element of the debug map corresponding to the current object file.
71
74
DebugMapObject *CurrentDebugMapObject;
72
75
76
+ // / Whether we need to skip the current debug map object.
77
+ bool SkipDebugMapObject;
78
+
73
79
// / Holds function info while function scope processing.
74
80
const char *CurrentFunctionName;
75
81
uint64_t CurrentFunctionAddress;
76
82
77
83
std::unique_ptr<DebugMap> parseOneBinary (const MachOObjectFile &MainBinary,
78
84
StringRef BinaryPath);
85
+ void handleStabDebugMap (
86
+ const MachOObjectFile &MainBinary,
87
+ std::function<void (uint32_t , uint8_t , uint8_t , uint16_t , uint64_t )> F);
79
88
80
89
void
81
90
switchToNewDebugMapObject (StringRef Filename,
@@ -85,13 +94,21 @@ class MachODebugMapParser {
85
94
std::vector<StringRef> getMainBinarySymbolNames (uint64_t Value);
86
95
void loadMainBinarySymbols (const MachOObjectFile &MainBinary);
87
96
void loadCurrentObjectFileSymbols (const object::MachOObjectFile &Obj);
97
+
98
+ void handleStabOSOEntry (uint32_t StringIndex, uint8_t Type,
99
+ uint8_t SectionIndex, uint16_t Flags, uint64_t Value,
100
+ llvm::DenseSet<OSO> &OSOs,
101
+ llvm::SmallSet<OSO, 4 > &Duplicates);
88
102
void handleStabSymbolTableEntry (uint32_t StringIndex, uint8_t Type,
89
103
uint8_t SectionIndex, uint16_t Flags,
90
- uint64_t Value);
104
+ uint64_t Value,
105
+ const llvm::SmallSet<OSO, 4 > &Duplicates);
91
106
92
- template <typename STEType> void handleStabDebugMapEntry (const STEType &STE) {
93
- handleStabSymbolTableEntry (STE.n_strx , STE.n_type , STE.n_sect , STE.n_desc ,
94
- STE.n_value );
107
+ template <typename STEType>
108
+ void handleStabDebugMapEntry (
109
+ const STEType &STE,
110
+ std::function<void (uint32_t , uint8_t , uint8_t , uint16_t , uint64_t )> F) {
111
+ F (STE.n_strx , STE.n_type , STE.n_sect , STE.n_desc , STE.n_value );
95
112
}
96
113
97
114
void addCommonSymbols ();
@@ -140,6 +157,7 @@ void MachODebugMapParser::resetParserState() {
140
157
CurrentObjectAliasMap.clear ();
141
158
SeenAliasValues.clear ();
142
159
CurrentDebugMapObject = nullptr ;
160
+ SkipDebugMapObject = false ;
143
161
}
144
162
145
163
// / Commons symbols won't show up in the symbol map but might need to be
@@ -198,21 +216,60 @@ static std::string getArchName(const object::MachOObjectFile &Obj) {
198
216
return std::string (T.getArchName ());
199
217
}
200
218
219
+ void MachODebugMapParser::handleStabDebugMap (
220
+ const MachOObjectFile &MainBinary,
221
+ std::function<void (uint32_t , uint8_t , uint8_t , uint16_t , uint64_t )> F) {
222
+ for (const SymbolRef &Symbol : MainBinary.symbols ()) {
223
+ const DataRefImpl &DRI = Symbol.getRawDataRefImpl ();
224
+ if (MainBinary.is64Bit ())
225
+ handleStabDebugMapEntry (MainBinary.getSymbol64TableEntry (DRI), F);
226
+ else
227
+ handleStabDebugMapEntry (MainBinary.getSymbolTableEntry (DRI), F);
228
+ }
229
+ }
230
+
201
231
std::unique_ptr<DebugMap>
202
232
MachODebugMapParser::parseOneBinary (const MachOObjectFile &MainBinary,
203
233
StringRef BinaryPath) {
204
234
Result = std::make_unique<DebugMap>(MainBinary.getArchTriple (), BinaryPath,
205
235
MainBinary.getUuid ());
206
236
loadMainBinarySymbols (MainBinary);
207
237
MainBinaryStrings = MainBinary.getStringTableData ();
208
- for (const SymbolRef &Symbol : MainBinary.symbols ()) {
209
- const DataRefImpl &DRI = Symbol.getRawDataRefImpl ();
210
- if (MainBinary.is64Bit ())
211
- handleStabDebugMapEntry (MainBinary.getSymbol64TableEntry (DRI));
212
- else
213
- handleStabDebugMapEntry (MainBinary.getSymbolTableEntry (DRI));
238
+
239
+ // Static archives can contain multiple object files with identical names, in
240
+ // which case the timestamp is used to disambiguate. However, if both are
241
+ // identical, there's no way to tell them apart. Detect this and skip
242
+ // duplicate debug map objects.
243
+ llvm::DenseSet<OSO> OSOs;
244
+ llvm::SmallSet<OSO, 4 > Duplicates;
245
+
246
+ // Iterate over all the STABS to find duplicate OSO entries.
247
+ handleStabDebugMap (MainBinary,
248
+ [&](uint32_t StringIndex, uint8_t Type,
249
+ uint8_t SectionIndex, uint16_t Flags, uint64_t Value) {
250
+ handleStabOSOEntry (StringIndex, Type, SectionIndex,
251
+ Flags, Value, OSOs, Duplicates);
252
+ });
253
+
254
+ // Print an informative warning with the duplicate object file name and time
255
+ // stamp.
256
+ for (const auto &OSO : Duplicates) {
257
+ std::string Buffer;
258
+ llvm::raw_string_ostream OS (Buffer);
259
+ OS << sys::TimePoint<std::chrono::seconds>(sys::toTimePoint (OSO.second ));
260
+ Warning (" skipping debug map object with duplicate name and timestamp: " +
261
+ OS.str () + Twine (" " ) + Twine (OSO.first ));
214
262
}
215
263
264
+ // Build the debug map by iterating over the STABS again but ignore the
265
+ // duplicate debug objects.
266
+ handleStabDebugMap (MainBinary, [&](uint32_t StringIndex, uint8_t Type,
267
+ uint8_t SectionIndex, uint16_t Flags,
268
+ uint64_t Value) {
269
+ handleStabSymbolTableEntry (StringIndex, Type, SectionIndex, Flags, Value,
270
+ Duplicates);
271
+ });
272
+
216
273
resetParserState ();
217
274
return std::move (Result);
218
275
}
@@ -407,20 +464,38 @@ ErrorOr<std::vector<std::unique_ptr<DebugMap>>> MachODebugMapParser::parse() {
407
464
return std::move (Results);
408
465
}
409
466
467
+ void MachODebugMapParser::handleStabOSOEntry (
468
+ uint32_t StringIndex, uint8_t Type, uint8_t SectionIndex, uint16_t Flags,
469
+ uint64_t Value, llvm::DenseSet<OSO> &OSOs,
470
+ llvm::SmallSet<OSO, 4 > &Duplicates) {
471
+ if (Type != MachO::N_OSO)
472
+ return ;
473
+
474
+ OSO O (&MainBinaryStrings.data ()[StringIndex], Value);
475
+ if (!OSOs.insert (O).second )
476
+ Duplicates.insert (O);
477
+ }
478
+
410
479
// / Interpret the STAB entries to fill the DebugMap.
411
- void MachODebugMapParser::handleStabSymbolTableEntry (uint32_t StringIndex,
412
- uint8_t Type,
413
- uint8_t SectionIndex,
414
- uint16_t Flags,
415
- uint64_t Value) {
480
+ void MachODebugMapParser::handleStabSymbolTableEntry (
481
+ uint32_t StringIndex, uint8_t Type, uint8_t SectionIndex, uint16_t Flags,
482
+ uint64_t Value, const llvm::SmallSet<OSO, 4 > &Duplicates) {
416
483
if (!(Type & MachO::N_STAB))
417
484
return ;
418
485
419
486
const char *Name = &MainBinaryStrings.data ()[StringIndex];
420
487
421
488
// An N_OSO entry represents the start of a new object file description.
422
- if (Type == MachO::N_OSO)
489
+ if (Type == MachO::N_OSO) {
490
+ if (Duplicates.count (OSO (Name, Value))) {
491
+ SkipDebugMapObject = true ;
492
+ return ;
493
+ }
423
494
return switchToNewDebugMapObject (Name, sys::toTimePoint (Value));
495
+ }
496
+
497
+ if (SkipDebugMapObject)
498
+ return ;
424
499
425
500
if (Type == MachO::N_AST) {
426
501
SmallString<80 > Path (PathPrefix);
0 commit comments