X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/a645023da60d22e86be13f7b4d97adeff8bc6665..e667b16e39593d19d0e25a8440a563eac3572653:/src/ld/passes/objc.cpp diff --git a/src/ld/passes/objc.cpp b/src/ld/passes/objc.cpp index f420b9f..2bbf54a 100644 --- a/src/ld/passes/objc.cpp +++ b/src/ld/passes/objc.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2010 Apple Inc. All rights reserved. + * Copyright (c) 2010-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -50,11 +50,11 @@ struct objc_image_info { uint32_t flags; }; -#define OBJC_IMAGE_IS_REPLACEMENT (1<<0) #define OBJC_IMAGE_SUPPORTS_GC (1<<1) #define OBJC_IMAGE_REQUIRES_GC (1<<2) #define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3) #define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4) +#define OBJC_IMAGE_IS_SIMULATED (1<<5) @@ -65,11 +65,9 @@ template class ObjCImageInfoAtom : public ld::Atom { public: ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, - bool compaction, bool objcReplacementClasses, bool abi2); + bool compaction, bool abi2, uint8_t swiftVersion); virtual const ld::File* file() const { return NULL; } - virtual bool translationUnitSource(const char** dir, const char**) const - { return false; } virtual const char* name() const { return "objc image info"; } virtual uint64_t size() const { return sizeof(objc_image_info); } virtual uint64_t objectAddress() const { return 0; } @@ -91,15 +89,13 @@ template ld::Section ObjCImageInfoAtom::_s_sectionABI2("__DATA", template ObjCImageInfoAtom::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, bool compaction, - bool objcReplacementClasses, bool abi2) + bool abi2, uint8_t swiftVersion) : ld::Atom(abi2 ? _s_sectionABI2 : _s_sectionABI1, ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified, symbolTableNotIn, false, false, false, ld::Atom::Alignment(2)) { uint32_t value = 0; - if ( objcReplacementClasses ) - value = OBJC_IMAGE_IS_REPLACEMENT; switch ( objcConstraint ) { case ld::File::objcConstraintNone: case ld::File::objcConstraintRetainRelease: @@ -116,8 +112,14 @@ ObjCImageInfoAtom::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, if ( compaction ) value |= OBJC_IMAGE_SUPPORTS_COMPACTION; break; + case ld::File::objcConstraintRetainReleaseForSimulator: + value |= OBJC_IMAGE_IS_SIMULATED; + break; } + // provide swift language version in final binary for runtime to inspect + value |= (swiftVersion << 8); + _content.version = 0; A::P::E::set32(_content.flags, value); } @@ -135,15 +137,13 @@ public: std::set& deadAtoms); virtual const ld::File* file() const { return _file; } - virtual bool translationUnitSource(const char** dir, const char**) const - { return false; } virtual const char* name() const { return "objc merged method list"; } virtual uint64_t size() const { return _methodCount*3*sizeof(pint_t) + 8; } virtual uint64_t objectAddress() const { return 0; } virtual void setScope(Scope) { } virtual void copyRawContent(uint8_t buffer[]) const { bzero(buffer, size()); - A::P::E::set32(*((uint32_t*)(&buffer[0])), 24); + A::P::E::set32(*((uint32_t*)(&buffer[0])), 3*sizeof(pint_t)); // entry size A::P::E::set32(*((uint32_t*)(&buffer[4])), _methodCount); } virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixups[0]; } @@ -174,8 +174,6 @@ public: std::set& deadAtoms); virtual const ld::File* file() const { return _file; } - virtual bool translationUnitSource(const char** dir, const char**) const - { return false; } virtual const char* name() const { return "objc merged protocol list"; } virtual uint64_t size() const { return (_protocolCount+1)*sizeof(pint_t); } virtual uint64_t objectAddress() const { return 0; } @@ -213,8 +211,6 @@ public: std::set& deadAtoms); virtual const ld::File* file() const { return _file; } - virtual bool translationUnitSource(const char** dir, const char**) const - { return false; } virtual const char* name() const { return "objc merged property list"; } virtual uint64_t size() const { return _propertyCount*2*sizeof(pint_t) + 8; } virtual uint64_t objectAddress() const { return 0; } @@ -255,8 +251,6 @@ public: // overrides of ld::Atom virtual const ld::File* file() const { return _atom->file(); } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return _atom->translationUnitSource(dir, nm); } virtual const char* name() const { return _atom->name(); } virtual uint64_t size() const { return _atom->size(); } virtual uint64_t objectAddress() const { return _atom->objectAddress(); } @@ -371,7 +365,7 @@ void ObjCData::setPointerInContent(ld::Internal& state, const ld::Atom* conte template class Category : public ObjCData { public: - static const ld::Atom* getClass(ld::Internal& state, const ld::Atom* contentAtom); + static const ld::Atom* getClass(ld::Internal& state, const ld::Atom* contentAtom, bool& hasAddend); static const ld::Atom* getInstanceMethods(ld::Internal& state, const ld::Atom* contentAtom); static const ld::Atom* getClassMethods(ld::Internal& state, const ld::Atom* contentAtom); static const ld::Atom* getProtocols(ld::Internal& state, const ld::Atom* contentAtom); @@ -383,9 +377,9 @@ private: template -const ld::Atom* Category::getClass(ld::Internal& state, const ld::Atom* contentAtom) +const ld::Atom* Category::getClass(ld::Internal& state, const ld::Atom* contentAtom, bool& hasAddend) { - return ObjCData::getPointerInContent(state, contentAtom, sizeof(pint_t)); // category_t.cls + return ObjCData::getPointerInContent(state, contentAtom, sizeof(pint_t), &hasAddend); // category_t.cls } template @@ -775,6 +769,35 @@ private: const std::set& _dead; }; + struct AtomSorter + { + bool operator()(const Atom* left, const Atom* right) + { + // sort by file ordinal, then object address, then zero size, then symbol name + // only file based atoms are supported (file() != NULL) + if (left==right) return false; + const File *leftf = left->file(); + const File *rightf = right->file(); + + if (leftf == rightf) { + if (left->objectAddress() != right->objectAddress()) { + return left->objectAddress() < right->objectAddress(); + } else { + // for atoms in the same file with the same address, zero sized + // atoms must sort before nonzero sized atoms + if ((left->size() == 0 && right->size() > 0) || (left->size() > 0 && right->size() == 0)) + return left->size() < right->size(); + return strcmp(left->name(), right->name()); + } + } + return (leftf->ordinal() < rightf->ordinal()); + } + }; + + static void sortAtomVector(std::vector &atoms) { + std::sort(atoms.begin(), atoms.end(), AtomSorter()); + } + template void OptimizeCategories::doit(const Options& opts, ld::Internal& state) { @@ -798,6 +821,7 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) // build map of all classes in this image that have categories on them typedef std::map*> CatMap; CatMap classToCategories; + std::vector classOrder; std::set deadAtoms; ld::Internal::FinalSection* methodListSection = NULL; for (std::vector::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) { @@ -813,17 +837,25 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) continue; } assert(categoryAtom != NULL); - assert(categoryAtom->size() == Category::size()); + assert(categoryAtom->size() >= Category::size()); // ignore categories also in __objc_nlcatlist if ( nlcatListAtoms.count(categoryAtom) != 0 ) continue; - const ld::Atom* categoryOnClassAtom = Category::getClass(state, categoryAtom); + const ld::Atom* categoryOnClassAtom = Category::getClass(state, categoryAtom, hasAddend); assert(categoryOnClassAtom != NULL); + // only look at classes defined in this image if ( categoryOnClassAtom->definition() != ld::Atom::definitionProxy ) { - // only look at classes defined in this image + // for now, back off optimization on new style classes + if ( hasAddend != 0 ) + continue; + // don't apply categories to swift classes + if ( categoryOnClassAtom->hasFixupsOfKind(ld::Fixup::kindNoneGroupSubordinate) ) + continue; + CatMap::iterator pos = classToCategories.find(categoryOnClassAtom); if ( pos == classToCategories.end() ) { classToCategories[categoryOnClassAtom] = new std::vector(); + classOrder.push_back(categoryOnClassAtom); } classToCategories[categoryOnClassAtom]->push_back(categoryAtom); // mark category atom and catlist atom as dead @@ -840,10 +872,11 @@ void OptimizeCategories::doit(const Options& opts, ld::Internal& state) // if found some categories if ( classToCategories.size() != 0 ) { assert(methodListSection != NULL); + sortAtomVector(classOrder); // alter each class definition to have new method list which includes all category methods - for (CatMap::iterator it=classToCategories.begin(); it != classToCategories.end(); ++it) { - const ld::Atom* classAtom = it->first; - const std::vector* categories = it->second; + for (std::vector::iterator it = classOrder.begin(); it != classOrder.end(); it++) { + const ld::Atom* classAtom = *it; + const std::vector* categories = classToCategories[classAtom]; assert(categories->size() != 0); // if any category adds instance methods, generate new merged method list, and replace if ( OptimizeCategories::hasInstanceMethods(state, categories) ) { @@ -918,6 +951,7 @@ MethodListAtom::MethodListAtom(ld::Internal& state, const ld::Atom* baseMetho symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _file(NULL), _methodCount(0) { unsigned int fixupCount = 0; + std::set baseMethodListMethodNameAtoms; // if base class has method list, then associate new method list with file defining class if ( baseMethodList != NULL ) { _file = baseMethodList->file(); @@ -925,6 +959,14 @@ MethodListAtom::MethodListAtom(ld::Internal& state, const ld::Atom* baseMetho _methodCount = MethodList::count(state, baseMethodList); deadAtoms.insert(baseMethodList); fixupCount = baseMethodList->fixupsEnd() - baseMethodList->fixupsBegin(); + for (ld::Fixup::iterator fit=baseMethodList->fixupsBegin(); fit != baseMethodList->fixupsEnd(); ++fit) { + if ( (fit->offsetInAtom - 8) % (3*sizeof(pint_t)) == 0 ) { + assert(fit->binding == ld::Fixup::bindingsIndirectlyBound && "malformed method list"); + const ld::Atom* target = state.indirectBindingTable[fit->u.bindingIndex]; + assert(target->contentType() == ld::Atom::typeCString && "malformed method list"); + baseMethodListMethodNameAtoms.insert(target); + } + } } for (std::vector::const_iterator ait=categories->begin(); ait != categories->end(); ++ait) { const ld::Atom* categoryMethodListAtom; @@ -950,6 +992,7 @@ MethodListAtom::MethodListAtom(ld::Internal& state, const ld::Atom* baseMetho // copy fixups and adjust offsets (in reverse order to simulator objc runtime) _fixups.reserve(fixupCount); uint32_t slide = 0; + std::set categoryMethodNameAtoms; for (std::vector::const_reverse_iterator rit=categories->rbegin(); rit != categories->rend(); ++rit) { const ld::Atom* categoryMethodListAtom; if ( meta ) @@ -961,8 +1004,24 @@ MethodListAtom::MethodListAtom(ld::Internal& state, const ld::Atom* baseMetho ld::Fixup fixup = *fit; fixup.offsetInAtom += slide; _fixups.push_back(fixup); - //if ( fixup.binding == ld::Fixup::bindingDirectlyBound ) - // fprintf(stderr, "offset=0x%08X, name=%s\n", fixup.offsetInAtom, fixup.u.target->name()); + if ( (fixup.offsetInAtom - 8) % (3*sizeof(pint_t)) == 0 ) { + // warning when a method is overridden in a category in the same link unit + assert(fixup.binding == ld::Fixup::bindingsIndirectlyBound && "malformed category method list"); + const ld::Atom* target = state.indirectBindingTable[fixup.u.bindingIndex]; + assert(target->contentType() == ld::Atom::typeCString && "malformed method list"); + // this objc pass happens after cstrings are coalesced, so we can just compare the atom addres instead of its content + if ( baseMethodListMethodNameAtoms.count(target) != 0 ) { + warning("%s method '%s' in category from %s overrides method from class in %s", + (meta ? "meta" : "instance"), target->rawContentPointer(), + categoryMethodListAtom->file()->path(), baseMethodList->file()->path() ); + } + if ( categoryMethodNameAtoms.count(target) != 0 ) { + warning("%s method '%s' in category from %s conflicts with same method from another category", + (meta ? "meta" : "instance"), target->rawContentPointer(), + categoryMethodListAtom->file()->path()); + } + categoryMethodNameAtoms.insert(target); + } } slide += 3*sizeof(pint_t) * MethodList::count(state, categoryMethodListAtom); } @@ -1119,26 +1178,30 @@ void doPass(const Options& opts, ld::Internal& state) // add image info atom switch ( opts.architecture() ) { +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - state.hasObjcReplacementClasses, true)); + true, state.swiftVersion)); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - state.hasObjcReplacementClasses, opts.objCABIVersion2POverride() ? true : false)); - break; - case CPU_TYPE_POWERPC: - state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - state.hasObjcReplacementClasses, false)); + opts.objCABIVersion2POverride() ? true : false, state.swiftVersion)); break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - state.hasObjcReplacementClasses, true)); + true, state.swiftVersion)); break; - case CPU_TYPE_POWERPC64: - state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, - state.hasObjcReplacementClasses, true)); +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + state.addAtom(*new ObjCImageInfoAtom(state.objcObjectConstraint, compaction, + true, state.swiftVersion)); break; +#endif default: assert(0 && "unknown objc arch"); } @@ -1147,21 +1210,27 @@ void doPass(const Options& opts, ld::Internal& state) if ( opts.objcCategoryMerging() ) { // optimize classes defined in this linkage unit by merging in categories also in this linkage unit switch ( opts.architecture() ) { +#if SUPPORT_ARCH_x86_64 case CPU_TYPE_X86_64: OptimizeCategories::doit(opts, state); break; +#endif +#if SUPPORT_ARCH_i386 case CPU_TYPE_I386: - // disable optimization until fully tested - //if ( opts.objCABIVersion2POverride() ) - // OptimizeCategories::doit(opts, state); + if ( opts.objCABIVersion2POverride() ) + OptimizeCategories::doit(opts, state); break; +#endif +#if SUPPORT_ARCH_arm_any case CPU_TYPE_ARM: - // disable optimization until fully tested - //OptimizeCategories::doit(opts, state); + OptimizeCategories::doit(opts, state); break; - case CPU_TYPE_POWERPC64: - case CPU_TYPE_POWERPC: +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + // disabled until tested break; +#endif default: assert(0 && "unknown objc arch"); }