git: aefae931820f - main - linker: Improve handling of ifuncs when fetching symbol metadata
Date: Wed, 02 Jul 2025 13:41:56 UTC
The branch main has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=aefae931820fe1e93a318552968510298c7941a0
commit aefae931820fe1e93a318552968510298c7941a0
Author: Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2025-07-02 13:34:47 +0000
Commit: Mark Johnston <markj@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);