Files
simplex-chat/scripts/nix/ghc914-rts-elf-comdat-functions.patch
T

150 lines
5.9 KiB
Diff

--- a/linker/Elf.c
+++ b/linker/Elf.c
@@ -796,6 +796,101 @@
}
#endif
-
+
+/* A zero st_size means unknown size, as used by GCC's i386 PIC thunks.
+ * Only local labels and section metadata may accompany that signature.
+ */
+static bool
+isOnlyFunctionDefinition_ELF(ObjectCode *oc, const Elf_Sym *function)
+{
+ for (ElfSymbolTable *table = oc->info->symbolTables;
+ table != NULL; table = table->next) {
+ for (size_t i = 0; i < table->n_symbols; i++) {
+ const Elf_Sym *other = table->symbols[i].elf_sym;
+ if (other == function || other->st_shndx != function->st_shndx) {
+ continue;
+ }
+ if (ELF_ST_BIND(other->st_info) != STB_LOCAL
+ || (ELF_ST_TYPE(other->st_info) != STT_NOTYPE
+ && ELF_ST_TYPE(other->st_info) != STT_SECTION)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/* Recognize only a COMDAT group containing one hidden function section.
+ * GCC's i386 PIC thunks use this form with a GLOBAL, not WEAK, signature.
+ * This is deliberately not general multi-section COMDAT selection: retaining
+ * duplicate initializer, TLS, data, or partial-function sections is unsafe.
+ */
+static bool
+isSingleFunctionComdat_ELF(ObjectCode *oc, const SymbolName *name)
+{
+ if (oc == NULL || oc->type != STATIC_OBJECT || oc->info == NULL
+ || oc->info->elfHeader == NULL || oc->image == NULL
+ || oc->fileSize <= 0) {
+ return false;
+ }
+
+ const Elf_Shdr *shdr = oc->info->sectionHeader;
+ const Elf_Word shnum = elf_shnum(oc->info->elfHeader);
+ const size_t imageSize = (size_t)oc->fileSize;
+ for (Elf_Word i = 0; i < shnum; i++) {
+ const Elf_Shdr *group = &shdr[i];
+ if (group->sh_type != SHT_GROUP
+ || group->sh_size != 2 * sizeof(Elf_Word)
+ || group->sh_offset > imageSize
+ || group->sh_size > imageSize - group->sh_offset) {
+ continue;
+ }
+ Elf_Word entries[2];
+ memcpy(entries, oc->image + group->sh_offset, sizeof(entries));
+ if (entries[0] != GRP_COMDAT || entries[1] == SHN_UNDEF
+ || entries[1] >= shnum) {
+ continue;
+ }
+ const Elf_Shdr *member = &shdr[entries[1]];
+ const Elf_Word required = SHF_ALLOC | SHF_EXECINSTR | SHF_GROUP;
+ if (member->sh_type != SHT_PROGBITS || member->sh_size == 0
+ || (member->sh_flags & required) != required
+ || (member->sh_flags & (SHF_WRITE | SHF_TLS)) != 0) {
+ continue;
+ }
+ for (ElfSymbolTable *table = oc->info->symbolTables;
+ table != NULL; table = table->next) {
+ if (table->index != group->sh_link
+ || group->sh_info >= table->n_symbols) {
+ continue;
+ }
+ const ElfSymbol *signature = &table->symbols[group->sh_info];
+ const Elf_Sym *symbol = signature->elf_sym;
+ if (signature->name != NULL && strcmp(signature->name, name) == 0
+ && ELF_ST_BIND(symbol->st_info) == STB_GLOBAL
+ && ELF_ST_TYPE(symbol->st_info) == STT_FUNC
+ && ELF_ST_VISIBILITY(symbol->st_other) == STV_HIDDEN
+ && symbol->st_shndx < SHN_LORESERVE
+ && symbol->st_shndx == entries[1]
+ && symbol->st_value == 0
+ && (symbol->st_size == member->sh_size
+ || (symbol->st_size == 0
+ && isOnlyFunctionDefinition_ELF(oc, symbol)))) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool
+ocIsComdatDuplicate_ELF(ObjectCode *oc, const SymbolName *name)
+{
+ RtsSymbolInfo *existing = lookupStrHashTable(symhash, name);
+ return existing != NULL && existing->owner != oc
+ && isSingleFunctionComdat_ELF(oc, name)
+ && isSingleFunctionComdat_ELF(existing->owner, name);
+}
+
int
ocGetNames_ELF ( ObjectCode* oc )
{
@@ -1234,7 +1329,9 @@
setWeakSymbol(oc, nm);
}
if (!ghciInsertSymbolTable(oc->fileName, symhash,
- nm, symbol->addr, isWeak, sym_type, symbol->elf_sym->st_size, oc)
+ nm, symbol->addr,
+ isWeak || ocIsComdatDuplicate_ELF(oc, nm),
+ sym_type, symbol->elf_sym->st_size, oc)
) {
goto fail;
}
--- a/linker/Elf.h
+++ b/linker/Elf.h
@@ -10,6 +10,7 @@
void ocDeinit_ELF ( ObjectCode* oc );
int ocVerifyImage_ELF ( ObjectCode* oc );
int ocGetNames_ELF ( ObjectCode* oc );
+bool ocIsComdatDuplicate_ELF(ObjectCode *oc, const SymbolName *name);
int ocResolve_ELF ( ObjectCode* oc );
int ocRunInit_ELF ( ObjectCode* oc );
int ocRunFini_ELF ( ObjectCode* oc );
--- a/Linker.c
+++ b/Linker.c
@@ -1645,10 +1645,19 @@
Symbol_t symbol;
for (x = 0; x < oc->n_symbols; x++) {
symbol = oc->symbols[x];
+ bool isWeak = isSymbolWeak(oc, symbol.name);
+#if defined(OBJFORMAT_ELF)
+ /* Archive discovery can change the selected owner. Recheck COMDAT
+ * eligibility now; never persist conditional acceptance as weakness.
+ */
+ if (symbol.name != NULL && !isWeak) {
+ isWeak = ocIsComdatDuplicate_ELF(oc, symbol.name);
+ }
+#endif
if ( symbol.name
&& !ghciInsertSymbolTable(oc->fileName, symhash, symbol.name,
symbol.addr,
- isSymbolWeak(oc, symbol.name),
+ isWeak,
symbol.type, 0, oc)) {
return 0;
}