diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c --- a/sys/kern/link_elf.c +++ b/sys/kern/link_elf.c @@ -1628,6 +1628,30 @@ return (i < ef->ddbsymcnt ? link_elf_ctf_get_ddb(lf, lc) : ENOENT); } +static void +link_elf_ifunc_symbol_value(linker_file_t lf, caddr_t *valp, size_t *sizep) +{ + c_linker_sym_t sym; + elf_file_t ef; + const Elf_Sym *es; + caddr_t val; + long off; + + val = *valp; + ef = (elf_file_t)lf; + + /* Provide the value and size of the target symbol, if available. */ + val = ((caddr_t (*)(void))val)(); + if (link_elf_search_symbol(lf, val, &sym, &off) == 0 && off == 0) { + es = (const Elf_Sym *)sym; + *valp = (caddr_t)ef->address + es->st_value; + *sizep = es->st_size; + } else { + *valp = val; + *sizep = 0; + } +} + static int link_elf_symbol_values1(linker_file_t lf, c_linker_sym_t sym, linker_symval_t *symval, bool see_local) @@ -1635,6 +1659,7 @@ elf_file_t ef; const Elf_Sym *es; caddr_t val; + size_t size; ef = (elf_file_t)lf; es = (const Elf_Sym *)sym; @@ -1644,9 +1669,11 @@ symval->name = ef->strtab + es->st_name; val = (caddr_t)ef->address + es->st_value; if (ELF_ST_TYPE(es->st_info) == STT_GNU_IFUNC) - val = ((caddr_t (*)(void))val)(); + link_elf_ifunc_symbol_value(lf, &val, &size); + else + size = es->st_size; symval->value = val; - symval->size = es->st_size; + symval->size = size; return (0); } return (ENOENT); @@ -1668,6 +1695,7 @@ elf_file_t ef = (elf_file_t)lf; const Elf_Sym *es = (const Elf_Sym *)sym; caddr_t val; + size_t size; if (link_elf_symbol_values1(lf, sym, symval, true) == 0) return (0); @@ -1678,9 +1706,11 @@ symval->name = ef->ddbstrtab + es->st_name; val = (caddr_t)ef->address + es->st_value; if (ELF_ST_TYPE(es->st_info) == STT_GNU_IFUNC) - val = ((caddr_t (*)(void))val)(); + link_elf_ifunc_symbol_value(lf, &val, &size); + else + size = es->st_size; symval->value = val; - symval->size = es->st_size; + symval->size = size; return (0); } return (ENOENT); diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c --- a/sys/kern/link_elf_obj.c +++ b/sys/kern/link_elf_obj.c @@ -1510,6 +1510,30 @@ return (link_elf_ctf_get_ddb(lf, lc)); } +static void +link_elf_ifunc_symbol_value(linker_file_t lf, caddr_t *valp, size_t *sizep) +{ + c_linker_sym_t sym; + elf_file_t ef; + const Elf_Sym *es; + caddr_t val; + long off; + + val = *valp; + ef = (elf_file_t)lf; + + /* Provide the value and size of the target symbol, if available. */ + val = ((caddr_t (*)(void))val)(); + if (link_elf_search_symbol(lf, val, &sym, &off) == 0 && off == 0) { + es = (const Elf_Sym *)sym; + *valp = (caddr_t)ef->address + es->st_value; + *sizep = es->st_size; + } else { + *valp = val; + *sizep = 0; + } +} + static int link_elf_symbol_values1(linker_file_t lf, c_linker_sym_t sym, linker_symval_t *symval, bool see_local) @@ -1517,6 +1541,7 @@ elf_file_t ef; const Elf_Sym *es; caddr_t val; + size_t size; ef = (elf_file_t) lf; es = (const Elf_Sym*) sym; @@ -1527,9 +1552,11 @@ symval->name = ef->ddbstrtab + es->st_name; val = (caddr_t)es->st_value; if (ELF_ST_TYPE(es->st_info) == STT_GNU_IFUNC) - val = ((caddr_t (*)(void))val)(); + link_elf_ifunc_symbol_value(lf, &val, &size); + else + size = es->st_size; symval->value = val; - symval->size = es->st_size; + symval->size = size; return (0); } return (ENOENT);