@@ -341,16 +341,15 @@ void line_header::read(freader& s, bool needs_byteswap) {
341
341
}
342
342
343
343
/* *************************************************************************************************/
344
-
345
- std::size_t fatal_attribute_hash (const attribute_sequence& attributes) {
346
- #if ORC_FEATURE(PROFILE_DIE_DETAILS)
347
- ZoneScoped;
348
- #endif // ORC_FEATURE(PROFILE_DIE_DETAILS)
349
-
350
- // We only hash the attributes that could contribute to an ODRV. We also sort that set of
351
- // attributes by name to make sure the hashing is consistent regardless of attribute order.
352
- constexpr const std::size_t max_names_k{32 };
353
- std::array<dw::at, max_names_k> names{dw::at::none};
344
+ // It is fixed to keep allocations from happening.
345
+ constexpr std::size_t max_names_k{32 };
346
+ using fixed_attribute_array = std::array<dw::at, max_names_k>;
347
+
348
+ // for an incoming set of arbitrary attributes, return the subset of those that are fatal.
349
+ // We also sort the set of attributes by name to make sure subsequent traversal of this set
350
+ // is consistent.
351
+ fixed_attribute_array fatal_attributes_within (const attribute_sequence& attributes) {
352
+ fixed_attribute_array names{dw::at::none};
354
353
std::size_t count{0 };
355
354
356
355
for (const auto & attr : attributes) {
@@ -362,17 +361,32 @@ std::size_t fatal_attribute_hash(const attribute_sequence& attributes) {
362
361
}
363
362
std::sort (&names[0 ], &names[count]);
364
363
364
+ return names;
365
+ }
366
+
367
+ /* *************************************************************************************************/
368
+
369
+ std::size_t fatal_attribute_hash (const attribute_sequence& attributes) {
370
+ #if ORC_FEATURE(PROFILE_DIE_DETAILS)
371
+ ZoneScoped;
372
+ #endif // ORC_FEATURE(PROFILE_DIE_DETAILS)
373
+
374
+ // We only hash the attributes that could contribute to an ODRV. That's what makes them "fatal"
375
+
365
376
std::size_t h{0 };
366
377
367
- for (std::size_t i{0 }; i < count; ++i) {
378
+ for (auto name : fatal_attributes_within (attributes)) {
379
+ // As soon as we hit our first no-name-attribute, we're done.
380
+ if (name == dw::at::none) break ;
381
+
368
382
// If this assert fires, it means an attribute's value was passed over during evaluation,
369
383
// but it was necessary for ODRV evaluation after all. The fix is to improve the attribute
370
384
// form evaluation engine such that this attribute's value is no longer passed over.
371
- const auto name = names[i];
372
385
const auto & attribute = attributes.get (name);
373
386
assert (!attributes.has (name, attribute_value::type::passover));
374
- const auto attribute_hash = attribute._value .hash ();
375
- h = orc::hash_combine (h, attribute_hash);
387
+
388
+ const auto hash = attribute._value .hash ();
389
+ h = orc::hash_combine (h, hash);
376
390
}
377
391
378
392
return h;
@@ -474,6 +488,7 @@ struct dwarf::implementation {
474
488
std::vector<pool_string> _decl_files;
475
489
std::unordered_map<std::size_t , pool_string> _type_cache;
476
490
std::unordered_map<std::size_t , pool_string> _debug_str_cache;
491
+ pool_string _last_typedef_name; // for unnamed structs - see https://github.com/adobe/orc/issues/84
477
492
cu_header _cu_header;
478
493
std::size_t _cu_header_offset{0 }; // offset of the compilation unit header. Relative to __debug_info.
479
494
std::size_t _cu_die_offset{0 }; // offset of the `compile_unit` die. Relative to start of `debug_info`
@@ -1380,6 +1395,18 @@ pool_string dwarf::implementation::resolve_type(attribute type) {
1380
1395
result = empool (" const " + recurse (attributes).allocate_string ());
1381
1396
} else if (die._tag == dw::tag::pointer_type) {
1382
1397
result = empool (recurse (attributes).allocate_string () + " *" );
1398
+ } else if (die._tag == dw::tag::typedef_) {
1399
+ if (const auto maybe_type = recurse (attributes)) {
1400
+ result = maybe_type;
1401
+ } else if (attributes.has_string (dw::at::name)) {
1402
+ result = attributes.string (dw::at::name);
1403
+ } else {
1404
+ // `result` will be empty if we hit this in release
1405
+ // builds. It's bad, but not UB, so the program can
1406
+ // continue from here (though the results will be
1407
+ // untrustworthy).
1408
+ assert (!" Got a typedef with no name?" );
1409
+ }
1383
1410
} else if (attributes.has_string (dw::at::type)) {
1384
1411
result = type.string ();
1385
1412
} else if (attributes.has_reference (dw::at::type)) {
@@ -1416,6 +1443,7 @@ die_pair dwarf::implementation::abbreviation_to_die(std::size_t die_address, pro
1416
1443
die._tag = a._tag ;
1417
1444
die._has_children = a._has_children ;
1418
1445
1446
+ // Can we get rid of this memory allocation? This happens a lot...
1419
1447
attributes.reserve (a._attributes .size ());
1420
1448
1421
1449
std::transform (a._attributes .begin (), a._attributes .end (), std::back_inserter (attributes),
@@ -1425,8 +1453,9 @@ die_pair dwarf::implementation::abbreviation_to_die(std::size_t die_address, pro
1425
1453
});
1426
1454
1427
1455
if (mode == process_mode::complete) {
1456
+ // These statements must be kept in sync with the ones dealing with issue 84 below.
1457
+ // See https://github.com/adobe/orc/issues/84
1428
1458
path_identifier_set (die_identifier (die, attributes));
1429
-
1430
1459
die._path = empool (std::string_view (qualified_symbol_name (die, attributes)));
1431
1460
}
1432
1461
@@ -1507,6 +1536,16 @@ bool dwarf::implementation::skip_die(die& d, const attribute_sequence& attribute
1507
1536
return true ;
1508
1537
}
1509
1538
1539
+ // There are some symbols that are owned by the compiler and/or OS vendor (read: Apple)
1540
+ // that have been observed to conflict. We skip them for our purposes, as we have no
1541
+ // control over them.
1542
+ if (d._path .view ().find (" ::[u]::objc_object" ) == 0 ) {
1543
+ #if ORC_FEATURE(PROFILE_DIE_DETAILS)
1544
+ ZoneTextL (" skipping: vendor- or compiler-defined symbol" );
1545
+ #endif // ORC_FEATURE(PROFILE_DIE_DETAILS)
1546
+ return true ;
1547
+ }
1548
+
1510
1549
// lambdas are ephemeral and can't cause (hopefully) an ODRV
1511
1550
if (d._path .view ().find (" lambda" ) != std::string::npos) {
1512
1551
#if ORC_FEATURE(PROFILE_DIE_DETAILS)
@@ -1652,12 +1691,37 @@ void dwarf::implementation::process_all_dies() {
1652
1691
}
1653
1692
}
1654
1693
1694
+ post_process_die_attributes (attributes);
1695
+
1696
+ // See https://github.com/adobe/orc/issues/84
1697
+ // This code accounts for unnamed structs that are part of
1698
+ // a typedef expression. In such case the typedef actually adds a name
1699
+ // for the purpose of linking, but the structure's
1700
+ // die does not contain the name. Furthermore, the typedef die has
1701
+ // been found to precede the die for the structure. So we grab it if
1702
+ // we find a typedef, and use it if an ensuing structure_type has no name.
1703
+ if (die._tag == dw::tag::typedef_ && attributes.has (dw::at::name)) {
1704
+ _last_typedef_name = attributes.get (dw::at::name).string ();
1705
+ } else if (die._tag == dw::tag::structure_type &&
1706
+ !attributes.has (dw::at::name) &&
1707
+ _last_typedef_name) {
1708
+ attribute name;
1709
+ name._name = dw::at::name;
1710
+ name._form = dw::form::strp;
1711
+ name._value .string (_last_typedef_name);
1712
+ attributes.push_back (name);
1713
+
1714
+ // These two statements must be in sync with the ones in `abbreviation_to_die`
1715
+ path_identifier_set (die_identifier (die, attributes));
1716
+ die._path = empool (std::string_view (qualified_symbol_name (die, attributes)));
1717
+
1718
+ _last_typedef_name = pool_string (); // reset it to avoid misuse
1719
+ }
1720
+
1655
1721
if (die._has_children ) {
1656
1722
path_identifier_push ();
1657
1723
}
1658
1724
1659
- post_process_die_attributes (attributes);
1660
-
1661
1725
die._skippable = skip_die (die, attributes);
1662
1726
die._ofd_index = _ofd_index;
1663
1727
die._hash = die_hash (die, attributes);
0 commit comments