Add support for weak symbols to the kernel linkers. It means that
linkers no longer raise an error when undefined weak symbols are found, but relocate as if the symbol value was 0. Note that we do not repeat the mistake of userspace dynamic linker of making the symbol lookup prefer non-weak symbol definition over the weak one, if both are available. In fact, kernel linker uses the first definition found, and ignores duplicates. Signature of the elf_lookup() and elf_obj_lookup() functions changed to split result/error code and the symbol address returned. Otherwise, it is impossible to return zero address as the symbol value, to MD relocation code. This explains the mechanical changes in elf_machdep.c sources. The powerpc64 R_PPC_JMP_SLOT handler did not checked error from the lookup() call, the patch leaves the code as is (untested). Reported by: glebius Sponsored by: The FreeBSD Foundation MFC after: 1 week
This commit is contained in:
parent
bb957021d7
commit
cff8c6f2d1
@ -168,6 +168,7 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
Elf_Size rtype, symidx;
|
||||
const Elf_Rel *rel;
|
||||
const Elf_Rela *rela;
|
||||
int error;
|
||||
|
||||
switch (type) {
|
||||
case ELF_RELOC_REL:
|
||||
@ -203,29 +204,29 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
break;
|
||||
|
||||
case R_X86_64_64: /* S + A */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
val = addr + addend;
|
||||
if (addr == 0)
|
||||
if (error != 0)
|
||||
return -1;
|
||||
if (*where != val)
|
||||
*where = val;
|
||||
break;
|
||||
|
||||
case R_X86_64_PC32: /* S + A - P */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
where32 = (Elf32_Addr *)where;
|
||||
val32 = (Elf32_Addr)(addr + addend - (Elf_Addr)where);
|
||||
if (addr == 0)
|
||||
if (error != 0)
|
||||
return -1;
|
||||
if (*where32 != val32)
|
||||
*where32 = val32;
|
||||
break;
|
||||
|
||||
case R_X86_64_32S: /* S + A sign extend */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
val32 = (Elf32_Addr)(addr + addend);
|
||||
where32 = (Elf32_Addr *)where;
|
||||
if (addr == 0)
|
||||
if (error != 0)
|
||||
return -1;
|
||||
if (*where32 != val32)
|
||||
*where32 = val32;
|
||||
@ -242,8 +243,8 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
|
||||
case R_X86_64_GLOB_DAT: /* S */
|
||||
case R_X86_64_JMP_SLOT: /* XXX need addend + offset */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return -1;
|
||||
if (*where != addr)
|
||||
*where = addr;
|
||||
|
@ -164,6 +164,7 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
Elf_Word rtype, symidx;
|
||||
const Elf_Rel *rel;
|
||||
const Elf_Rela *rela;
|
||||
int error;
|
||||
|
||||
switch (type) {
|
||||
case ELF_RELOC_REL:
|
||||
@ -199,8 +200,8 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
break;
|
||||
|
||||
case R_ARM_ABS32:
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return -1;
|
||||
store_ptr(where, addr + load_ptr(where));
|
||||
break;
|
||||
@ -215,8 +216,8 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
break;
|
||||
|
||||
case R_ARM_JUMP_SLOT:
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr) {
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error == 0) {
|
||||
store_ptr(where, addr);
|
||||
return (0);
|
||||
}
|
||||
|
@ -178,6 +178,7 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
Elf_Word rtype, symidx;
|
||||
const Elf_Rel *rel;
|
||||
const Elf_Rela *rela;
|
||||
int error;
|
||||
|
||||
switch (type) {
|
||||
case ELF_RELOC_REL:
|
||||
@ -213,8 +214,8 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
break;
|
||||
|
||||
case R_386_32: /* S + A */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return -1;
|
||||
addr += addend;
|
||||
if (*where != addr)
|
||||
@ -222,8 +223,8 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
break;
|
||||
|
||||
case R_386_PC32: /* S + A - P */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return -1;
|
||||
addr += addend - (Elf_Addr)where;
|
||||
if (*where != addr)
|
||||
@ -240,8 +241,8 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
break;
|
||||
|
||||
case R_386_GLOB_DAT: /* S */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return -1;
|
||||
if (*where != addr)
|
||||
*where = addr;
|
||||
|
@ -158,7 +158,7 @@ static int link_elf_each_function_nameval(linker_file_t,
|
||||
static void link_elf_reloc_local(linker_file_t);
|
||||
static long link_elf_symtab_get(linker_file_t, const Elf_Sym **);
|
||||
static long link_elf_strtab_get(linker_file_t, caddr_t *);
|
||||
static Elf_Addr elf_lookup(linker_file_t, Elf_Size, int);
|
||||
static int elf_lookup(linker_file_t, Elf_Size, int, Elf_Addr *);
|
||||
|
||||
static kobj_method_t link_elf_methods[] = {
|
||||
KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol),
|
||||
@ -1548,8 +1548,8 @@ elf_get_symname(linker_file_t lf, Elf_Size symidx)
|
||||
* This is not only more efficient, it's also more correct. It's not always
|
||||
* the case that the symbol can be found through the hash table.
|
||||
*/
|
||||
static Elf_Addr
|
||||
elf_lookup(linker_file_t lf, Elf_Size symidx, int deps)
|
||||
static int
|
||||
elf_lookup(linker_file_t lf, Elf_Size symidx, int deps, Elf_Addr *res)
|
||||
{
|
||||
elf_file_t ef = (elf_file_t)lf;
|
||||
const Elf_Sym *sym;
|
||||
@ -1557,8 +1557,10 @@ elf_lookup(linker_file_t lf, Elf_Size symidx, int deps)
|
||||
Elf_Addr addr, start, base;
|
||||
|
||||
/* Don't even try to lookup the symbol if the index is bogus. */
|
||||
if (symidx >= ef->nchains)
|
||||
return (0);
|
||||
if (symidx >= ef->nchains) {
|
||||
*res = 0;
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
sym = ef->symtab + symidx;
|
||||
|
||||
@ -1568,9 +1570,12 @@ elf_lookup(linker_file_t lf, Elf_Size symidx, int deps)
|
||||
*/
|
||||
if (ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
|
||||
/* Force lookup failure when we have an insanity. */
|
||||
if (sym->st_shndx == SHN_UNDEF || sym->st_value == 0)
|
||||
return (0);
|
||||
return ((Elf_Addr)ef->address + sym->st_value);
|
||||
if (sym->st_shndx == SHN_UNDEF || sym->st_value == 0) {
|
||||
*res = 0;
|
||||
return (EINVAL);
|
||||
}
|
||||
*res = ((Elf_Addr)ef->address + sym->st_value);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1583,8 +1588,10 @@ elf_lookup(linker_file_t lf, Elf_Size symidx, int deps)
|
||||
symbol = ef->strtab + sym->st_name;
|
||||
|
||||
/* Force a lookup failure if the symbol name is bogus. */
|
||||
if (*symbol == 0)
|
||||
return (0);
|
||||
if (*symbol == 0) {
|
||||
*res = 0;
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
addr = ((Elf_Addr)linker_file_lookup_symbol(lf, symbol, deps));
|
||||
|
||||
@ -1594,7 +1601,8 @@ elf_lookup(linker_file_t lf, Elf_Size symidx, int deps)
|
||||
else if (elf_set_find(&set_vnet_list, addr, &start, &base))
|
||||
addr = addr - start + base;
|
||||
#endif
|
||||
return addr;
|
||||
*res = addr;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -144,7 +144,8 @@ static void link_elf_reloc_local(linker_file_t);
|
||||
static long link_elf_symtab_get(linker_file_t, const Elf_Sym **);
|
||||
static long link_elf_strtab_get(linker_file_t, caddr_t *);
|
||||
|
||||
static Elf_Addr elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps);
|
||||
static int elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps,
|
||||
Elf_Addr *);
|
||||
|
||||
static kobj_method_t link_elf_methods[] = {
|
||||
KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol),
|
||||
@ -1253,38 +1254,46 @@ elf_obj_cleanup_globals_cache(elf_file_t ef)
|
||||
* This is not only more efficient, it's also more correct. It's not always
|
||||
* the case that the symbol can be found through the hash table.
|
||||
*/
|
||||
static Elf_Addr
|
||||
elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps)
|
||||
static int
|
||||
elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps, Elf_Addr *res)
|
||||
{
|
||||
elf_file_t ef = (elf_file_t)lf;
|
||||
Elf_Sym *sym;
|
||||
const char *symbol;
|
||||
Elf_Addr ret;
|
||||
Elf_Addr res1;
|
||||
|
||||
/* Don't even try to lookup the symbol if the index is bogus. */
|
||||
if (symidx >= ef->ddbsymcnt)
|
||||
return (0);
|
||||
if (symidx >= ef->ddbsymcnt) {
|
||||
*res = 0;
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
sym = ef->ddbsymtab + symidx;
|
||||
|
||||
/* Quick answer if there is a definition included. */
|
||||
if (sym->st_shndx != SHN_UNDEF)
|
||||
return (sym->st_value);
|
||||
if (sym->st_shndx != SHN_UNDEF) {
|
||||
*res = sym->st_value;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* If we get here, then it is undefined and needs a lookup. */
|
||||
switch (ELF_ST_BIND(sym->st_info)) {
|
||||
case STB_LOCAL:
|
||||
/* Local, but undefined? huh? */
|
||||
return (0);
|
||||
*res = 0;
|
||||
return (EINVAL);
|
||||
|
||||
case STB_GLOBAL:
|
||||
case STB_WEAK:
|
||||
/* Relative to Data or Function name */
|
||||
symbol = ef->ddbstrtab + sym->st_name;
|
||||
|
||||
/* Force a lookup failure if the symbol name is bogus. */
|
||||
if (*symbol == 0)
|
||||
return (0);
|
||||
ret = ((Elf_Addr)linker_file_lookup_symbol(lf, symbol, deps));
|
||||
if (*symbol == 0) {
|
||||
*res = 0;
|
||||
return (EINVAL);
|
||||
}
|
||||
res1 = (Elf_Addr)linker_file_lookup_symbol(lf, symbol, deps);
|
||||
|
||||
/*
|
||||
* Cache global lookups during module relocation. The failure
|
||||
@ -1296,18 +1305,20 @@ elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps)
|
||||
* restored to SHN_UNDEF in elf_obj_cleanup_globals_cache(),
|
||||
* above.
|
||||
*/
|
||||
if (ret != 0) {
|
||||
if (res1 != 0) {
|
||||
sym->st_shndx = SHN_FBSD_CACHED;
|
||||
sym->st_value = ret;
|
||||
sym->st_value = res1;
|
||||
*res = res1;
|
||||
return (0);
|
||||
} else if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {
|
||||
sym->st_value = 0;
|
||||
*res = 0;
|
||||
return (0);
|
||||
}
|
||||
return (ret);
|
||||
|
||||
case STB_WEAK:
|
||||
printf("link_elf_obj: Weak symbols not supported\n");
|
||||
return (0);
|
||||
return (EINVAL);
|
||||
|
||||
default:
|
||||
return (0);
|
||||
return (EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,6 +176,7 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
Elf_Word rtype = (Elf_Word)0, symidx;
|
||||
const Elf_Rel *rel = NULL;
|
||||
const Elf_Rela *rela = NULL;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Stash R_MIPS_HI16 info so we can use it when processing R_MIPS_LO16
|
||||
@ -215,8 +216,8 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
break;
|
||||
|
||||
case R_MIPS_32: /* S + A */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return (-1);
|
||||
addr += addend;
|
||||
if (*where != addr)
|
||||
@ -224,8 +225,8 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
break;
|
||||
|
||||
case R_MIPS_26: /* ((A << 2) | (P & 0xf0000000) + S) >> 2 */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return (-1);
|
||||
|
||||
addend &= 0x03ffffff;
|
||||
@ -243,8 +244,8 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
break;
|
||||
|
||||
case R_MIPS_64: /* S + A */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return (-1);
|
||||
addr += addend;
|
||||
if (*(Elf64_Addr*)where != addr)
|
||||
@ -253,8 +254,8 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
|
||||
case R_MIPS_HI16: /* ((AHL + S) - ((short)(AHL + S)) >> 16 */
|
||||
if (rela != NULL) {
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return (-1);
|
||||
addr += addend;
|
||||
*where &= 0xffff0000;
|
||||
@ -268,8 +269,8 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
|
||||
case R_MIPS_LO16: /* AHL + S */
|
||||
if (rela != NULL) {
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return (-1);
|
||||
addr += addend;
|
||||
*where &= 0xffff0000;
|
||||
@ -277,8 +278,8 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
}
|
||||
else {
|
||||
ahl += (int16_t)addend;
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return (-1);
|
||||
|
||||
addend &= 0xffff0000;
|
||||
@ -294,8 +295,8 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
break;
|
||||
|
||||
case R_MIPS_HIGHER: /* %higher(A+S) */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return (-1);
|
||||
addr += addend;
|
||||
*where &= 0xffff0000;
|
||||
@ -303,8 +304,8 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
break;
|
||||
|
||||
case R_MIPS_HIGHEST: /* %highest(A+S) */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return (-1);
|
||||
addr += addend;
|
||||
*where &= 0xffff0000;
|
||||
|
@ -183,6 +183,7 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
Elf_Addr addend;
|
||||
Elf_Word rtype, symidx;
|
||||
const Elf_Rela *rela;
|
||||
int error;
|
||||
|
||||
switch (type) {
|
||||
case ELF_RELOC_REL:
|
||||
@ -206,15 +207,15 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
break;
|
||||
|
||||
case R_PPC_ADDR32: /* word32 S + A */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
return -1;
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return -1;
|
||||
*where = elf_relocaddr(lf, addr + addend);
|
||||
break;
|
||||
|
||||
case R_PPC_ADDR16_LO: /* #lo(S) */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return -1;
|
||||
/*
|
||||
* addend values are sometimes relative to sections
|
||||
@ -228,8 +229,8 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
break;
|
||||
|
||||
case R_PPC_ADDR16_HA: /* #ha(S) */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return -1;
|
||||
/*
|
||||
* addend values are sometimes relative to sections
|
||||
|
@ -154,6 +154,7 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
Elf_Addr addend;
|
||||
Elf_Word rtype, symidx;
|
||||
const Elf_Rela *rela;
|
||||
int error;
|
||||
|
||||
switch (type) {
|
||||
case ELF_RELOC_REL:
|
||||
@ -176,9 +177,9 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
break;
|
||||
|
||||
case R_PPC64_ADDR64: /* doubleword64 S + A */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
return -1;
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return -1;
|
||||
addr += addend;
|
||||
*where = addr;
|
||||
break;
|
||||
@ -188,7 +189,7 @@ elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
|
||||
break;
|
||||
|
||||
case R_PPC_JMP_SLOT: /* function descriptor copy */
|
||||
addr = lookup(lf, symidx, 1);
|
||||
lookup(lf, symidx, 1, &addr);
|
||||
memcpy(where, (Elf_Addr *)addr, 3*sizeof(Elf_Addr));
|
||||
__asm __volatile("dcbst 0,%0; sync" :: "r"(where) : "memory");
|
||||
break;
|
||||
|
@ -344,6 +344,7 @@ elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type,
|
||||
Elf_Addr value;
|
||||
Elf_Addr mask;
|
||||
Elf_Addr addr;
|
||||
int error;
|
||||
|
||||
if (type != ELF_RELOC_RELA)
|
||||
return (-1);
|
||||
@ -372,8 +373,8 @@ elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type,
|
||||
value = rela->r_addend;
|
||||
|
||||
if (RELOC_RESOLVE_SYMBOL(rtype)) {
|
||||
addr = lookup(lf, symidx, 1);
|
||||
if (addr == 0)
|
||||
error = lookup(lf, symidx, 1, &addr);
|
||||
if (error != 0)
|
||||
return (-1);
|
||||
value += addr;
|
||||
if (RELOC_BARE_SYMBOL(rtype))
|
||||
|
@ -264,7 +264,7 @@ extern int kld_debug;
|
||||
|
||||
#endif
|
||||
|
||||
typedef Elf_Addr elf_lookup_fn(linker_file_t, Elf_Size, int);
|
||||
typedef int elf_lookup_fn(linker_file_t, Elf_Size, int, Elf_Addr *);
|
||||
|
||||
/* Support functions */
|
||||
int elf_reloc(linker_file_t _lf, Elf_Addr base, const void *_rel, int _type, elf_lookup_fn _lu);
|
||||
|
Loading…
Reference in New Issue
Block a user