diff options
author | jake <jake@FreeBSD.org> | 2003-01-21 02:42:44 +0000 |
---|---|---|
committer | jake <jake@FreeBSD.org> | 2003-01-21 02:42:44 +0000 |
commit | c1e42cc8bb6beb27c070748587d19f1d63fc956a (patch) | |
tree | a6736a5694dc3395d090d1d257e111c315fd88a0 /sys | |
parent | 013247cd19dd036ba508021e0edaba94b2a75960 (diff) | |
download | FreeBSD-src-c1e42cc8bb6beb27c070748587d19f1d63fc956a.zip FreeBSD-src-c1e42cc8bb6beb27c070748587d19f1d63fc956a.tar.gz |
Resolve relative relocations in klds before trying to parse the module's
metadata. This fixes module dependency resolution by the kernel linker on
sparc64, where the relocations for the metadata are different than on other
architectures; the relative offset is in the addend of an Elf_Rela record
instead of the original value of the location being patched.
Also fix printf formats in debug code.
Submitted by: Hartmut Brandt <brandt@fokus.gmd.de>
PR: 46732
Tested on: alpha (obrien), i386, sparc64
Diffstat (limited to 'sys')
-rw-r--r-- | sys/alpha/alpha/elf_machdep.c | 30 | ||||
-rw-r--r-- | sys/amd64/amd64/elf_machdep.c | 32 | ||||
-rw-r--r-- | sys/i386/i386/elf_machdep.c | 32 | ||||
-rw-r--r-- | sys/ia64/ia64/elf_machdep.c | 25 | ||||
-rw-r--r-- | sys/kern/kern_linker.c | 102 | ||||
-rw-r--r-- | sys/kern/link_elf.c | 32 | ||||
-rw-r--r-- | sys/kern/link_elf_obj.c | 32 | ||||
-rw-r--r-- | sys/powerpc/powerpc/elf_machdep.c | 18 | ||||
-rw-r--r-- | sys/sparc64/sparc64/elf_machdep.c | 24 | ||||
-rw-r--r-- | sys/sys/linker.h | 1 |
10 files changed, 232 insertions, 96 deletions
diff --git a/sys/alpha/alpha/elf_machdep.c b/sys/alpha/alpha/elf_machdep.c index 285a174..1b3c6f2 100644 --- a/sys/alpha/alpha/elf_machdep.c +++ b/sys/alpha/alpha/elf_machdep.c @@ -89,8 +89,8 @@ SYSINIT(elf64, SI_SUB_EXEC, SI_ORDER_ANY, &freebsd_brand_info); /* Process one elf relocation with addend. */ -int -elf_reloc(linker_file_t lf, const void *data, int type) +static int +elf_reloc_internal(linker_file_t lf, const void *data, int type, int local) { Elf_Addr relocbase = (Elf_Addr) lf->address; Elf_Addr *where; @@ -119,6 +119,15 @@ elf_reloc(linker_file_t lf, const void *data, int type) panic("elf_reloc: unknown relocation mode %d\n", type); } + if (local) { + if (rtype == R_ALPHA_RELATIVE) { + addr = relocbase + addend; + if (*where != addr) + *where = addr; + } + return (0); + } + switch (rtype) { case R_ALPHA_NONE: @@ -152,9 +161,6 @@ elf_reloc(linker_file_t lf, const void *data, int type) break; case R_ALPHA_RELATIVE: - addr = relocbase + addend; - if (*where != addr) - *where = addr; break; case R_ALPHA_COPY: @@ -174,6 +180,20 @@ elf_reloc(linker_file_t lf, const void *data, int type) } int +elf_reloc(linker_file_t lf, const void *data, int type) +{ + + return (elf_reloc_internal(lf, data, type, 0)); +} + +int +elf_reloc_local(linker_file_t lf, const void *data, int type) +{ + + return (elf_reloc_internal(lf, data, type, 1)); +} + +int elf_cpu_load_file(linker_file_t lf __unused) { diff --git a/sys/amd64/amd64/elf_machdep.c b/sys/amd64/amd64/elf_machdep.c index 71520de..4cf9f21 100644 --- a/sys/amd64/amd64/elf_machdep.c +++ b/sys/amd64/amd64/elf_machdep.c @@ -86,8 +86,8 @@ SYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_ANY, &freebsd_brand_info); /* Process one elf relocation with addend. */ -int -elf_reloc(linker_file_t lf, const void *data, int type) +static int +elf_reloc_internal(linker_file_t lf, const void *data, int type, int local) { Elf_Addr relocbase = (Elf_Addr) lf->address; Elf_Addr *where; @@ -116,6 +116,15 @@ elf_reloc(linker_file_t lf, const void *data, int type) panic("unknown reloc type %d\n", type); } + if (local) { + if (rtype == R_386_RELATIVE) { /* A + B */ + addr = relocbase + addend; + if (*where != addr) + *where = addr; + } + return (0); + } + switch (rtype) { case R_386_NONE: /* none */ @@ -156,10 +165,7 @@ elf_reloc(linker_file_t lf, const void *data, int type) *where = addr; break; - case R_386_RELATIVE: /* B + A */ - addr = relocbase + addend; - if (*where != addr) - *where = addr; + case R_386_RELATIVE: break; default: @@ -171,6 +177,20 @@ elf_reloc(linker_file_t lf, const void *data, int type) } int +elf_reloc(linker_file_t lf, const void *data, int type) +{ + + return (elf_reloc_internal(lf, data, type, 0)); +} + +int +elf_reloc_local(linker_file_t lf, const void *data, int type) +{ + + return (elf_reloc_internal(lf, data, type, 1)); +} + +int elf_cpu_load_file(linker_file_t lf __unused) { diff --git a/sys/i386/i386/elf_machdep.c b/sys/i386/i386/elf_machdep.c index 71520de..4cf9f21 100644 --- a/sys/i386/i386/elf_machdep.c +++ b/sys/i386/i386/elf_machdep.c @@ -86,8 +86,8 @@ SYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_ANY, &freebsd_brand_info); /* Process one elf relocation with addend. */ -int -elf_reloc(linker_file_t lf, const void *data, int type) +static int +elf_reloc_internal(linker_file_t lf, const void *data, int type, int local) { Elf_Addr relocbase = (Elf_Addr) lf->address; Elf_Addr *where; @@ -116,6 +116,15 @@ elf_reloc(linker_file_t lf, const void *data, int type) panic("unknown reloc type %d\n", type); } + if (local) { + if (rtype == R_386_RELATIVE) { /* A + B */ + addr = relocbase + addend; + if (*where != addr) + *where = addr; + } + return (0); + } + switch (rtype) { case R_386_NONE: /* none */ @@ -156,10 +165,7 @@ elf_reloc(linker_file_t lf, const void *data, int type) *where = addr; break; - case R_386_RELATIVE: /* B + A */ - addr = relocbase + addend; - if (*where != addr) - *where = addr; + case R_386_RELATIVE: break; default: @@ -171,6 +177,20 @@ elf_reloc(linker_file_t lf, const void *data, int type) } int +elf_reloc(linker_file_t lf, const void *data, int type) +{ + + return (elf_reloc_internal(lf, data, type, 0)); +} + +int +elf_reloc_local(linker_file_t lf, const void *data, int type) +{ + + return (elf_reloc_internal(lf, data, type, 1)); +} + +int elf_cpu_load_file(linker_file_t lf __unused) { diff --git a/sys/ia64/ia64/elf_machdep.c b/sys/ia64/ia64/elf_machdep.c index c4a4938..b3c7d90 100644 --- a/sys/ia64/ia64/elf_machdep.c +++ b/sys/ia64/ia64/elf_machdep.c @@ -141,8 +141,8 @@ lookup_fdesc(linker_file_t lf, Elf_Word symidx) } /* Process one elf relocation with addend. */ -int -elf_reloc(linker_file_t lf, const void *data, int type) +static int +elf_reloc_internal(linker_file_t lf, const void *data, int type, int local) { Elf_Addr relocbase = (Elf_Addr)lf->address; Elf_Addr *where; @@ -179,6 +179,12 @@ elf_reloc(linker_file_t lf, const void *data, int type) panic("%s: invalid ELF relocation (0x%x)\n", __func__, type); } + if (local) { + if (rtype == R_IA64_REL64LSB) + *where = relocbase + addend; + return (0); + } + switch (rtype) { case R_IA64_NONE: break; @@ -199,7 +205,6 @@ elf_reloc(linker_file_t lf, const void *data, int type) *where = addr; break; case R_IA64_REL64LSB: /* word64 LSB BD + A */ - *where = relocbase + addend; break; case R_IA64_IPLTLSB: addr = lookup_fdesc(lf, symidx); @@ -218,6 +223,20 @@ elf_reloc(linker_file_t lf, const void *data, int type) } int +elf_reloc(linker_file_t lf, const void *data, int type) +{ + + return (elf_reloc_internal(lf, data, type, 0)); +} + +int +elf_reloc_local(linker_file_t lf, const void *data, int type) +{ + + return (elf_reloc_internal(lf, data, type, 1)); +} + +int elf_cpu_load_file(linker_file_t lf) { Elf_Ehdr *hdr; diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c index c1a7d3a..3c38bac 100644 --- a/sys/kern/kern_linker.c +++ b/sys/kern/kern_linker.c @@ -499,7 +499,7 @@ linker_file_unload(linker_file_t file) * Give the module a chance to veto the unload. */ if ((error = module_unload(mod)) != 0) { - KLD_DPF(FILE, ("linker_file_unload: module %x" + KLD_DPF(FILE, ("linker_file_unload: module %p" " vetoes unload\n", mod)); goto out; } else @@ -594,7 +594,7 @@ linker_file_lookup_symbol(linker_file_t file, const char *name, int deps) size_t common_size = 0; int i; - KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%x, name=%s, deps=%d\n", + KLD_DPF(SYM, ("linker_file_lookup_symbol: file=%p, name=%s, deps=%d\n", file, name, deps)); if (LINKER_LOOKUP_SYMBOL(file, name, &sym) == 0) { @@ -608,7 +608,7 @@ linker_file_lookup_symbol(linker_file_t file, const char *name, int deps) common_size = symval.size; else { KLD_DPF(SYM, ("linker_file_lookup_symbol: symbol" - ".value=%x\n", symval.value)); + ".value=%p\n", symval.value)); return (symval.value); } } @@ -618,7 +618,7 @@ linker_file_lookup_symbol(linker_file_t file, const char *name, int deps) name, 0); if (address) { KLD_DPF(SYM, ("linker_file_lookup_symbol:" - " deps value=%x\n", address)); + " deps value=%p\n", address)); return (address); } } @@ -634,7 +634,7 @@ linker_file_lookup_symbol(linker_file_t file, const char *name, int deps) STAILQ_FOREACH(cp, &file->common, link) { if (strcmp(cp->name, name) == 0) { KLD_DPF(SYM, ("linker_file_lookup_symbol:" - " old common value=%x\n", cp->address)); + " old common value=%p\n", cp->address)); return (cp->address); } } @@ -656,7 +656,7 @@ linker_file_lookup_symbol(linker_file_t file, const char *name, int deps) STAILQ_INSERT_TAIL(&file->common, cp, link); KLD_DPF(SYM, ("linker_file_lookup_symbol: new common" - " value=%x\n", cp->address)); + " value=%p\n", cp->address)); return (cp->address); } KLD_DPF(SYM, ("linker_file_lookup_symbol: fail\n")); @@ -1106,46 +1106,6 @@ modlist_newmodule(const char *modname, int version, linker_file_t container) return (mod); } -/* - * This routine is cheap and nasty but will work for data pointers. - */ -static void * -linker_reloc_ptr(linker_file_t lf, const void *offset) -{ - return (lf->address + (uintptr_t)offset); -} - -/* - * Dereference MDT_VERSION metadata into module name and version - */ -static void -linker_mdt_version(linker_file_t lf, struct mod_metadata *mp, - const char **modname, int *version) -{ - struct mod_version *mvp; - - if (modname) - *modname = linker_reloc_ptr(lf, mp->md_cval); - if (version) { - mvp = linker_reloc_ptr(lf, mp->md_data); - *version = mvp->mv_version; - } -} - -/* - * Dereference MDT_DEPEND metadata into module name and mod_depend structure - */ -static void -linker_mdt_depend(linker_file_t lf, struct mod_metadata *mp, - const char **modname, struct mod_depend **verinfo) -{ - - if (modname) - *modname = linker_reloc_ptr(lf, mp->md_cval); - if (verinfo) - *verinfo = linker_reloc_ptr(lf, mp->md_data); -} - static void linker_addmodules(linker_file_t lf, struct mod_metadata **start, struct mod_metadata **stop, int preload) @@ -1155,17 +1115,11 @@ linker_addmodules(linker_file_t lf, struct mod_metadata **start, int ver; for (mdp = start; mdp < stop; mdp++) { - if (preload) - mp = *mdp; - else - mp = linker_reloc_ptr(lf, *mdp); + mp = *mdp; if (mp->md_type != MDT_VERSION) continue; - if (preload) { - modname = mp->md_cval; - ver = ((struct mod_version *)mp->md_data)->mv_version; - } else - linker_mdt_version(lf, mp, &modname, &ver); + modname = mp->md_cval; + ver = ((struct mod_version *)mp->md_data)->mv_version; if (modlist_lookup(modname, ver) != NULL) { printf("module %s already present!\n", modname); /* XXX what can we do? this is a build error. :-( */ @@ -1249,18 +1203,16 @@ restart: resolves = 1; /* unless we know otherwise */ if (!error) { for (mdp = start; mdp < stop; mdp++) { - mp = linker_reloc_ptr(lf, *mdp); + mp = *mdp; if (mp->md_type != MDT_DEPEND) continue; - linker_mdt_depend(lf, mp, &modname, &verinfo); + modname = mp->md_cval; + verinfo = mp->md_data; for (nmdp = start; nmdp < stop; nmdp++) { - nmp = linker_reloc_ptr(lf, *nmdp); + nmp = *nmdp; if (nmp->md_type != MDT_VERSION) continue; - linker_mdt_version(lf, nmp, &nmodname, - NULL); - nmodname = linker_reloc_ptr(lf, - nmp->md_cval); + nmodname = nmp->md_cval; if (strcmp(modname, nmodname) == 0) break; } @@ -1283,11 +1235,12 @@ restart: if (resolves) { if (!error) { for (mdp = start; mdp < stop; mdp++) { - mp = linker_reloc_ptr(lf, *mdp); + mp = *mdp; if (mp->md_type != MDT_VERSION) continue; - linker_mdt_version(lf, mp, - &modname, &nver); + modname = mp->md_cval; + nver = ((struct mod_version *) + mp->md_data)->mv_version; if (modlist_lookup(modname, nver) != NULL) { printf("module %s already" @@ -1338,10 +1291,11 @@ restart: &stop, NULL); if (!error) { for (mdp = start; mdp < stop; mdp++) { - mp = linker_reloc_ptr(lf, *mdp); + mp = *mdp; if (mp->md_type != MDT_DEPEND) continue; - linker_mdt_depend(lf, mp, &modname, &verinfo); + modname = mp->md_cval; + verinfo = mp->md_data; mod = modlist_lookup2(modname, verinfo); mod->container->refs++; error = linker_file_add_dependency(lf, @@ -1763,10 +1717,11 @@ linker_load_dependencies(linker_file_t lf) &count) != 0) return (0); for (mdp = start; mdp < stop; mdp++) { - mp = linker_reloc_ptr(lf, *mdp); + mp = *mdp; if (mp->md_type != MDT_VERSION) continue; - linker_mdt_version(lf, mp, &modname, &ver); + modname = mp->md_cval; + ver = ((struct mod_version *)mp->md_data)->mv_version; mod = modlist_lookup(modname, ver); if (mod != NULL) { printf("interface %s.%d already present in the KLD" @@ -1777,16 +1732,17 @@ linker_load_dependencies(linker_file_t lf) } for (mdp = start; mdp < stop; mdp++) { - mp = linker_reloc_ptr(lf, *mdp); + mp = *mdp; if (mp->md_type != MDT_DEPEND) continue; - linker_mdt_depend(lf, mp, &modname, &verinfo); + modname = mp->md_cval; + verinfo = mp->md_data; nmodname = NULL; for (nmdp = start; nmdp < stop; nmdp++) { - nmp = linker_reloc_ptr(lf, *nmdp); + nmp = *nmdp; if (nmp->md_type != MDT_VERSION) continue; - nmodname = linker_reloc_ptr(lf, nmp->md_cval); + nmodname = nmp->md_cval; if (strcmp(modname, nmodname) == 0) break; } diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c index 3e5224e..f15803f 100644 --- a/sys/kern/link_elf.c +++ b/sys/kern/link_elf.c @@ -116,6 +116,7 @@ static int link_elf_lookup_set(linker_file_t, const char *, static int link_elf_each_function_name(linker_file_t, int (*)(const char *, void *), void *); +static void link_elf_reloc_local(linker_file_t); static kobj_method_t link_elf_methods[] = { KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol), @@ -488,6 +489,7 @@ link_elf_link_preload(linker_class_t cls, linker_file_unload(lf); return error; } + link_elf_reloc_local(lf); *result = lf; return (0); } @@ -748,6 +750,8 @@ link_elf_load_file(linker_class_t cls, const char* filename, error = parse_dynamic(ef); if (error) goto out; + link_elf_reloc_local(lf); + error = linker_load_dependencies(lf); if (error) goto out; @@ -1266,3 +1270,31 @@ elf_lookup(linker_file_t lf, Elf_Word symidx, int deps) return ((Elf_Addr)linker_file_lookup_symbol(lf, symbol, deps)); } + +static void +link_elf_reloc_local(linker_file_t lf) +{ + const Elf_Rel *rellim; + const Elf_Rel *rel; + const Elf_Rela *relalim; + const Elf_Rela *rela; + elf_file_t ef = (elf_file_t)lf; + + /* Perform relocations without addend if there are any: */ + if ((rel = ef->rel) != NULL) { + rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize); + while (rel < rellim) { + elf_reloc_local(lf, rel, ELF_RELOC_REL); + rel++; + } + } + + /* Perform relocations with addend if there are any: */ + if ((rela = ef->rela) != NULL) { + relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize); + while (rela < relalim) { + elf_reloc_local(lf, rela, ELF_RELOC_RELA); + rela++; + } + } +} diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c index 3e5224e..f15803f 100644 --- a/sys/kern/link_elf_obj.c +++ b/sys/kern/link_elf_obj.c @@ -116,6 +116,7 @@ static int link_elf_lookup_set(linker_file_t, const char *, static int link_elf_each_function_name(linker_file_t, int (*)(const char *, void *), void *); +static void link_elf_reloc_local(linker_file_t); static kobj_method_t link_elf_methods[] = { KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol), @@ -488,6 +489,7 @@ link_elf_link_preload(linker_class_t cls, linker_file_unload(lf); return error; } + link_elf_reloc_local(lf); *result = lf; return (0); } @@ -748,6 +750,8 @@ link_elf_load_file(linker_class_t cls, const char* filename, error = parse_dynamic(ef); if (error) goto out; + link_elf_reloc_local(lf); + error = linker_load_dependencies(lf); if (error) goto out; @@ -1266,3 +1270,31 @@ elf_lookup(linker_file_t lf, Elf_Word symidx, int deps) return ((Elf_Addr)linker_file_lookup_symbol(lf, symbol, deps)); } + +static void +link_elf_reloc_local(linker_file_t lf) +{ + const Elf_Rel *rellim; + const Elf_Rel *rel; + const Elf_Rela *relalim; + const Elf_Rela *rela; + elf_file_t ef = (elf_file_t)lf; + + /* Perform relocations without addend if there are any: */ + if ((rel = ef->rel) != NULL) { + rellim = (const Elf_Rel *)((const char *)ef->rel + ef->relsize); + while (rel < rellim) { + elf_reloc_local(lf, rel, ELF_RELOC_REL); + rel++; + } + } + + /* Perform relocations with addend if there are any: */ + if ((rela = ef->rela) != NULL) { + relalim = (const Elf_Rela *)((const char *)ef->rela + ef->relasize); + while (rela < relalim) { + elf_reloc_local(lf, rela, ELF_RELOC_RELA); + rela++; + } + } +} diff --git a/sys/powerpc/powerpc/elf_machdep.c b/sys/powerpc/powerpc/elf_machdep.c index e00c9d3..1621e1b 100644 --- a/sys/powerpc/powerpc/elf_machdep.c +++ b/sys/powerpc/powerpc/elf_machdep.c @@ -89,8 +89,8 @@ SYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_ANY, &freebsd_brand_info); /* Process one elf relocation with addend. */ -int -elf_reloc(linker_file_t lf, const void *data, int type) +static int +elf_reloc_internal(linker_file_t lf, const void *data, int type, int local) { Elf_Addr relocbase = (Elf_Addr) lf->address; Elf_Addr *where; @@ -165,6 +165,20 @@ elf_reloc(linker_file_t lf, const void *data, int type) } int +elf_reloc(linker_file_t lf, const void *data, int type) +{ + + return (elf_reloc_internal(lf, data, type, 0)); +} + +int +elf_reloc_local(linker_file_t lf, const void *data, int type) +{ + + return (elf_reloc_internal(lf, data, type, 1)); +} + +int elf_cpu_load_file(linker_file_t lf __unused) { diff --git a/sys/sparc64/sparc64/elf_machdep.c b/sys/sparc64/sparc64/elf_machdep.c index 592be48..a14db6c 100644 --- a/sys/sparc64/sparc64/elf_machdep.c +++ b/sys/sparc64/sparc64/elf_machdep.c @@ -235,6 +235,28 @@ static long reloc_target_bitmask[] = { }; #define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t]) +int +elf_reloc_local(linker_file_t lf, const void *data, int type) +{ + const Elf_Rela *rela; + Elf_Addr value; + Elf_Addr *where; + + if (type != ELF_RELOC_RELA) + return (-1); + + rela = (const Elf_Rela *)data; + if (ELF_R_TYPE(rela->r_info) != R_SPARC_RELATIVE) + return (-1); + + value = rela->r_addend + (Elf_Addr)lf->address; + where = (Elf_Addr *)((Elf_Addr)lf->address + rela->r_offset); + + *where = value; + + return (0); +} + /* Process one elf relocation with addend. */ int elf_reloc(linker_file_t lf, const void *data, int type) @@ -258,7 +280,7 @@ elf_reloc(linker_file_t lf, const void *data, int type) rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); - if (rtype == R_SPARC_NONE) + if (rtype == R_SPARC_NONE || rtype == R_SPARC_RELATIVE) return (0); if (rtype == R_SPARC_JMP_SLOT || rtype == R_SPARC_COPY || diff --git a/sys/sys/linker.h b/sys/sys/linker.h index 5e0a760..a601e3a 100644 --- a/sys/sys/linker.h +++ b/sys/sys/linker.h @@ -229,6 +229,7 @@ extern int kld_debug; /* Support functions */ int elf_reloc(linker_file_t _lf, const void *_rel, int _type); +int elf_reloc_local(linker_file_t _lf, const void *_rel, int _type); Elf_Addr elf_lookup(linker_file_t, Elf_Word, int); const Elf_Sym *elf_get_sym(linker_file_t _lf, Elf_Word _symidx); const char *elf_get_symname(linker_file_t _lf, Elf_Word _symidx); |