@@ -716,52 +716,176 @@ class ExportOrdinalChunk : public NonSectionChunk {
716
716
void IdataContents::create (COFFLinkerContext &ctx) {
717
717
std::vector<std::vector<DefinedImportData *>> v = binImports (ctx, imports);
718
718
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
+
719
776
// Create .idata contents for each DLL.
720
777
for (std::vector<DefinedImportData *> &syms : v) {
721
778
// Create lookup and address tables. If they have external names,
722
779
// we need to create hintName chunks to store the names.
723
780
// If they don't (if they are import-by-ordinals), we store only
724
781
// ordinal values to the table.
725
782
size_t base = lookups.size ();
783
+ Chunk *lookupsTerminator = nullptr , *addressesTerminator = nullptr ;
726
784
for (DefinedImportData *s : syms) {
727
785
uint16_t ord = s->getOrdinal ();
786
+ HintNameChunk *hintChunk = nullptr ;
787
+ Chunk *lookupsChunk, *addressesChunk;
788
+
728
789
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);
731
792
} 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 );
736
797
}
737
798
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 ()) {
739
833
auto chunk = make<AuxImportChunk>(s->file );
740
834
auxIat.push_back (chunk);
741
835
s->file ->impECSym ->setLocation (chunk);
742
836
743
837
chunk = make<AuxImportChunk>(s->file );
744
838
auxIatCopy.push_back (chunk);
745
839
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));
746
844
}
747
845
}
748
846
// 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 ) {
752
852
auxIat.push_back (make<NullChunk>(ctx));
753
853
auxIatCopy.push_back (make<NullChunk>(ctx));
754
854
}
755
855
756
- for (int i = 0 , e = syms.size (); i < e; ++i)
856
+ for (int i = 0 , e = syms.size (); i < e; ++i) {
757
857
syms[i]->setLocation (addresses[base + i]);
858
+ if (syms[i]->file ->hybridFile )
859
+ syms[i]->file ->hybridFile ->impSym ->setLocation (addresses[base + i]);
860
+ }
758
861
759
862
// Create the import table header.
760
863
dllNames.push_back (make<StringChunk>(syms[0 ]->getDLLName ()));
761
864
auto *dir = make<ImportDirectoryChunk>(dllNames.back ());
762
865
dir->lookupTab = lookups[base];
763
866
dir->addressTab = addresses[base];
764
867
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
+ }
765
889
}
766
890
// Add null terminator.
767
891
dirs.push_back (make<NullChunk>(sizeof (ImportDirectoryTableEntry), 4 ));
0 commit comments