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