Andreas Klebinger pushed to branch wip/andreask/linker_fix at Glasgow Haskell Compiler / GHC Commits: 25e881a4 by Andreas Klebinger at 2026-03-25T08:38:33+00:00 Add debug tracing things. - - - - - b262ea83 by Andreas Klebinger at 2026-03-25T08:38:53+00:00 check for mac sections - - - - - c76691b7 by Andreas Klebinger at 2026-03-25T09:14:11+00:00 Make invalid machO entries a failure condition for now. - - - - - bbb9a677 by Andreas Klebinger at 2026-03-25T17:58:07+00:00 Rip out old macOS misalignment code. Best I can tell it is no longer needed as we now *always* align individual sections. The handling was already very inconsistent with very few code paths actually doing this alignment. So I'm letting it go and will see if anything falls over. - - - - - 7 changed files: - rts/Linker.c - rts/LinkerInternals.h - rts/linker/LoadArchive.c - rts/linker/LoadNativeObjPosix.c - rts/linker/MachO.c - rts/linker/MachO.h - rts/linker/SymbolExtras.c Changes: ===================================== rts/Linker.c ===================================== @@ -1240,7 +1240,7 @@ void freeObjectCode (ObjectCode *oc) ObjectCode* mkOc( ObjectType type, pathchar *path, char *image, int imageSize, - bool mapped, pathchar *archiveMemberName, int misalignment ) { + bool mapped, pathchar *archiveMemberName ) { ObjectCode* oc; @@ -1294,7 +1294,6 @@ mkOc( ObjectType type, pathchar *path, char *image, int imageSize, oc->bssEnd = NULL; oc->imageMapped = mapped; - oc->misalignment = misalignment; oc->cxa_finalize = NULL; oc->extraInfos = NULL; @@ -1352,7 +1351,6 @@ preloadObjectFile (pathchar *path) int r; void *image; ObjectCode *oc; - int misalignment = 0; r = pathstat(path, &st); if (r == -1) { @@ -1410,29 +1408,8 @@ preloadObjectFile (pathchar *path) return NULL; } -# if defined(darwin_HOST_OS) - - // In a Mach-O .o file, all sections can and will be misaligned - // if the total size of the headers is not a multiple of the - // desired alignment. This is fine for .o files that only serve - // as input for the static linker, but it's not fine for us, - // as SSE (used by gcc for floating point) and Altivec require - // 16-byte alignment. - // We calculate the correct alignment from the header before - // reading the file, and then we misalign image on purpose so - // that the actual sections end up aligned again. - machoGetMisalignment(f, &misalignment); - //machoGetMisalignment might fail to parse the header, but in that - //case so will verifyAndInitOc so we leave cleanup to after verifyAndInitOc. - image = stgMallocBytes(fileSize + misalignment, "loadObj(image)"); - image += misalignment; - -# else /* !defined(darwin_HOST_OS) */ - image = stgMallocBytes(fileSize, "loadObj(image)"); -#endif /* !defined(darwin_HOST_OS) */ - int n; n = fread ( image, 1, fileSize, f ); fclose(f); @@ -1447,9 +1424,10 @@ preloadObjectFile (pathchar *path) IF_DEBUG(linker, debugBelch("loadObj: preloaded image at %p\n", (void *) image)); /* FIXME (AP): =mapped= parameter unconditionally set to true */ - oc = mkOc(STATIC_OBJECT, path, image, fileSize, true, NULL, misalignment); + oc = mkOc(STATIC_OBJECT, path, image, fileSize, true, NULL); if (!verifyAndInitOc(oc)) { + freePreloadObjectFile(oc); freeObjectCode(oc); debugBelch("loadObj: Failed to verify oc.\n"); return NULL; @@ -1510,7 +1488,7 @@ HsInt loadObj (pathchar *path) return r; } -// Call the relevant VeriffyImage_* and ocInit_* functions. +// Call the relevant VerifyImage_* and ocInit_* functions. // Return 1 on success. HsInt verifyAndInitOc (ObjectCode* oc) { @@ -1536,6 +1514,7 @@ HsInt verifyAndInitOc (ObjectCode* oc) #endif if (!r) { IF_DEBUG(linker, ocDebugBelch(oc, "ocVerifyImage_* failed\n")); + ocDebugBelch(oc, "ocVerifyImage_* failed\n"); return r; } return 1; @@ -1571,6 +1550,7 @@ HsInt loadOc (ObjectCode* oc) if (!r) { IF_DEBUG(linker, ocDebugBelch(oc, "ocAllocateExtras_MachO failed\n")); + ocDebugBelch(oc, "ocAllocateExtras_MachO failed\n"); return r; } # elif defined(OBJFORMAT_ELF) @@ -1595,6 +1575,7 @@ HsInt loadOc (ObjectCode* oc) # endif if (!r) { IF_DEBUG(linker, ocDebugBelch(oc, "ocGetNames_* failed\n")); + ocDebugBelch(oc, "ocGetNames_* failed\n"); return r; } ===================================== rts/LinkerInternals.h ===================================== @@ -264,10 +264,6 @@ struct _ObjectCode { /* non-zero if the object file was mmap'd, otherwise malloc'd */ int imageMapped; - /* record by how much image has been deliberately misaligned - after allocation, so that we can use realloc */ - int misalignment; - /* The address of __cxa_finalize; set when at least one finalizer was * register and therefore we must call __cxa_finalize before unloading. * See Note [Resolving __dso_handle]. */ @@ -385,7 +381,7 @@ extern Elf_Word shndx_table_uninit_label; ) #define ocDebugBelch(oc, s, ...) \ - debugBelch("%s(%" PATH_FMT ": " s, \ + errorBelch("%s(%" PATH_FMT ": " s, \ __func__, \ OC_INFORMATIVE_FILENAME(oc), \ ##__VA_ARGS__) @@ -494,8 +490,7 @@ HsInt verifyAndInitOc( ObjectCode *oc ); //Expects the oc to be verified already. HsInt loadOc( ObjectCode* oc ); ObjectCode* mkOc( ObjectType type, pathchar *path, char *image, int imageSize, - bool mapped, pathchar *archiveMemberName, - int misalignment + bool mapped, pathchar *archiveMemberName ); ===================================== rts/linker/LoadArchive.c ===================================== @@ -26,6 +26,7 @@ #include <ctype.h> #include <sys/types.h> #include <fs_rts.h> +#include <stdio.h> #define FAIL(...) do {\ errorBelch("loadArchive: "__VA_ARGS__); \ @@ -33,6 +34,7 @@ } while (0) #define DEBUG_LOG(...) IF_DEBUG(linker, debugBelch("loadArchive: " __VA_ARGS__)) +#define DEBUG_LOG_ALWAYS(...) (fprintf(stderr, "loadArchive: " __VA_ARGS__)) #if defined(darwin_HOST_OS) || defined(ios_HOST_OS) @@ -111,11 +113,8 @@ static bool loadFatArchive(char input[static 20], FILE* f, pathchar* path) } #endif -static bool readThinArchiveMember(int memberSize, pathchar* path, - char* fileName, char* image) +static pathchar* thinArchiveMemberPath(pathchar* path, char* fileName) { - bool has_succeeded = false; - FILE* member = NULL; pathchar *pathCopy, *dirName, *memberPath, *objFileName; memberPath = NULL; /* Allocate and setup the dirname of the archive. We'll need @@ -129,8 +128,22 @@ static bool readThinArchiveMember(int memberSize, pathchar* path, objFileName = mkPath(fileName); pathprintf(memberPath, memberLen, WSTR("%" PATH_FMT "%" PATH_FMT), dirName, objFileName); - stgFree(objFileName); + + stgFree(pathCopy); stgFree(dirName); + stgFree(objFileName); + return memberPath; +} + +static bool readThinArchiveMember(int memberSize, pathchar* path, + char* fileName, char* image) +{ + bool has_succeeded = false; + FILE* member = NULL; + pathchar *memberPath; + //Fully resolved path. + memberPath = thinArchiveMemberPath(path, fileName); + member = pathopen(memberPath, WSTR("rb")); if (!member) { errorBelch("loadObj: can't read thin archive `%" PATH_FMT "'", @@ -148,7 +161,6 @@ static bool readThinArchiveMember(int memberSize, pathchar* path, inner_fail: fclose(member); stgFree(memberPath); - stgFree(pathCopy); return has_succeeded; } @@ -335,7 +347,7 @@ HsInt loadArchive_ (pathchar *path) int memberIdx = 0; FILE *f = NULL; size_t thisFileNameSize = (size_t) -1; /* shut up bogus GCC warning */ - int misalignment = 0; + int macho_misalignment = 0; DEBUG_LOG("start\n"); DEBUG_LOG("Loading archive `%" PATH_FMT "'\n", path); @@ -344,7 +356,7 @@ HsInt loadArchive_ (pathchar *path) Ignore requests to load multiple times */ if (isAlreadyLoaded(path)) { IF_DEBUG(linker, - debugBelch("ignoring repeated load of %" PATH_FMT "\n", path)); + DEBUG_LOG("ignoring repeated load of %" PATH_FMT "\n", path)); return 1; /* success */ } @@ -365,6 +377,7 @@ HsInt loadArchive_ (pathchar *path) bool isThin = archive_fmt == ThinArchive; if(isThin) { DEBUG_LOG("Found thin archive.\n"); + DEBUG_LOG_ALWAYS("Found thin archive.\n"); } DEBUG_LOG("loading archive contents\n"); @@ -507,6 +520,17 @@ HsInt loadArchive_ (pathchar *path) DEBUG_LOG("Found member file `%s'\n", fileName); bool is_symbol_table = strcmp("", fileName) == 0; +#if defined(OBJFORMAT_MACHO) + if (!is_symbol_table) { + /* Darwin ranlib symbol tables are named __.SYMDEF* */ + if (strncmp(fileName, "__.SYMDEF", sizeof("__.SYMDEF")) == 0 || + strncmp(fileName, "__.SYMDEF SORTED", sizeof("__.SYMDEF SORTED")) == 0 || + strncmp(fileName, "__.SYMDEF_64", sizeof("__.SYMDEF_64")) == 0 || + strncmp(fileName, "__.SYMDEF_64 SORTED", sizeof("__.SYMDEF_64 SORTED")) == 0) { + is_symbol_table = true; + } + } +#endif ///////////////////////////////////////////////// // We found the member file. Load it into memory. @@ -571,27 +595,21 @@ HsInt loadArchive_ (pathchar *path) #endif ) { + ASSERT(!isGnuIndex); DEBUG_LOG("Member might be an object file...loading...\n"); #if defined(darwin_HOST_OS) || defined(ios_HOST_OS) - if (RTS_LINKER_USE_MMAP) - image = mmapAnonForLinker(memberSize); - else { - /* See loadObj() */ - if(!machoGetMisalignment(f, &misalignment)) { - DEBUG_LOG("Failed to load member as mach-o file. Skipping.\n"); - continue; - } - image = stgMallocBytes(memberSize + misalignment, - "loadArchive(image)"); - image += misalignment; - } +#if defined(RTS_LINKER_USE_MMAP) + image = mmapAnonForLinker(memberSize); +#else +#error "Only MMAP based loading supported on Apple platforms" +#endif // defined(RTS_LINKER_USE_MMAP) #else // not darwin image = stgMallocBytes(memberSize, "loadArchive(image)"); #endif if (isThin) { if (!readThinArchiveMember(memberSize, path, fileName, image)) { - goto fail; + FAIL("Failed to read thin member %" PATH_FMT"\n", path); } } else @@ -616,20 +634,23 @@ HsInt loadArchive_ (pathchar *path) /////////////////////////////////////////////////////////////// // Prepare headers, doesn't load any data yet. - ObjectCode *oc = mkOc(STATIC_OBJECT, path, image, memberSize, false, archiveMemberName, - misalignment); + ObjectCode *oc = mkOc(STATIC_OBJECT, path, image, memberSize, false, archiveMemberName); stgFree(archiveMemberName); if(!verifyAndInitOc( oc )) { - freeObjectCode( oc ); IF_DEBUG(linker, ocDebugBelch(oc, "Faild to verify ... skipping.")); + errorBelch("Faild to verify ... skipping."); + freeObjectCode( oc ); + //TODO: Unmap or free image continue; } if (0 == loadOc(oc)) { + errorBelch("Failed to load OC %" PATH_FMT " , aborting.\n", path); stgFree(fileName); + freeObjectCode( oc ); fclose(f); return 0; } else { @@ -744,4 +765,3 @@ bool isArchive (pathchar *path) } return strncmp(ARCHIVE_HEADER, buffer, sizeof(ARCHIVE_HEADER)-1) == 0; } - ===================================== rts/linker/LoadNativeObjPosix.c ===================================== @@ -134,7 +134,7 @@ void * loadNativeObj_POSIX (pathchar *path, char **errmsg) goto dlopen_fail; } - nc = mkOc(DYNAMIC_OBJECT, path, NULL, 0, false, NULL, 0); + nc = mkOc(DYNAMIC_OBJECT, path, NULL, 0, false, NULL); // If we HAVE_DLINFO, we use RTLD_NOW rather than RTLD_LAZY because we want // to learn eagerly about all external functions. Otherwise, there is no ===================================== rts/linker/MachO.c ===================================== @@ -1,4 +1,5 @@ #include "Rts.h" +#include "rts/Linker.h" #if defined(darwin_HOST_OS) || defined(ios_HOST_OS) @@ -200,6 +201,10 @@ int ocVerifyImage_MachO(ObjectCode * oc) { char *image = (char*) oc->image; + if (oc->fileSize < (int) sizeof(MachOHeader)) { + errorBelch("Tried loading machO smaller than header size.\n"); + return 0; + } MachOHeader *header = (MachOHeader*) image; IF_DEBUG(linker, debugBelch("ocVerifyImage_MachO: start\n")); @@ -1774,44 +1779,4 @@ ocRunFini_MachO ( ObjectCode *oc ) return 1; } -/* - * Figure out by how much to shift the entire Mach-O file in memory - * when loading so that its single segment ends up 16-byte-aligned - * - * Returns 1 and sets misalignment_out to the detected misalignment if - * we successfully parsed the file. - * - * If we can't parse the file we set misalignment_out to 0 and return 0 - */ -int -machoGetMisalignment( FILE * f, int* misalignment_out ) -{ - MachOHeader header; - int misalignment; - *misalignment_out = 0; - - { - size_t n = fread(&header, sizeof(header), 1, f); - if (n != 1) { - debugBelch("machoGetMisalignment: can't read the Mach-O header"); - return 0; - } - } - fseek(f, -sizeof(header), SEEK_CUR); - - if(header.magic != MH_MAGIC_64) { - debugBelch("Bad magic. Expected: %08x, got: %08x.", - MH_MAGIC_64, header.magic); - return 0; - } - - misalignment = (header.sizeofcmds + sizeof(header)) - & 0xF; - - IF_DEBUG(linker, debugBelch("mach-o misalignment %d\n", misalignment)); - misalignment = misalignment ? (16 - misalignment) : 0; - *misalignment_out = misalignment; - return 1; -} - #endif /* darwin_HOST_OS || ios_HOST_OS */ ===================================== rts/linker/MachO.h ===================================== @@ -13,7 +13,6 @@ int ocGetNames_MachO ( ObjectCode* oc ); int ocResolve_MachO ( ObjectCode* oc ); int ocRunInit_MachO ( ObjectCode* oc ); int ocRunFini_MachO ( ObjectCode* oc ); -int machoGetMisalignment ( FILE *, int* ); int ocAllocateExtras_MachO ( ObjectCode* oc ); SectionKind getSectionKind_MachO ( MachOSection *macho ); ===================================== rts/linker/SymbolExtras.c ===================================== @@ -63,14 +63,10 @@ int ocAllocateExtras(ObjectCode* oc, int count, int first, int bssSize) // round up to the nearest 4 int aligned = (oc->fileSize + 3) & ~3; - int misalignment = oc->misalignment; - oc->image -= misalignment; oc->image = stgReallocBytes( oc->image, - misalignment + aligned + extras_size, "ocAllocateExtras" ); - oc->image += misalignment; oc->symbol_extras = (SymbolExtra *) (oc->image + aligned); } else if (USE_CONTIGUOUS_MMAP || RtsFlags.MiscFlags.linkerAlwaysPic) { View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/fbef162a39d46295fea24cd6c3b1eb4... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/fbef162a39d46295fea24cd6c3b1eb4... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Andreas Klebinger (@AndreasK)