@@ -716,52 +716,176 @@ class ExportOrdinalChunk : public NonSectionChunk {
716716void IdataContents::create (COFFLinkerContext &ctx) {
717717 std::vector<std::vector<DefinedImportData *>> v = binImports (ctx, imports);
718718
719+ // In hybrid images, EC and native code are usually very similar,
720+ // resulting in a highly similar set of imported symbols. Consequently,
721+ // their import tables can be shared, with ARM64X relocations handling any
722+ // differences. Identify matching import files used by EC and native code, and
723+ // merge them into a single hybrid import entry.
724+ if (ctx.hybridSymtab ) {
725+ for (std::vector<DefinedImportData *> &syms : v) {
726+ std::vector<DefinedImportData *> hybridSyms;
727+ ImportFile *prev = nullptr ;
728+ for (DefinedImportData *sym : syms) {
729+ ImportFile *file = sym->file ;
730+ // At this stage, symbols are sorted by base name, ensuring that
731+ // compatible import files, if present, are adjacent. Check if the
732+ // current symbol's file imports the same symbol as the previously added
733+ // one (if any and if it was not already merged). Additionally, verify
734+ // that one of them is native while the other is EC. In rare cases,
735+ // separate matching import entries may exist within the same namespace,
736+ // which cannot be merged.
737+ if (!prev || file->isEC () == prev->isEC () ||
738+ !file->isSameImport (prev)) {
739+ // We can't merge the import file, just add it to hybridSyms
740+ // and set prev to its file so that we can try to match the next
741+ // symbol.
742+ hybridSyms.push_back (sym);
743+ prev = file;
744+ continue ;
745+ }
746+
747+ // A matching symbol may appear in syms in any order. The native variant
748+ // exposes a subset of EC symbols and chunks, so always use the EC
749+ // variant as the hybrid import file. If the native file was already
750+ // added, replace it with the EC symbol in hybridSyms. Otherwise, the EC
751+ // variant is already pushed, so we can simply merge it.
752+ if (file->isEC ()) {
753+ hybridSyms.pop_back ();
754+ hybridSyms.push_back (sym);
755+ }
756+
757+ // Merge import files by storing their hybrid form in the corresponding
758+ // file class.
759+ prev->hybridFile = file;
760+ file->hybridFile = prev;
761+ prev = nullptr ; // A hybrid import file cannot be merged again.
762+ }
763+
764+ // Sort symbols by type: native-only files first, followed by merged
765+ // hybrid files, and then EC-only files.
766+ llvm::stable_sort (hybridSyms,
767+ [](DefinedImportData *a, DefinedImportData *b) {
768+ if (a->file ->hybridFile )
769+ return !b->file ->hybridFile && b->file ->isEC ();
770+ return !a->file ->isEC () && b->file ->isEC ();
771+ });
772+ syms = std::move (hybridSyms);
773+ }
774+ }
775+
719776 // Create .idata contents for each DLL.
720777 for (std::vector<DefinedImportData *> &syms : v) {
721778 // Create lookup and address tables. If they have external names,
722779 // we need to create hintName chunks to store the names.
723780 // If they don't (if they are import-by-ordinals), we store only
724781 // ordinal values to the table.
725782 size_t base = lookups.size ();
783+ Chunk *lookupsTerminator = nullptr , *addressesTerminator = nullptr ;
726784 for (DefinedImportData *s : syms) {
727785 uint16_t ord = s->getOrdinal ();
786+ HintNameChunk *hintChunk = nullptr ;
787+ Chunk *lookupsChunk, *addressesChunk;
788+
728789 if (s->getExternalName ().empty ()) {
729- lookups. push_back ( make<OrdinalOnlyChunk>(ctx, ord) );
730- addresses. push_back ( make<OrdinalOnlyChunk>(ctx, ord) );
790+ lookupsChunk = make<OrdinalOnlyChunk>(ctx, ord);
791+ addressesChunk = make<OrdinalOnlyChunk>(ctx, ord);
731792 } else {
732- auto *c = make<HintNameChunk>(s->getExternalName (), ord);
733- lookups. push_back ( make<LookupChunk>(ctx, c) );
734- addresses. push_back ( make<LookupChunk>(ctx, c) );
735- hints.push_back (c );
793+ hintChunk = make<HintNameChunk>(s->getExternalName (), ord);
794+ lookupsChunk = make<LookupChunk>(ctx, hintChunk );
795+ addressesChunk = make<LookupChunk>(ctx, hintChunk );
796+ hints.push_back (hintChunk );
736797 }
737798
738- if (s->file ->impECSym ) {
799+ // Detect the first EC-only import in the hybrid IAT. Emit null chunk
800+ // as a terminator for the native view, and add an ARM64X relocation to
801+ // replace it with the correct import for the EC view.
802+ //
803+ // Additionally, for MSVC compatibility, store the lookup and address
804+ // chunks and append them at the end of EC-only imports, where a null
805+ // terminator chunk would typically be placed. Since they appear after
806+ // the native terminator, they will be ignored in the native view.
807+ // In the EC view, they should act as terminators, so emit ZEROFILL
808+ // relocations overriding them.
809+ if (ctx.hybridSymtab && !lookupsTerminator && s->file ->isEC () &&
810+ !s->file ->hybridFile ) {
811+ lookupsTerminator = lookupsChunk;
812+ addressesTerminator = addressesChunk;
813+ lookupsChunk = make<NullChunk>(ctx);
814+ addressesChunk = make<NullChunk>(ctx);
815+
816+ Arm64XRelocVal relocVal = hintChunk;
817+ if (!hintChunk)
818+ relocVal = (1ULL << 63 ) | ord;
819+ ctx.dynamicRelocs ->add (IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE,
820+ sizeof (uint64_t ), lookupsChunk, relocVal);
821+ ctx.dynamicRelocs ->add (IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE,
822+ sizeof (uint64_t ), addressesChunk, relocVal);
823+ ctx.dynamicRelocs ->add (IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL,
824+ sizeof (uint64_t ), lookupsTerminator);
825+ ctx.dynamicRelocs ->add (IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL,
826+ sizeof (uint64_t ), addressesTerminator);
827+ }
828+
829+ lookups.push_back (lookupsChunk);
830+ addresses.push_back (addressesChunk);
831+
832+ if (s->file ->isEC ()) {
739833 auto chunk = make<AuxImportChunk>(s->file );
740834 auxIat.push_back (chunk);
741835 s->file ->impECSym ->setLocation (chunk);
742836
743837 chunk = make<AuxImportChunk>(s->file );
744838 auxIatCopy.push_back (chunk);
745839 s->file ->auxImpCopySym ->setLocation (chunk);
840+ } else if (ctx.hybridSymtab ) {
841+ // Fill the auxiliary IAT with null chunks for native-only imports.
842+ auxIat.push_back (make<NullChunk>(ctx));
843+ auxIatCopy.push_back (make<NullChunk>(ctx));
746844 }
747845 }
748846 // Terminate with null values.
749- lookups.push_back (make<NullChunk>(ctx));
750- addresses.push_back (make<NullChunk>(ctx));
751- if (ctx.config .machine == ARM64EC) {
847+ lookups.push_back (lookupsTerminator ? lookupsTerminator
848+ : make<NullChunk>(ctx));
849+ addresses.push_back (addressesTerminator ? addressesTerminator
850+ : make<NullChunk>(ctx));
851+ if (ctx.symtabEC ) {
752852 auxIat.push_back (make<NullChunk>(ctx));
753853 auxIatCopy.push_back (make<NullChunk>(ctx));
754854 }
755855
756- for (int i = 0 , e = syms.size (); i < e; ++i)
856+ for (int i = 0 , e = syms.size (); i < e; ++i) {
757857 syms[i]->setLocation (addresses[base + i]);
858+ if (syms[i]->file ->hybridFile )
859+ syms[i]->file ->hybridFile ->impSym ->setLocation (addresses[base + i]);
860+ }
758861
759862 // Create the import table header.
760863 dllNames.push_back (make<StringChunk>(syms[0 ]->getDLLName ()));
761864 auto *dir = make<ImportDirectoryChunk>(dllNames.back ());
762865 dir->lookupTab = lookups[base];
763866 dir->addressTab = addresses[base];
764867 dirs.push_back (dir);
868+
869+ if (ctx.hybridSymtab ) {
870+ // If native-only imports exist, they will appear as a prefix to all
871+ // imports. Emit ARM64X relocations to skip them in the EC view.
872+ uint32_t nativeOnly =
873+ llvm::find_if (syms,
874+ [](DefinedImportData *s) { return s->file ->isEC (); }) -
875+ syms.begin ();
876+ if (nativeOnly) {
877+ ctx.dynamicRelocs ->add (
878+ IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0 ,
879+ Arm64XRelocVal (
880+ dir, offsetof (ImportDirectoryTableEntry, ImportLookupTableRVA)),
881+ nativeOnly * sizeof (uint64_t ));
882+ ctx.dynamicRelocs ->add (
883+ IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0 ,
884+ Arm64XRelocVal (dir, offsetof (ImportDirectoryTableEntry,
885+ ImportAddressTableRVA)),
886+ nativeOnly * sizeof (uint64_t ));
887+ }
888+ }
765889 }
766890 // Add null terminator.
767891 dirs.push_back (make<NullChunk>(sizeof (ImportDirectoryTableEntry), 4 ));
0 commit comments