The branch main has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=aefae931820fe1e93a318552968510298c7941a0
commit aefae931820fe1e93a318552968510298c7941a0 Author: Mark Johnston <ma...@freebsd.org> AuthorDate: 2025-07-02 13:34:47 +0000 Commit: Mark Johnston <ma...@freebsd.org> CommitDate: 2025-07-02 13:34:47 +0000 linker: Improve handling of ifuncs when fetching symbol metadata When looking up symbol values, we map ifunc symbols to the value returned by the resolver. However, the returned symbol size is still that of the resolver. Be consistent and provide the size of the implementation symbol as well. This fixes an inconsistency in dtrace's FBT provider, which enumerates all function symbols and disassembles their values, using the symbol size as the bound for the disassembly loop. In particular, for ifuncs, we were not creating return probes. Reviewed by: kib MFC after: 2 weeks Sponsored by: Innovate UK Differential Revision: https://reviews.freebsd.org/D50683 --- sys/kern/link_elf.c | 38 ++++++++++++++++++++++++++++++++++---- sys/kern/link_elf_obj.c | 31 +++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c index 53af1e164980..bbebadc4c395 100644 --- a/sys/kern/link_elf.c +++ b/sys/kern/link_elf.c @@ -1628,6 +1628,30 @@ link_elf_lookup_debug_symbol_ctf(linker_file_t lf, const char *name, 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 @@ link_elf_symbol_values1(linker_file_t lf, c_linker_sym_t sym, 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 @@ link_elf_symbol_values1(linker_file_t lf, c_linker_sym_t sym, 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 @@ link_elf_debug_symbol_values(linker_file_t lf, c_linker_sym_t sym, 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 @@ link_elf_debug_symbol_values(linker_file_t lf, c_linker_sym_t sym, 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 index 02fd4caffcd9..3d18aed2b1c0 100644 --- a/sys/kern/link_elf_obj.c +++ b/sys/kern/link_elf_obj.c @@ -1510,6 +1510,30 @@ link_elf_lookup_debug_symbol_ctf(linker_file_t lf, const char *name, 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 @@ link_elf_symbol_values1(linker_file_t lf, c_linker_sym_t sym, 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 @@ link_elf_symbol_values1(linker_file_t lf, c_linker_sym_t sym, 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);