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
This commit is contained in:
Mark Johnston 2025-07-02 13:34:47 +00:00
parent c9fcffff6c
commit aefae93182
2 changed files with 63 additions and 6 deletions

View file

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

View file

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