@@ -716,52 +716,155 @@ class ExportOrdinalChunk : public NonSectionChunk {
716716void IdataContents::create (COFFLinkerContext &ctx) {
717717 std::vector<std::vector<DefinedImportData *>> v = binImports (ctx, imports);
718718
719+ // Merge compatible EC and native import files in hybrid images.
720+ if (ctx.hybridSymtab ) {
721+ for (std::vector<DefinedImportData *> &syms : v) {
722+ // At this point, symbols are sorted by base name, ensuring that
723+ // compatible import files, if present, are adjacent.
724+ std::vector<DefinedImportData *> hybridSyms;
725+ ImportFile *prev = nullptr ;
726+ for (DefinedImportData *sym : syms) {
727+ ImportFile *file = sym->file ;
728+ if (!prev || file->isEC () == prev->isEC () ||
729+ !file->isSameImport (prev)) {
730+ hybridSyms.push_back (sym);
731+ prev = file;
732+ continue ;
733+ }
734+
735+ // The native variant exposes a subset of EC symbols and chunks. Use the
736+ // EC variant to represent both.
737+ if (file->isEC ()) {
738+ hybridSyms.pop_back ();
739+ hybridSyms.push_back (sym);
740+ }
741+
742+ prev->hybridFile = file;
743+ file->hybridFile = prev;
744+ prev = nullptr ;
745+ }
746+
747+ // Sort symbols by type: native-only files first, followed by merged
748+ // hybrid files, and then EC-only files.
749+ llvm::stable_sort (hybridSyms,
750+ [](DefinedImportData *a, DefinedImportData *b) {
751+ if (a->file ->hybridFile )
752+ return !b->file ->hybridFile && b->file ->isEC ();
753+ return !a->file ->isEC () && b->file ->isEC ();
754+ });
755+ syms = std::move (hybridSyms);
756+ }
757+ }
758+
719759 // Create .idata contents for each DLL.
720760 for (std::vector<DefinedImportData *> &syms : v) {
721761 // Create lookup and address tables. If they have external names,
722762 // we need to create hintName chunks to store the names.
723763 // If they don't (if they are import-by-ordinals), we store only
724764 // ordinal values to the table.
725765 size_t base = lookups.size ();
766+ Chunk *lookupsTerminator = nullptr , *addressesTerminator = nullptr ;
726767 for (DefinedImportData *s : syms) {
727768 uint16_t ord = s->getOrdinal ();
769+ HintNameChunk *hintChunk = nullptr ;
770+ Chunk *lookupsChunk, *addressesChunk;
771+
728772 if (s->getExternalName ().empty ()) {
729- lookups. push_back ( make<OrdinalOnlyChunk>(ctx, ord) );
730- addresses. push_back ( make<OrdinalOnlyChunk>(ctx, ord) );
773+ lookupsChunk = make<OrdinalOnlyChunk>(ctx, ord);
774+ addressesChunk = make<OrdinalOnlyChunk>(ctx, ord);
731775 } 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 );
776+ hintChunk = make<HintNameChunk>(s->getExternalName (), ord);
777+ lookupsChunk = make<LookupChunk>(ctx, hintChunk );
778+ addressesChunk = make<LookupChunk>(ctx, hintChunk );
779+ hints.push_back (hintChunk );
736780 }
737781
738- if (s->file ->impECSym ) {
782+ // Detect the first EC-only import in the hybrid IAT. Emit null chunks
783+ // and add an ARM64X relocation to replace it with the import for the EC
784+ // view. Additionally, use the original chunks as import terminators
785+ // and zero them with ARM64X relocations. Since these chunks appear
786+ // after the null terminator in the native view, they are always ignored
787+ // by the loader. However, MSVC emits them for some reason.
788+ if (ctx.hybridSymtab && !lookupsTerminator && s->file ->isEC () &&
789+ !s->file ->hybridFile ) {
790+ lookupsTerminator = lookupsChunk;
791+ addressesTerminator = addressesChunk;
792+ lookupsChunk = make<NullChunk>(ctx);
793+ addressesChunk = make<NullChunk>(ctx);
794+
795+ Arm64XRelocVal relocVal = hintChunk;
796+ if (!hintChunk)
797+ relocVal = (1ULL << 63 ) | ord;
798+ ctx.dynamicRelocs ->add (IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE,
799+ sizeof (uint64_t ), lookupsChunk, relocVal);
800+ ctx.dynamicRelocs ->add (IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE,
801+ sizeof (uint64_t ), addressesChunk, relocVal);
802+ ctx.dynamicRelocs ->add (IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL,
803+ sizeof (uint64_t ), lookupsTerminator);
804+ ctx.dynamicRelocs ->add (IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL,
805+ sizeof (uint64_t ), addressesTerminator);
806+ }
807+
808+ lookups.push_back (lookupsChunk);
809+ addresses.push_back (addressesChunk);
810+
811+ if (s->file ->isEC ()) {
739812 auto chunk = make<AuxImportChunk>(s->file );
740813 auxIat.push_back (chunk);
741814 s->file ->impECSym ->setLocation (chunk);
742815
743816 chunk = make<AuxImportChunk>(s->file );
744817 auxIatCopy.push_back (chunk);
745818 s->file ->auxImpCopySym ->setLocation (chunk);
819+ } else if (ctx.hybridSymtab ) {
820+ // Fill the auxiliary IAT with null chunks for native-only imports.
821+ auxIat.push_back (make<NullChunk>(ctx));
822+ auxIatCopy.push_back (make<NullChunk>(ctx));
746823 }
747824 }
748825 // Terminate with null values.
749- lookups.push_back (make<NullChunk>(ctx));
750- addresses.push_back (make<NullChunk>(ctx));
751- if (ctx.config .machine == ARM64EC) {
826+ lookups.push_back (lookupsTerminator ? lookupsTerminator
827+ : make<NullChunk>(ctx));
828+ addresses.push_back (addressesTerminator ? addressesTerminator
829+ : make<NullChunk>(ctx));
830+ if (ctx.symtabEC ) {
752831 auxIat.push_back (make<NullChunk>(ctx));
753832 auxIatCopy.push_back (make<NullChunk>(ctx));
754833 }
755834
756- for (int i = 0 , e = syms.size (); i < e; ++i)
835+ for (int i = 0 , e = syms.size (); i < e; ++i) {
757836 syms[i]->setLocation (addresses[base + i]);
837+ if (syms[i]->file ->hybridFile )
838+ syms[i]->file ->hybridFile ->impSym ->setLocation (addresses[base + i]);
839+ }
758840
759841 // Create the import table header.
760842 dllNames.push_back (make<StringChunk>(syms[0 ]->getDLLName ()));
761843 auto *dir = make<ImportDirectoryChunk>(dllNames.back ());
762844 dir->lookupTab = lookups[base];
763845 dir->addressTab = addresses[base];
764846 dirs.push_back (dir);
847+
848+ if (ctx.hybridSymtab ) {
849+ // If native-only imports exist, emit ARM64X relocations, skipping them in
850+ // the EC view.
851+ uint32_t nativeOnly =
852+ llvm::find_if (syms,
853+ [](DefinedImportData *s) { return s->file ->isEC (); }) -
854+ syms.begin ();
855+ if (nativeOnly) {
856+ ctx.dynamicRelocs ->add (
857+ IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0 ,
858+ Arm64XRelocVal (
859+ dir, offsetof (ImportDirectoryTableEntry, ImportLookupTableRVA)),
860+ nativeOnly * sizeof (uint64_t ));
861+ ctx.dynamicRelocs ->add (
862+ IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0 ,
863+ Arm64XRelocVal (dir, offsetof (ImportDirectoryTableEntry,
864+ ImportAddressTableRVA)),
865+ nativeOnly * sizeof (uint64_t ));
866+ }
867+ }
765868 }
766869 // Add null terminator.
767870 dirs.push_back (make<NullChunk>(sizeof (ImportDirectoryTableEntry), 4 ));
0 commit comments