Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/link_elf.c
Show First 20 Lines • Show All 184 Lines • ▼ Show 20 Lines | |||||
#else | #else | ||||
"elf64", | "elf64", | ||||
#endif | #endif | ||||
link_elf_methods, sizeof(struct elf_file) | link_elf_methods, sizeof(struct elf_file) | ||||
}; | }; | ||||
static int parse_dynamic(elf_file_t); | static int parse_dynamic(elf_file_t); | ||||
static int relocate_file(elf_file_t); | static int relocate_file(elf_file_t); | ||||
static int relocate_file1(elf_file_t ef, int (*elf_reloc_func)( | |||||
linker_file_t lf, Elf_Addr relocbase, const void *data, | |||||
int type, elf_lookup_fn lookup)); | |||||
static int link_elf_preload_parse_symbols(elf_file_t); | static int link_elf_preload_parse_symbols(elf_file_t); | ||||
static struct elf_set_head set_pcpu_list; | static struct elf_set_head set_pcpu_list; | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
static struct elf_set_head set_vnet_list; | static struct elf_set_head set_vnet_list; | ||||
#endif | #endif | ||||
static void | static void | ||||
▲ Show 20 Lines • Show All 971 Lines • ▼ Show 20 Lines | symbol_name(elf_file_t ef, Elf_Size r_info) | ||||
if (ELF_R_SYM(r_info)) { | if (ELF_R_SYM(r_info)) { | ||||
ref = ef->symtab + ELF_R_SYM(r_info); | ref = ef->symtab + ELF_R_SYM(r_info); | ||||
return (ef->strtab + ref->st_name); | return (ef->strtab + ref->st_name); | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
static int | static int | ||||
relocate_file(elf_file_t ef) | relocate_file1(elf_file_t ef, int (*elf_reloc_func)(linker_file_t lf, | ||||
Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup)) | |||||
{ | { | ||||
const Elf_Rel *rellim; | const Elf_Rel *rellim; | ||||
const Elf_Rel *rel; | const Elf_Rel *rel; | ||||
const Elf_Rela *relalim; | const Elf_Rela *relalim; | ||||
const Elf_Rela *rela; | const Elf_Rela *rela; | ||||
const char *symname; | const char *symname; | ||||
/* Perform relocations without addend if there are any: */ | /* Perform relocations without addend if there are any: */ | ||||
rel = ef->rel; | rel = ef->rel; | ||||
if (rel != NULL) { | if (rel != NULL) { | ||||
rellim = (const Elf_Rel *) | rellim = (const Elf_Rel *) | ||||
((const char *)ef->rel + ef->relsize); | ((const char *)ef->rel + ef->relsize); | ||||
while (rel < rellim) { | while (rel < rellim) { | ||||
if (elf_reloc(&ef->lf, (Elf_Addr)ef->address, rel, | if (elf_reloc_func(&ef->lf, (Elf_Addr)ef->address, rel, | ||||
ELF_RELOC_REL, elf_lookup)) { | ELF_RELOC_REL, elf_lookup)) { | ||||
symname = symbol_name(ef, rel->r_info); | symname = symbol_name(ef, rel->r_info); | ||||
printf("link_elf: symbol %s undefined\n", symname); | printf("link_elf: symbol %s undefined\n", symname); | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
rel++; | rel++; | ||||
} | } | ||||
} | } | ||||
/* Perform relocations with addend if there are any: */ | /* Perform relocations with addend if there are any: */ | ||||
rela = ef->rela; | rela = ef->rela; | ||||
if (rela != NULL) { | if (rela != NULL) { | ||||
relalim = (const Elf_Rela *) | relalim = (const Elf_Rela *) | ||||
((const char *)ef->rela + ef->relasize); | ((const char *)ef->rela + ef->relasize); | ||||
while (rela < relalim) { | while (rela < relalim) { | ||||
if (elf_reloc(&ef->lf, (Elf_Addr)ef->address, rela, | if (elf_reloc_func(&ef->lf, (Elf_Addr)ef->address, rela, | ||||
ELF_RELOC_RELA, elf_lookup)) { | ELF_RELOC_RELA, elf_lookup)) { | ||||
symname = symbol_name(ef, rela->r_info); | symname = symbol_name(ef, rela->r_info); | ||||
printf("link_elf: symbol %s undefined\n", | printf("link_elf: symbol %s undefined\n", | ||||
symname); | symname); | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
rela++; | rela++; | ||||
} | } | ||||
} | } | ||||
/* Perform PLT relocations without addend if there are any: */ | /* Perform PLT relocations without addend if there are any: */ | ||||
rel = ef->pltrel; | rel = ef->pltrel; | ||||
if (rel != NULL) { | if (rel != NULL) { | ||||
rellim = (const Elf_Rel *) | rellim = (const Elf_Rel *) | ||||
((const char *)ef->pltrel + ef->pltrelsize); | ((const char *)ef->pltrel + ef->pltrelsize); | ||||
while (rel < rellim) { | while (rel < rellim) { | ||||
if (elf_reloc(&ef->lf, (Elf_Addr)ef->address, rel, | if (elf_reloc_func(&ef->lf, (Elf_Addr)ef->address, rel, | ||||
ELF_RELOC_REL, elf_lookup)) { | ELF_RELOC_REL, elf_lookup)) { | ||||
symname = symbol_name(ef, rel->r_info); | symname = symbol_name(ef, rel->r_info); | ||||
printf("link_elf: symbol %s undefined\n", | printf("link_elf: symbol %s undefined\n", | ||||
symname); | symname); | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
rel++; | rel++; | ||||
} | } | ||||
} | } | ||||
/* Perform relocations with addend if there are any: */ | /* Perform relocations with addend if there are any: */ | ||||
rela = ef->pltrela; | rela = ef->pltrela; | ||||
if (rela != NULL) { | if (rela != NULL) { | ||||
relalim = (const Elf_Rela *) | relalim = (const Elf_Rela *) | ||||
((const char *)ef->pltrela + ef->pltrelasize); | ((const char *)ef->pltrela + ef->pltrelasize); | ||||
while (rela < relalim) { | while (rela < relalim) { | ||||
if (elf_reloc(&ef->lf, (Elf_Addr)ef->address, rela, | if (elf_reloc_func(&ef->lf, (Elf_Addr)ef->address, rela, | ||||
ELF_RELOC_RELA, elf_lookup)) { | ELF_RELOC_RELA, elf_lookup)) { | ||||
symname = symbol_name(ef, rela->r_info); | symname = symbol_name(ef, rela->r_info); | ||||
printf("link_elf: symbol %s undefined\n", | printf("link_elf: symbol %s undefined\n", | ||||
symname); | symname); | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
rela++; | rela++; | ||||
} | } | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | |||||
relocate_file(elf_file_t ef) | |||||
{ | |||||
int e; | |||||
e = relocate_file1(ef, elf_reloc); | |||||
if (e == 0) | |||||
e = relocate_file1(ef, elf_reloc_ifunc); | |||||
return (e); | |||||
} | |||||
/* | /* | ||||
* Hash function for symbol table lookup. Don't even think about changing | * Hash function for symbol table lookup. Don't even think about changing | ||||
* this. It is specified by the System V ABI. | * this. It is specified by the System V ABI. | ||||
*/ | */ | ||||
static unsigned long | static unsigned long | ||||
elf_hash(const char *name) | elf_hash(const char *name) | ||||
{ | { | ||||
const unsigned char *p = (const unsigned char *) name; | const unsigned char *p = (const unsigned char *) name; | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | if (symp->st_name == 0) { | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
strp = ef->strtab + symp->st_name; | strp = ef->strtab + symp->st_name; | ||||
if (strcmp(name, strp) == 0) { | if (strcmp(name, strp) == 0) { | ||||
if (symp->st_shndx != SHN_UNDEF || | if (symp->st_shndx != SHN_UNDEF || | ||||
(symp->st_value != 0 && | (symp->st_value != 0 && | ||||
ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { | (ELF_ST_TYPE(symp->st_info) == STT_FUNC || | ||||
ELF_ST_TYPE(symp->st_info) == STT_GNU_IFUNC))) { | |||||
*sym = (c_linker_sym_t) symp; | *sym = (c_linker_sym_t) symp; | ||||
return (0); | return (0); | ||||
} | } | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
symnum = ef->chains[symnum]; | symnum = ef->chains[symnum]; | ||||
} | } | ||||
/* If we have not found it, look at the full table (if loaded) */ | /* If we have not found it, look at the full table (if loaded) */ | ||||
if (ef->symtab == ef->ddbsymtab) | if (ef->symtab == ef->ddbsymtab) | ||||
return (ENOENT); | return (ENOENT); | ||||
/* Exhaustive search */ | /* Exhaustive search */ | ||||
for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { | for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { | ||||
strp = ef->ddbstrtab + symp->st_name; | strp = ef->ddbstrtab + symp->st_name; | ||||
if (strcmp(name, strp) == 0) { | if (strcmp(name, strp) == 0) { | ||||
if (symp->st_shndx != SHN_UNDEF || | if (symp->st_shndx != SHN_UNDEF || | ||||
(symp->st_value != 0 && | (symp->st_value != 0 && | ||||
ELF_ST_TYPE(symp->st_info) == STT_FUNC)) { | (ELF_ST_TYPE(symp->st_info) == STT_FUNC || | ||||
ELF_ST_TYPE(symp->st_info) == STT_GNU_IFUNC))) { | |||||
*sym = (c_linker_sym_t) symp; | *sym = (c_linker_sym_t) symp; | ||||
return (0); | return (0); | ||||
} | } | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
} | } | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
static int | static int | ||||
link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, | link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, | ||||
linker_symval_t *symval) | linker_symval_t *symval) | ||||
{ | { | ||||
elf_file_t ef = (elf_file_t) lf; | elf_file_t ef; | ||||
const Elf_Sym* es = (const Elf_Sym*) sym; | const Elf_Sym *es; | ||||
caddr_t val; | |||||
ef = (elf_file_t)lf; | |||||
es = (const Elf_Sym *)sym; | |||||
if (es >= ef->symtab && es < (ef->symtab + ef->nchains)) { | if (es >= ef->symtab && es < (ef->symtab + ef->nchains)) { | ||||
symval->name = ef->strtab + es->st_name; | symval->name = ef->strtab + es->st_name; | ||||
symval->value = (caddr_t) ef->address + es->st_value; | val = (caddr_t)ef->address + es->st_value; | ||||
if (ELF_ST_TYPE(es->st_info) == STT_GNU_IFUNC) | |||||
val = ((caddr_t (*)(void))val)(); | |||||
symval->value = val; | |||||
symval->size = es->st_size; | symval->size = es->st_size; | ||||
return (0); | return (0); | ||||
} | } | ||||
if (ef->symtab == ef->ddbsymtab) | if (ef->symtab == ef->ddbsymtab) | ||||
return (ENOENT); | return (ENOENT); | ||||
if (es >= ef->ddbsymtab && es < (ef->ddbsymtab + ef->ddbsymcnt)) { | if (es >= ef->ddbsymtab && es < (ef->ddbsymtab + ef->ddbsymcnt)) { | ||||
symval->name = ef->ddbstrtab + es->st_name; | symval->name = ef->ddbstrtab + es->st_name; | ||||
symval->value = (caddr_t) ef->address + es->st_value; | val = (caddr_t)ef->address + es->st_value; | ||||
if (ELF_ST_TYPE(es->st_info) == STT_GNU_IFUNC) | |||||
val = ((caddr_t (*)(void))val)(); | |||||
symval->value = val; | |||||
symval->size = es->st_size; | symval->size = es->st_size; | ||||
return (0); | return (0); | ||||
} | } | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
static int | static int | ||||
link_elf_search_symbol(linker_file_t lf, caddr_t value, | link_elf_search_symbol(linker_file_t lf, caddr_t value, | ||||
▲ Show 20 Lines • Show All 93 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
elf_file_t ef = (elf_file_t)file; | elf_file_t ef = (elf_file_t)file; | ||||
const Elf_Sym *symp; | const Elf_Sym *symp; | ||||
int i, error; | int i, error; | ||||
/* Exhaustive search */ | /* Exhaustive search */ | ||||
for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { | for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { | ||||
if (symp->st_value != 0 && | if (symp->st_value != 0 && | ||||
ELF_ST_TYPE(symp->st_info) == STT_FUNC) { | (ELF_ST_TYPE(symp->st_info) == STT_FUNC || | ||||
ELF_ST_TYPE(symp->st_info) == STT_GNU_IFUNC)) { | |||||
error = callback(ef->ddbstrtab + symp->st_name, opaque); | error = callback(ef->ddbstrtab + symp->st_name, opaque); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
} | } | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
link_elf_each_function_nameval(linker_file_t file, | link_elf_each_function_nameval(linker_file_t file, | ||||
linker_function_nameval_callback_t callback, void *opaque) | linker_function_nameval_callback_t callback, void *opaque) | ||||
{ | { | ||||
linker_symval_t symval; | linker_symval_t symval; | ||||
elf_file_t ef = (elf_file_t)file; | elf_file_t ef = (elf_file_t)file; | ||||
const Elf_Sym* symp; | const Elf_Sym* symp; | ||||
int i, error; | int i, error; | ||||
/* Exhaustive search */ | /* Exhaustive search */ | ||||
for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { | for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { | ||||
if (symp->st_value != 0 && | if (symp->st_value != 0 && | ||||
ELF_ST_TYPE(symp->st_info) == STT_FUNC) { | (ELF_ST_TYPE(symp->st_info) == STT_FUNC || | ||||
ELF_ST_TYPE(symp->st_info) == STT_GNU_IFUNC)) { | |||||
error = link_elf_symbol_values(file, | error = link_elf_symbol_values(file, | ||||
(c_linker_sym_t) symp, &symval); | (c_linker_sym_t) symp, &symval); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
error = callback(file, i, &symval, opaque); | error = callback(file, i, &symval, opaque); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 141 Lines • ▼ Show 20 Lines | link_elf_strtab_get(linker_file_t lf, caddr_t *strtab) | ||||
elf_file_t ef = (elf_file_t)lf; | elf_file_t ef = (elf_file_t)lf; | ||||
*strtab = ef->ddbstrtab; | *strtab = ef->ddbstrtab; | ||||
if (*strtab == NULL) | if (*strtab == NULL) | ||||
return (0); | return (0); | ||||
return (ef->ddbstrcnt); | return (ef->ddbstrcnt); | ||||
} | |||||
void | |||||
link_elf_ireloc(caddr_t kmdp) | |||||
{ | |||||
struct elf_file eff; | |||||
elf_file_t ef; | |||||
ef = &eff; | |||||
bzero(ef, sizeof(*ef)); | |||||
ef->modptr = kmdp; | |||||
ef->dynamic = (Elf_Dyn *)&_DYNAMIC; | |||||
parse_dynamic(ef); | |||||
ef->address = 0; | |||||
link_elf_preload_parse_symbols(ef); | |||||
relocate_file1(ef, elf_reloc_ifunc); | |||||
} | } |