@@ -716,52 +716,155 @@ 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
+ // 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
+
719
759
// Create .idata contents for each DLL.
720
760
for (std::vector<DefinedImportData *> &syms : v) {
721
761
// Create lookup and address tables. If they have external names,
722
762
// we need to create hintName chunks to store the names.
723
763
// If they don't (if they are import-by-ordinals), we store only
724
764
// ordinal values to the table.
725
765
size_t base = lookups.size ();
766
+ Chunk *lookupsTerminator = nullptr , *addressesTerminator = nullptr ;
726
767
for (DefinedImportData *s : syms) {
727
768
uint16_t ord = s->getOrdinal ();
769
+ HintNameChunk *hintChunk = nullptr ;
770
+ Chunk *lookupsChunk, *addressesChunk;
771
+
728
772
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);
731
775
} 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 );
736
780
}
737
781
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 ()) {
739
812
auto chunk = make<AuxImportChunk>(s->file );
740
813
auxIat.push_back (chunk);
741
814
s->file ->impECSym ->setLocation (chunk);
742
815
743
816
chunk = make<AuxImportChunk>(s->file );
744
817
auxIatCopy.push_back (chunk);
745
818
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));
746
823
}
747
824
}
748
825
// 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 ) {
752
831
auxIat.push_back (make<NullChunk>(ctx));
753
832
auxIatCopy.push_back (make<NullChunk>(ctx));
754
833
}
755
834
756
- for (int i = 0 , e = syms.size (); i < e; ++i)
835
+ for (int i = 0 , e = syms.size (); i < e; ++i) {
757
836
syms[i]->setLocation (addresses[base + i]);
837
+ if (syms[i]->file ->hybridFile )
838
+ syms[i]->file ->hybridFile ->impSym ->setLocation (addresses[base + i]);
839
+ }
758
840
759
841
// Create the import table header.
760
842
dllNames.push_back (make<StringChunk>(syms[0 ]->getDLLName ()));
761
843
auto *dir = make<ImportDirectoryChunk>(dllNames.back ());
762
844
dir->lookupTab = lookups[base];
763
845
dir->addressTab = addresses[base];
764
846
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
+ }
765
868
}
766
869
// Add null terminator.
767
870
dirs.push_back (make<NullChunk>(sizeof (ImportDirectoryTableEntry), 4 ));
0 commit comments