@@ -716,32 +716,49 @@ 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.
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.
720724 if (ctx.hybridSymtab ) {
721725 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.
724726 std::vector<DefinedImportData *> hybridSyms;
725727 ImportFile *prev = nullptr ;
726728 for (DefinedImportData *sym : syms) {
727729 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.
728737 if (!prev || file->isEC () == prev->isEC () ||
729738 !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.
730742 hybridSyms.push_back (sym);
731743 prev = file;
732744 continue ;
733745 }
734746
735- // The native variant exposes a subset of EC symbols and chunks. Use the
736- // EC variant to represent both.
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.
737752 if (file->isEC ()) {
738753 hybridSyms.pop_back ();
739754 hybridSyms.push_back (sym);
740755 }
741756
757+ // Merge import files by storing their hybrid form in the corresponding
758+ // file class.
742759 prev->hybridFile = file;
743760 file->hybridFile = prev;
744- prev = nullptr ;
761+ prev = nullptr ; // A hybrid import file cannot be merged again.
745762 }
746763
747764 // Sort symbols by type: native-only files first, followed by merged
@@ -780,11 +797,15 @@ void IdataContents::create(COFFLinkerContext &ctx) {
780797 }
781798
782799 // 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.
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.
788809 if (ctx.hybridSymtab && !lookupsTerminator && s->file ->isEC () &&
789810 !s->file ->hybridFile ) {
790811 lookupsTerminator = lookupsChunk;
@@ -846,8 +867,8 @@ void IdataContents::create(COFFLinkerContext &ctx) {
846867 dirs.push_back (dir);
847868
848869 if (ctx.hybridSymtab ) {
849- // If native-only imports exist, emit ARM64X relocations, skipping them in
850- // the EC view.
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.
851872 uint32_t nativeOnly =
852873 llvm::find_if (syms,
853874 [](DefinedImportData *s) { return s->file ->isEC (); }) -
0 commit comments