|
1 | 1 | #include "objc.h"
|
2 | 2 | #include "inttypes.h"
|
| 3 | +#include <optional> |
3 | 4 |
|
4 | 5 | using namespace BinaryNinja;
|
5 | 6 |
|
| 7 | +namespace { |
| 8 | + |
| 9 | +// Attempt to recover an Objective-C class name from the symbol's name. |
| 10 | +// Note: classes defined in the current image should be looked up in m_classes |
| 11 | +// rather than using this function. |
| 12 | +std::optional<std::string> ClassNameFromSymbolName(const Ref<Symbol>& symbol) |
| 13 | +{ |
| 14 | + std::string_view symbolName = symbol->GetFullNameRef(); |
| 15 | + |
| 16 | + // Symbols named `_OBJC_CLASS_$_` are references to external classes. |
| 17 | + if (symbolName.size() > 14 && symbolName.rfind("_OBJC_CLASS_$_", 0) == 0) |
| 18 | + return std::string(symbolName.substr(14)); |
| 19 | + |
| 20 | + // Symbols named `cls_` are classes defined in a loaded image other than |
| 21 | + // the image currently being analyzed. |
| 22 | + if (symbolName.size() > 4 && symbolName.rfind("cls_", 0) == 0) |
| 23 | + return std::string(symbolName.substr(4)); |
| 24 | + |
| 25 | + return std::nullopt; |
| 26 | +} |
| 27 | + |
| 28 | +} // namespace |
| 29 | + |
| 30 | + |
6 | 31 | Ref<Metadata> ObjCProcessor::SerializeMethod(uint64_t loc, const Method& method)
|
7 | 32 | {
|
8 | 33 | std::map<std::string, Ref<Metadata>> methodMeta;
|
@@ -535,6 +560,39 @@ void ObjCProcessor::LoadClasses(ObjCReader* reader, Ref<Section> classPtrSection
|
535 | 560 | }
|
536 | 561 | }
|
537 | 562 |
|
| 563 | +std::optional<std::string> ObjCProcessor::ClassNameForTargetOfPointerAt(ObjCReader* reader, uint64_t offset) |
| 564 | +{ |
| 565 | + auto savedOffset = reader->GetOffset(); |
| 566 | + reader->Seek(offset); |
| 567 | + auto target = ReadPointerAccountingForRelocations(reader); |
| 568 | + reader->Seek(savedOffset); |
| 569 | + |
| 570 | + if (target) { |
| 571 | + // Classes defined in the current image must be looked up in m_classes |
| 572 | + // as adding their symbol may be deferred. |
| 573 | + if (auto it = m_classes.find(target); it != m_classes.end()) |
| 574 | + return it->second.name; |
| 575 | + |
| 576 | + // Classes defined in other images are looked up by their symbol name. |
| 577 | + // This is common for cross-image references in the shared cache. |
| 578 | + if (auto symbol = GetSymbol(target)) |
| 579 | + { |
| 580 | + if (auto className = ClassNameFromSymbolName(symbol)) |
| 581 | + return *className; |
| 582 | + } |
| 583 | + } |
| 584 | + |
| 585 | + // If there's no target, or we can't find a symbol for it, check whether the pointer has a relocation |
| 586 | + // that contains a symbol. This is the case for cross-image references outside of the shared cache. |
| 587 | + for (const auto& relocation : m_data->GetRelocationsAt(offset)) |
| 588 | + { |
| 589 | + if (auto symbol = relocation->GetSymbol()) |
| 590 | + return ClassNameFromSymbolName(symbol); |
| 591 | + } |
| 592 | + |
| 593 | + return std::nullopt; |
| 594 | +} |
| 595 | + |
538 | 596 | void ObjCProcessor::LoadCategories(ObjCReader* reader, Ref<Section> classPtrSection)
|
539 | 597 | {
|
540 | 598 | if (!classPtrSection)
|
@@ -574,29 +632,9 @@ void ObjCProcessor::LoadCategories(ObjCReader* reader, Ref<Section> classPtrSect
|
574 | 632 | }
|
575 | 633 |
|
576 | 634 | std::string categoryAdditionsName;
|
577 |
| - std::string categoryBaseClassName; |
| 635 | + std::string categoryBaseClassName = |
| 636 | + ClassNameForTargetOfPointerAt(reader, catLocation + ptrSize).value_or(std::string()); |
578 | 637 |
|
579 |
| - if (const auto& it = m_classes.find(cat.cls); it != m_classes.end()) |
580 |
| - { |
581 |
| - categoryBaseClassName = it->second.name; |
582 |
| - category.associatedName = it->second.associatedName; |
583 |
| - } |
584 |
| - else if (const auto symbol = GetSymbol(cat.cls)) |
585 |
| - { |
586 |
| - if (symbol->GetType() == ImportedDataSymbol || symbol->GetType() == ImportAddressSymbol |
587 |
| - || symbol->GetType() == DataSymbol || symbol->GetType() == ExternalSymbol) |
588 |
| - { |
589 |
| - // Symbols named `_OBJC_CLASS_$_` are references to external classes. |
590 |
| - // Symbols named `cls_` are classes defined in a loaded image other than |
591 |
| - // the image currently being analyzed. Classes from the current image |
592 |
| - // are found via `m_classes`. |
593 |
| - const std::string_view symbolName = symbol->GetFullNameRef(); |
594 |
| - if (symbolName.size() > 14 && symbolName.rfind("_OBJC_CLASS_$_", 0) == 0) |
595 |
| - categoryBaseClassName = symbolName.substr(14); |
596 |
| - else if (symbolName.size() > 4 && symbolName.rfind("cls_", 0) == 0) |
597 |
| - categoryBaseClassName = symbolName.substr(4); |
598 |
| - } |
599 |
| - } |
600 | 638 | if (categoryBaseClassName.empty())
|
601 | 639 | {
|
602 | 640 | m_logger->LogInfo("Using base address as stand-in classname for category at 0x%llx", catLocation);
|
@@ -1171,15 +1209,8 @@ void ObjCProcessor::PostProcessObjCSections(ObjCReader* reader)
|
1171 | 1209 | auto type = Type::PointerType(ptrSize, Type::NamedType(m_data, m_typeNames.cls));
|
1172 | 1210 | for (view_ptr_t i = start; i < end; i += ptrSize)
|
1173 | 1211 | {
|
1174 |
| - reader->Seek(i); |
1175 |
| - auto clsLoc = ReadPointerAccountingForRelocations(reader); |
1176 |
| - if (const auto& it = m_classes.find(clsLoc); it != m_classes.end()) |
1177 |
| - { |
1178 |
| - auto& cls = it->second; |
1179 |
| - std::string name = cls.name; |
1180 |
| - if (!name.empty()) |
1181 |
| - DefineObjCSymbol(DataSymbol, type, "clsRef_" + name, i, true); |
1182 |
| - } |
| 1212 | + if (auto className = ClassNameForTargetOfPointerAt(reader, i)) |
| 1213 | + DefineObjCSymbol(DataSymbol, type, "clsRef_" + *className, i, true); |
1183 | 1214 | }
|
1184 | 1215 | }
|
1185 | 1216 | if (auto superRefs = GetSectionWithName("__objc_superrefs"))
|
|
0 commit comments