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:
Konstantin Belousov 2015-09-20 01:27:59 +00:00
parent bb957021d7
commit cff8c6f2d1
10 changed files with 105 additions and 79 deletions

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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))

View File

@ -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);