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

Reply via email to