From: Kris Van Hees <kris.van.hees@oracle.com>
To: Alan Maguire <alan.maguire@oracle.com>
Cc: Kris Van Hees <kris.van.hees@oracle.com>,
dtrace@lists.linux.dev, OpenAI Codex <codex@openai.com>,
dtrace-devel@oss.oracle.com
Subject: Re: [DTrace-devel] [PATCH 4/6] dtrace: Add uresolve(symbol_name[, optional_fallback]) for symbol lookup
Date: Mon, 8 Jun 2026 16:54:23 -0400 [thread overview]
Message-ID: <aicr_xS0ZiXkCKdO@oracle.com> (raw)
In-Reply-To: <e8a1fcd4-1823-43b8-8df4-5188ed0a7dc0@oracle.com>
On Mon, Jun 08, 2026 at 09:38:52PM +0100, Alan Maguire wrote:
> On 08/06/2026 21:27, Kris Van Hees wrote:
> > On Mon, Jun 08, 2026 at 11:43:47AM +0100, Alan Maguire wrote:
> >> On 06/06/2026 02:08, Kris Van Hees wrote:
> >>> A few initial comments:
> >>>
> >>> - How is this different from what ``symbol or object``symbol would provide?
> >>> (Yes, as far as I know it never got fully implemented but I think this might
> >>> be similar to what you are doing, and perhaps it would be better to use the
> >>> already documented syntax, and actually implement it more fully.)
> >>>
> >>
> >> this is a good idea; I've rebuilt using the existing `` syntax, and it works
> >> well. My only reservation was losing the fallback behaviour that uresolve() provided;
> >> however I've implemented that with an option -xuresolvefail=zero which zeros out the
> >> results of address resolution to allow scripts to handle missing symbols.
> >
> > Hm, but the <module>``<symbol> resolving is done at compile time, so there
> > should be no need to have a fallback - if the symbol cannot be resolved, then
> > the script shouldn't be able to work. This is similar to how kernel symbols
> > that do not exist cause <module>`<symbol> to fail to resolve and thus the
> > script won't compile.
> >
> > Why would you end up trying to use a script using a userspace symbol without
> > knowing whether the symbol exists?
> >
>
> Because in some cases (like libpython) the variables of interest change over
> time from version to version. Having a way to handle multiple versions allows
> us to create more portable scripts that are less tied to a specific version
> and can behave differently based on presence/absence of variables.
I think this would be better handled with #ifdef or some similar preprocessor
conditional than causing the compiler to accept an unknown symbol and giving it
a 0 value.
This may need a bit more thinking about... I just don't like the notion of a
script using a userspace symbol, and having the code effectively act as if the
symbol does exist when it does not. It feels like there ought to be a better
way than this, since it feels rather kludgy.
> >> One other change below relating to your next question..
> >>
> >>> - I think it is problematic that the cg code is making calls to a function in
> >>> a particular provider. THe architecture has been quite careful at avoiding
> >>> breaking through that boundary. I'm still digging a bit more through the
> >>> code to determine exactly why that call is truly needed - it should not be
> >>> necessary as far as I can see.
> >>>
> >>
> >> We can fix that as well; it is reasonable to expect in systemwide probe context
> >> that a user qualify their symbol with the object (with the proviso that we
> >> support path expansion as we do for the probemod). This requires a few minor
> >> lexical changes to support filenames with "." in them, but it makes sense and
> >> avoids us having to make inferences from the probe context. So in other words
> >> to retrieve a global variable from libpython during systemwide probing we'd have
> >
> > Yes, the module name needs to allow for . characters, per the module naming
> > rules in DTrace.
> >
> >> python*:libpython3.6m.so::function-entry
> >> {
> >> this->cur = copyin((uintptr_t)&libpython3.6m.so``_PyThreadState_Current, sizeof(void *));
> >> ..
> >> }
> >>
> >>> Resolution of a userspace symbol should essentially be a simple matter of:
> >>>
> >>> object-base-addr + symbol-offset
> >>>
> >>> where the object is a particular mapping within the address space of the
> >>> current user process.
> >>>
> >>> On Fri, Jun 05, 2026 at 11:12:15PM +0100, Alan Maguire via DTrace-devel wrote:
> >>>> Add uresolve(symbol[, fallback]) to resolve user-space symbols from D
> >>>> scripts. uresolve() resolves a symbol to an object-relative offset at
> >>>> compile time, then computes the process-specific runtime address at probe
> >>>> fire time as:
> >>>>
> >>>> current uprobe IP - probe offset + symbol offset
> >>>>
> >>>> For pid$target probes, use the target process and its /proc/<pid>/maps state
> >>>> to find the symbol and translate it to the same object-relative coordinate
> >>>> used by uprobes. For system-wide uprobes, resolve the offset from the probed
> >>>> object file. In both cases, address generation is shared and remains dynamic;
> >>>> the presence of a target process does not bake in an absolute address.
> >>>>
> >>>> Use bpf_get_func_ip() when available so system-wide uprobes use the actual
> >>>> probed instruction address rather than a saved pt_regs IP that may contain
> >>>> the resume address. Retain the saved-IP path as a fallback.
> >>>>
> >>>> This allows scripts to locate process-local data such as
> >>>> _PyThreadState_Current across ASLR and across system-wide probes firing in
> >>>> different processes. The optional fallback argument is returned when the
> >>>> symbol cannot be resolved; without it, resolution failure remains a
> >>>> compile-time error.
> >>>>
> >>>> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> >>>> Assisted-by: OpenAI Codex (GPT-5) <codex@openai.com>
> >>>> ---
> >>>> include/dtrace/dif_defines.h | 3 +-
> >>>> libdtrace/dt_bpf.c | 1 +
> >>>> libdtrace/dt_cg.c | 456 +++++++++++++++++++++++++++++++++++
> >>>> libdtrace/dt_open.c | 2 +
> >>>> libdtrace/dt_probe.h | 1 +
> >>>> libdtrace/dt_prov_uprobe.c | 58 ++++-
> >>>> 6 files changed, 516 insertions(+), 5 deletions(-)
> >>>>
> >>>> diff --git a/include/dtrace/dif_defines.h b/include/dtrace/dif_defines.h
> >>>> index edb98f06..63b8c293 100644
> >>>> --- a/include/dtrace/dif_defines.h
> >>>> +++ b/include/dtrace/dif_defines.h
> >>>> @@ -213,8 +213,9 @@
> >>>> #define DIF_SUBR_LINK_NTOP 45
> >>>> #define DIF_SUBR_STACK 46
> >>>> #define DIF_SUBR_USTACK 47
> >>>> +#define DIF_SUBR_URESOLVE 48
> >>>>
> >>>> -#define DIF_SUBR_MAX 47
> >>>> +#define DIF_SUBR_MAX 48
> >>>>
> >>>> typedef uint32_t dif_instr_t;
> >>>>
> >>>> diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c
> >>>> index 146a66e3..aecd71d9 100644
> >>>> --- a/libdtrace/dt_bpf.c
> >>>> +++ b/libdtrace/dt_bpf.c
> >>>> @@ -470,6 +470,7 @@ dt_bpf_init_helpers(dtrace_hdl_t *dtp)
> >>>> BPF_HELPER_MAP(probe_read_user_str, probe_read_str);
> >>>> BPF_HELPER_MAP(probe_read_kernel, probe_read);
> >>>> BPF_HELPER_MAP(probe_read_kernel_str, probe_read_str);
> >>>> + BPF_HELPER_MAP(get_func_ip, unspec);
> >>>> BPF_HELPER_MAP(get_current_task_btf, unspec);
> >>>> BPF_HELPER_MAP(task_pt_regs, unspec);
> >>>> #undef BPF_HELPER_MAP
> >>>> diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
> >>>> index 1bd73e11..16c58e35 100644
> >>>> --- a/libdtrace/dt_cg.c
> >>>> +++ b/libdtrace/dt_cg.c
> >>>> @@ -11,6 +11,9 @@
> >>>> #include <setjmp.h>
> >>>> #include <assert.h>
> >>>> #include <errno.h>
> >>>> +#include <ctype.h>
> >>>> +#include <fcntl.h>
> >>>> +#include <unistd.h>
> >>>>
> >>>> #include <sys/socket.h> /* needed for if_arp.h on OL7/x86 */
> >>>> #include <linux/if_arp.h> /* ARPHRD_ETHER ARPHRD_INFINIBAND */
> >>>> @@ -6988,6 +6991,458 @@ dt_cg_subr_link_ntop(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
> >>>> dt_regset_free(drp, type->dn_reg);
> >>>> }
> >>>>
> >>>> +static const char *
> >>>> +dt_cg_basename(const char *path)
> >>>> +{
> >>>> + const char *p;
> >>>> +
> >>>> + p = strrchr(path, '/');
> >>>> + return p != NULL ? p + 1 : path;
> >>>> +}
> >>>> +
> >>>> +static int
> >>>> +dt_cg_parse_objname(const char *mod, Lmid_t *lmidp, const char **objp)
> >>>> +{
> >>>> + char *end;
> >>>> +
> >>>> + if (mod == NULL || mod[0] == '\0' || strisglob(mod))
> >>>> + return -1;
> >>>> +
> >>>> + if (strncmp(mod, "LM", 2) == 0 && isdigit((unsigned char)mod[2])) {
> >>>> + *lmidp = strtoul(mod + 2, &end, 16);
> >>>> + if (*end != '`' || end[1] == '\0' || strchr(end + 1, '`'))
> >>>> + return -1;
> >>>> +
> >>>> + *objp = end + 1;
> >>>> + return 0;
> >>>> + }
> >>>> +
> >>>> + *lmidp = PR_LMID_EVERY;
> >>>> + *objp = mod;
> >>>> + return 0;
> >>>> +}
> >>>> +
> >>>> +static int
> >>>> +dt_cg_probe_objname(const dt_probe_t *prp, Lmid_t *lmidp, const char **objp)
> >>>> +{
> >>>> + if (prp == NULL || prp->desc == NULL)
> >>>> + return -1;
> >>>> +
> >>>> + return dt_cg_parse_objname(prp->desc->mod, lmidp, objp);
> >>>> +}
> >>>> +
> >>>> +static int
> >>>> +dt_cg_obj_matches_path(const char *obj, const char *path)
> >>>> +{
> >>>> + const char *base;
> >>>> + Lmid_t lmid;
> >>>> +
> >>>> + if (obj == NULL || path == NULL)
> >>>> + return 0;
> >>>> +
> >>>> + if (dt_cg_parse_objname(obj, &lmid, &obj) == -1)
> >>>> + return 0;
> >>>> +
> >>>> + base = dt_cg_basename(path);
> >>>> + return strcmp(obj, path) == 0 || strcmp(obj, base) == 0;
> >>>> +}
> >>>> +
> >>>> +static int
> >>>> +dt_cg_elf_sym_to_offset(Elf *elf, const GElf_Sym *sym, uint64_t *offp)
> >>>> +{
> >>>> + GElf_Ehdr ehdr;
> >>>> + size_t i, phnum;
> >>>> + uint64_t base = 0;
> >>>> +
> >>>> + if (sym->st_shndx == SHN_UNDEF ||
> >>>> + gelf_getehdr(elf, &ehdr) == NULL ||
> >>>> + elf_getphdrnum(elf, &phnum) != 0)
> >>>> + return -1;
> >>>> +
> >>>> + /*
> >>>> + * Match the object-relative coordinate used by uprobe offsets. For
> >>>> + * ET_DYN, libproc computes file_dyn_base from the first PT_LOAD
> >>>> + * segment's page offset. For ET_EXEC, the load bias is zero and the
> >>>> + * first segment's vaddr remains part of the object-relative offset.
> >>>> + */
> >>>> + for (i = 0; i < phnum; i++) {
> >>>> + GElf_Phdr phdr;
> >>>> +
> >>>> + if (gelf_getphdr(elf, i, &phdr) == NULL ||
> >>>> + phdr.p_type != PT_LOAD)
> >>>> + continue;
> >>>> +
> >>>> + if (ehdr.e_type == ET_EXEC)
> >>>> + base = phdr.p_vaddr;
> >>>> + else if (ehdr.e_type == ET_DYN && phdr.p_align != 0)
> >>>> + base = phdr.p_vaddr & (phdr.p_align - 1);
> >>>> +
> >>>> + break;
> >>>> + }
> >>>> +
> >>>> + if (i == phnum || sym->st_value < base)
> >>>> + return -1;
> >>>> +
> >>>> + *offp = sym->st_value - base;
> >>>> + return 0;
> >>>> +}
> >>>> +
> >>>> +static int
> >>>> +dt_cg_elf_lookup_symtab(Elf *elf, int type, const char *name, GElf_Sym *symp)
> >>>> +{
> >>>> + Elf_Scn *scn = NULL;
> >>>> +
> >>>> + while ((scn = elf_nextscn(elf, scn)) != NULL) {
> >>>> + GElf_Shdr shdr;
> >>>> + Elf_Data *data;
> >>>> + size_t i, nsyms;
> >>>> +
> >>>> + if (gelf_getshdr(scn, &shdr) == NULL ||
> >>>> + shdr.sh_type != type ||
> >>>> + shdr.sh_entsize == 0)
> >>>> + continue;
> >>>> +
> >>>> + data = elf_getdata(scn, NULL);
> >>>> + if (data == NULL)
> >>>> + continue;
> >>>> +
> >>>> + nsyms = shdr.sh_size / shdr.sh_entsize;
> >>>> + for (i = 0; i < nsyms; i++) {
> >>>> + GElf_Sym sym;
> >>>> + const char *s;
> >>>> + uchar_t stype;
> >>>> +
> >>>> + if (gelf_getsym(data, i, &sym) == NULL ||
> >>>> + sym.st_name == 0 ||
> >>>> + sym.st_shndx == SHN_UNDEF)
> >>>> + continue;
> >>>> +
> >>>> + stype = GELF_ST_TYPE(sym.st_info);
> >>>> + if (stype == STT_FILE || stype == STT_SECTION)
> >>>> + continue;
> >>>> +
> >>>> + s = elf_strptr(elf, shdr.sh_link, sym.st_name);
> >>>> + if (s != NULL && strcmp(s, name) == 0) {
> >>>> + *symp = sym;
> >>>> + return 0;
> >>>> + }
> >>>> + }
> >>>> + }
> >>>> +
> >>>> + return -1;
> >>>> +}
> >>>> +
> >>>> +static int
> >>>> +dt_cg_elf_lookup_offset(const char *path, const char *name, uint64_t *offp)
> >>>> +{
> >>>> + GElf_Sym sym;
> >>>> + Elf *elf = NULL;
> >>>> + int fd, rc = -1;
> >>>> +
> >>>> + fd = open(path, O_RDONLY);
> >>>> + if (fd < 0)
> >>>> + return -1;
> >>>> +
> >>>> + elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
> >>>> + if (elf == NULL || elf_kind(elf) != ELF_K_ELF)
> >>>> + goto out;
> >>>> +
> >>>> + if (dt_cg_elf_lookup_symtab(elf, SHT_SYMTAB, name, &sym) == -1 &&
> >>>> + dt_cg_elf_lookup_symtab(elf, SHT_DYNSYM, name, &sym) == -1)
> >>>> + goto out;
> >>>> +
> >>>> + rc = dt_cg_elf_sym_to_offset(elf, &sym, offp);
> >>>> +
> >>>> +out:
> >>>> + if (elf != NULL)
> >>>> + elf_end(elf);
> >>>> + close(fd);
> >>>> + return rc;
> >>>> +}
> >>>> +
> >>>> +typedef struct dt_cg_uresolve_arg {
> >>>> + const char *name;
> >>>> + const char *obj;
> >>>> + int explicit_obj;
> >>>> + int nmatches;
> >>>> + int no_path;
> >>>> + int no_symbol;
> >>>> + int bad_object;
> >>>> + int different_offset;
> >>>> + uint64_t off;
> >>>> +} dt_cg_uresolve_arg_t;
> >>>> +
> >>>> +static int
> >>>> +dt_cg_uresolve_probe(dtrace_hdl_t *dtp, dt_probe_t *prp, void *arg)
> >>>> +{
> >>>> + dt_cg_uresolve_arg_t *uap = arg;
> >>>> + const char *path;
> >>>> + uint64_t off;
> >>>> +
> >>>> + path = dt_probe_uprobe_path(prp);
> >>>> + if (path == NULL) {
> >>>> + uap->no_path = 1;
> >>>> + return 1;
> >>>> + }
> >>>> +
> >>>> + if (uap->explicit_obj && !dt_cg_obj_matches_path(uap->obj, path)) {
> >>>> + uap->bad_object = 1;
> >>>> + return 1;
> >>>> + }
> >>>> +
> >>>> + if (dt_cg_elf_lookup_offset(path, uap->name, &off) != 0) {
> >>>> + uap->no_symbol = 1;
> >>>> + return 1;
> >>>> + }
> >>>> +
> >>>> + if (uap->nmatches++ == 0) {
> >>>> + uap->off = off;
> >>>> + return 0;
> >>>> + }
> >>>> +
> >>>> + if (uap->off != off) {
> >>>> + uap->different_offset = 1;
> >>>> + return 1;
> >>>> + }
> >>>> +
> >>>> + return 0;
> >>>> +}
> >>>> +
> >>>> +static int
> >>>> +dt_cg_uresolve_from_probe_file(dt_node_t *dnp, dt_node_t *arg,
> >>>> + const char *name, const char *obj,
> >>>> + int explicit_obj, int allow_fallback,
> >>>> + uint64_t *offp)
> >>>> +{
> >>>> + dt_cg_uresolve_arg_t uarg = { 0 };
> >>>> + dtrace_hdl_t *dtp = yypcb->pcb_hdl;
> >>>> +
> >>>> + uarg.name = name;
> >>>> + uarg.obj = obj;
> >>>> + uarg.explicit_obj = explicit_obj;
> >>>> +
> >>>> + if (yypcb->pcb_pdesc != NULL)
> >>>> + dt_probe_iter(dtp, yypcb->pcb_pdesc, dt_cg_uresolve_probe,
> >>>> + NULL, &uarg);
> >>>> + else if (yypcb->pcb_probe != NULL)
> >>>> + dt_cg_uresolve_probe(dtp, yypcb->pcb_probe, &uarg);
> >>>> +
> >>>> + if (uarg.bad_object)
> >>>> + dnerror(arg, D_PROC_NAME, "uresolve( ) object %s does not "
> >>>> + "match the current uprobe object\n", obj);
> >>>> + if (uarg.no_path)
> >>>> + dnerror(dnp, D_PROC_NAME, "uresolve( ) requires a unique "
> >>>> + "uprobe object path for this probe\n");
> >>>> + if (uarg.no_symbol) {
> >>>> + if (allow_fallback)
> >>>> + return -1;
> >>>> +
> >>>> + dnerror(arg, D_PROC_FUNC, "failed to resolve user symbol %s "
> >>>> + "in the current uprobe object\n", arg->dn_string);
> >>>> + }
> >>>> +
> >>>> + if (uarg.nmatches == 0) {
> >>>> + dnerror(dnp, D_PROC_NAME, "uresolve( ) cannot determine the "
> >>>> + "current uprobe object\n");
> >>>> + }
> >>>> +
> >>>> + if (uarg.different_offset)
> >>>> + dnerror(arg, D_PROC_FUNC, "uresolve( ) matched multiple "
> >>>> + "uprobe objects with different offsets for symbol %s\n",
> >>>> + arg->dn_string);
> >>>> +
> >>>> + *offp = uarg.off;
> >>>> + return 0;
> >>>> +}
> >>>> +
> >>>> +static int
> >>>> +dt_cg_uresolve_from_target(dt_node_t *dnp, dt_node_t *arg, pid_t pid,
> >>>> + Lmid_t lmid, const char *obj, const char *name,
> >>>> + int allow_fallback, uint64_t *offp)
> >>>> +{
> >>>> + dtrace_hdl_t *dtp = yypcb->pcb_hdl;
> >>>> + const prmap_t *mapp, *first;
> >>>> + GElf_Sym sym;
> >>>> + pid_t gpid;
> >>>> + int err = 0;
> >>>> +
> >>>> + gpid = dt_proc_grab_lock(dtp, pid,
> >>>> + DTRACE_PROC_WAITING | DTRACE_PROC_SHORTLIVED);
> >>>> + if (gpid <= 0)
> >>>> + dnerror(dnp, D_PROC_GRAB, "failed to grab target process %d "
> >>>> + "for uresolve( )\n", pid);
> >>>> +
> >>>> + if (dt_Pxlookup_by_name(dtp, gpid, lmid, obj, name,
> >>>> + &sym, NULL) != 0) {
> >>>> + err = 1;
> >>>> + } else if ((mapp = dt_Paddr_to_map(dtp, gpid, sym.st_value)) == NULL ||
> >>>> + mapp->pr_file == NULL ||
> >>>> + (first = mapp->pr_file->first_segment) == NULL ||
> >>>> + sym.st_value < first->pr_vaddr) {
> >>>> + err = 1;
> >>>> + } else {
> >>>> + *offp = sym.st_value - first->pr_vaddr;
> >>>> + }
> >>>> +
> >>>> + dt_proc_release_unlock(dtp, gpid);
> >>>> +
> >>>> + if (err == 0)
> >>>> + return 0;
> >>>> +
> >>>> + if (allow_fallback)
> >>>> + return -1;
> >>>> +
> >>>> + dnerror(arg, D_PROC_FUNC, "failed to resolve target user symbol %s\n",
> >>>> + arg->dn_string);
> >>>> +}
> >>>> +
> >>>> +static void
> >>>> +dt_cg_uresolve_emit_offset_addr(dt_node_t *dnp, dt_irlist_t *dlp,
> >>>> + dt_regset_t *drp, uint64_t symoff)
> >>>> +{
> >>>> + dtrace_hdl_t *dtp = yypcb->pcb_hdl;
> >>>> + int treg;
> >>>> +
> >>>> + /*
> >>>> + * Compute the current object base from the saved user PC and the
> >>>> + * uprobe offset, then add the compile-time-resolved symbol offset.
> >>>> + * When available, use the kernel's current uprobe IP directly because
> >>>> + * the saved register block may not reliably carry the breakpoint
> >>>> + * address for system-wide uprobes across different processes.
> >>>> + */
> >>>> + if (dtp->dt_bpfhelper[BPF_FUNC_get_func_ip] != BPF_FUNC_unspec) {
> >>>> + if (dt_regset_xalloc_args(drp) == -1)
> >>>> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> >>>> + if (dt_regset_xalloc(drp, BPF_REG_0) == -1)
> >>>> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> >>>> +
> >>>> + dt_cg_access_dctx(BPF_REG_1, dlp, drp, DCTX_CTX);
> >>>> + emit(dlp, BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_get_func_ip]));
> >>>> + dt_regset_free_args(drp);
> >>>> +
> >>>> + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1)
> >>>> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> >>>> +
> >>>> + emit(dlp, BPF_MOV_REG(dnp->dn_reg, BPF_REG_0));
> >>>> + dt_regset_free(drp, BPF_REG_0);
> >>>> + } else {
> >>>> + if ((dnp->dn_reg = dt_regset_alloc(drp)) == -1)
> >>>> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> >>>> +
> >>>> + if ((treg = dt_regset_alloc(drp)) == -1)
> >>>> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> >>>> +
> >>>> + dt_cg_access_dctx(treg, dlp, drp, DCTX_MST);
> >>>> + emit(dlp, BPF_LOAD(BPF_DW, dnp->dn_reg, treg,
> >>>> + DMST_REGS + PT_REGS_IP));
> >>>> + dt_regset_free(drp, treg);
> >>>> + }
> >>>> +
> >>>> + if ((treg = dt_regset_alloc(drp)) == -1)
> >>>> + longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
> >>>> +
> >>>> + dt_cg_access_dctx(treg, dlp, drp, DCTX_MST);
> >>>> + emit(dlp, BPF_LOAD(BPF_DW, treg, treg, DMST_PROFF));
> >>>> + emit(dlp, BPF_ALU64_REG(BPF_SUB, dnp->dn_reg, treg));
> >>>> + dt_cg_xsetx(dlp, NULL, DT_LBL_NONE, treg, symoff);
> >>>> + emit(dlp, BPF_ALU64_REG(BPF_ADD, dnp->dn_reg, treg));
> >>>> +
> >>>> + dt_regset_free(drp, treg);
> >>>> +}
> >>>> +
> >>>> +static void
> >>>> +dt_cg_subr_uresolve(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
> >>>> +{
> >>>> + dtrace_hdl_t *dtp = yypcb->pcb_hdl;
> >>>> + dt_node_t *arg = dnp->dn_args;
> >>>> + dt_node_t *fallback;
> >>>> + dt_ident_t *tidp;
> >>>> + pid_t pid = 0;
> >>>> + char *spec, *tick;
> >>>> + const char *obj = NULL;
> >>>> + const char *name;
> >>>> + Lmid_t lmid = PR_LMID_EVERY;
> >>>> + uint64_t off = 0;
> >>>> + int err = 0, explicit_obj = 0;
> >>>> + int use_fallback = 0;
> >>>> +
> >>>> + if (arg == NULL || (arg->dn_list != NULL &&
> >>>> + arg->dn_list->dn_list != NULL))
> >>>> + dnerror(dnp, D_PROTO_LEN, "uresolve( ) prototype mismatch: "
> >>>> + "expected 1 or 2 arguments\n");
> >>>> +
> >>>> + if (arg->dn_kind != DT_NODE_STRING)
> >>>> + dnerror(arg, D_PROTO_ARG, "uresolve( ) argument #1 must be "
> >>>> + "a string constant\n");
> >>>> +
> >>>> + fallback = arg->dn_list;
> >>>> + tidp = dt_idhash_lookup(dtp->dt_macros, "target");
> >>>> + if (tidp != NULL)
> >>>> + pid = tidp->di_id;
> >>>> +
> >>>> + spec = strdup(arg->dn_string);
> >>>> + if (spec == NULL)
> >>>> + longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM);
> >>>> +
> >>>> + tick = strrchr(spec, '`');
> >>>> + if (tick != NULL) {
> >>>> + *tick = '\0';
> >>>> + obj = spec;
> >>>> + name = tick + 1;
> >>>> + explicit_obj = 1;
> >>>> +
> >>>> + if (*obj == '\0' || *name == '\0')
> >>>> + err = 1;
> >>>> + } else {
> >>>> + name = spec;
> >>>> + }
> >>>> +
> >>>> + if (err != 0) {
> >>>> + free(spec);
> >>>> + dnerror(arg, D_PROC_NAME, "uresolve( ) argument must be "
> >>>> + "\"symbol\" or \"object`symbol\"\n");
> >>>> + }
> >>>> +
> >>>> + if (pid == 0) {
> >>>> + if (dt_cg_uresolve_from_probe_file(dnp, arg, name, obj,
> >>>> + explicit_obj, fallback != NULL, &off) != 0)
> >>>> + use_fallback = 1;
> >>>> + goto done;
> >>>> + }
> >>>> +
> >>>> + if (explicit_obj) {
> >>>> + if (dt_cg_parse_objname(obj, &lmid, &obj) != 0) {
> >>>> + free(spec);
> >>>> + dnerror(arg, D_PROC_NAME, "uresolve( ) object %s is "
> >>>> + "not a valid user object name\n", arg->dn_string);
> >>>> + }
> >>>> + } else if (dt_cg_probe_objname(yypcb->pcb_probe, &lmid, &obj) != 0) {
> >>>> + const char *path = dt_probe_uprobe_path(yypcb->pcb_probe);
> >>>> +
> >>>> + if (path != NULL) {
> >>>> + obj = path;
> >>>> + lmid = PR_LMID_EVERY;
> >>>> + } else {
> >>>> + free(spec);
> >>>> + dnerror(arg, D_PROC_NAME, "unqualified uresolve( ) "
> >>>> + "requires a concrete probe module\n");
> >>>> + }
> >>>> + }
> >>>> +
> >>>> + if (dt_cg_uresolve_from_target(dnp, arg, pid, lmid, obj, name,
> >>>> + fallback != NULL, &off) != 0)
> >>>> + use_fallback = 1;
> >>>> +
> >>>> +done:
> >>>> + free(spec);
> >>>> +
> >>>> + if (use_fallback) {
> >>>> + dt_cg_node(fallback, dlp, drp);
> >>>> + dnp->dn_reg = fallback->dn_reg;
> >>>> + } else
> >>>> + dt_cg_uresolve_emit_offset_addr(dnp, dlp, drp, off);
> >>>> +}
> >>>> +
> >>>> typedef void dt_cg_subr_f(dt_node_t *, dt_irlist_t *, dt_regset_t *);
> >>>>
> >>>> static dt_cg_subr_f *_dt_cg_subr[DIF_SUBR_MAX + 1] = {
> >>>> @@ -7039,6 +7494,7 @@ static dt_cg_subr_f *_dt_cg_subr[DIF_SUBR_MAX + 1] = {
> >>>> [DIF_SUBR_LINK_NTOP] = &dt_cg_subr_link_ntop,
> >>>> [DIF_SUBR_STACK] = &dt_cg_subr_stack,
> >>>> [DIF_SUBR_USTACK] = &dt_cg_subr_ustack,
> >>>> + [DIF_SUBR_URESOLVE] = &dt_cg_subr_uresolve,
> >>>> };
> >>>>
> >>>> static void
> >>>> diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c
> >>>> index ca00788a..0505e2ff 100644
> >>>> --- a/libdtrace/dt_open.c
> >>>> +++ b/libdtrace/dt_open.c
> >>>> @@ -330,6 +330,8 @@ static const dt_ident_t _dtrace_globals[] = {
> >>>> DT_VERS_1_2, &dt_idops_func, "_usymaddr(uintptr_t)" },
> >>>> { "uregs", DT_IDENT_ARRAY, 0, DIF_VAR_UREGS, DT_ATTR_STABCMN, DT_VERS_1_0,
> >>>> &dt_idops_regs, NULL },
> >>>> +{ "uresolve", DT_IDENT_FUNC, 0, DIF_SUBR_URESOLVE, DT_ATTR_STABCMN,
> >>>> + DT_VERS_2_0, &dt_idops_func, "uintptr_t(string, [uintptr_t])" },
> >>>> { "ustack", DT_IDENT_FUNC, DT_IDFLG_DPTR, DIF_SUBR_USTACK, DT_ATTR_STABCMN,
> >>>> DT_VERS_1_0, &dt_idops_func, "dt_stack_t([uint32_t], [uint32_t])" },
> >>>> { "ustackdepth", DT_IDENT_SCALAR, 0, DIF_VAR_USTACKDEPTH,
> >>>> diff --git a/libdtrace/dt_probe.h b/libdtrace/dt_probe.h
> >>>> index 54053cd3..bff47055 100644
> >>>> --- a/libdtrace/dt_probe.h
> >>>> +++ b/libdtrace/dt_probe.h
> >>>> @@ -102,6 +102,7 @@ typedef int dt_dependent_f(dtrace_hdl_t *dtp, dt_probe_t *prp, void *arg);
> >>>> extern int dt_probe_dependent_iter(dtrace_hdl_t *dtp, const dt_probe_t *prp,
> >>>> dt_dependent_f *func, void *arg);
> >>>>
> >>>> +extern const char *dt_probe_uprobe_path(const dt_probe_t *prp);
> >>>>
> >>>> extern void dt_probe_init(dtrace_hdl_t *dtp);
> >>>> extern void dt_probe_detach_all(dtrace_hdl_t *dtp);
> >>>> diff --git a/libdtrace/dt_prov_uprobe.c b/libdtrace/dt_prov_uprobe.c
> >>>> index d53b1e43..9a481f59 100644
> >>>> --- a/libdtrace/dt_prov_uprobe.c
> >>>> +++ b/libdtrace/dt_prov_uprobe.c
> >>>> @@ -307,6 +307,47 @@ typedef struct uprobe_data {
> >>>> int ref_shift;
> >>>> } uprobe_data_t;
> >>>>
> >>>> +dt_provimpl_t dt_pid;
> >>>> +dt_provimpl_t dt_usdt;
> >>>> +dt_provimpl_t dt_stapsdt;
> >>>> +
> >>>> +const char *
> >>>> +dt_probe_uprobe_path(const dt_probe_t *prp)
> >>>> +{
> >>>> + const dt_uprobe_t *upp;
> >>>> + const list_probe_t *pup;
> >>>> + const char *path = NULL;
> >>>> +
> >>>> + if (prp == NULL || prp->prov == NULL)
> >>>> + return NULL;
> >>>> +
> >>>> + if (prp->prov->impl == &dt_uprobe) {
> >>>> + upp = prp->prv_data;
> >>>> + return upp != NULL ? upp->fn : NULL;
> >>>> + }
> >>>> +
> >>>> + if (prp->prov->impl != &dt_pid &&
> >>>> + prp->prov->impl != &dt_usdt &&
> >>>> + prp->prov->impl != &dt_stapsdt)
> >>>> + return NULL;
> >>>> +
> >>>> + for (pup = prp->prv_data; pup != NULL; pup = dt_list_next(pup)) {
> >>>> + if (pup->probe == NULL || pup->probe->prv_data == NULL)
> >>>> + return NULL;
> >>>> +
> >>>> + upp = pup->probe->prv_data;
> >>>> + if (upp->fn == NULL)
> >>>> + return NULL;
> >>>> +
> >>>> + if (path == NULL)
> >>>> + path = upp->fn;
> >>>> + else if (strcmp(path, upp->fn) != 0)
> >>>> + return NULL;
> >>>> + }
> >>>> +
> >>>> + return path;
> >>>> +}
> >>>> +
> >>>> static const dtrace_pattr_t pattr = {
> >>>> { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
> >>>> { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
> >>>> @@ -315,10 +356,6 @@ static const dtrace_pattr_t pattr = {
> >>>> { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
> >>>> };
> >>>>
> >>>> -dt_provimpl_t dt_pid;
> >>>> -dt_provimpl_t dt_usdt;
> >>>> -dt_provimpl_t dt_stapsdt;
> >>>> -
> >>>> #define UPROBE_CONFIG "/sys/bus/event_source/devices/uprobe/"
> >>>> #define PERF_TYPE_FILE (UPROBE_CONFIG "type")
> >>>> #define RET_FLAG_FILE (UPROBE_CONFIG "format/retprobe")
> >>>> @@ -1392,6 +1429,19 @@ static int trampoline(dt_pcb_t *pcb, uint_t exitlbl)
> >>>> */
> >>>> dt_cg_tramp_copy_regs(pcb);
> >>>>
> >>>> + /*
> >>>> + * On some architectures the saved user IP in the uprobe pt_regs is the
> >>>> + * resume address rather than the probed instruction address. Normalize
> >>>> + * the saved IP to the actual uprobe address when the kernel can provide
> >>>> + * it so consumers such as uresolve() can derive the correct object base.
> >>>> + */
> >>>> + if (dtp->dt_bpfhelper[BPF_FUNC_get_func_ip] != BPF_FUNC_unspec) {
> >>>> + emit(dlp, BPF_MOV_REG(BPF_REG_1, BPF_REG_8));
> >>>> + emit(dlp, BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_get_func_ip]));
> >>>> + emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7,
> >>>> + DMST_REGS + PT_REGS_IP, BPF_REG_0));
> >>>> + }
> >>>> +
> >>>> /*
> >>>> * Record the uprobe instrumentation offset in the probe context.
> >>>> * This is the same object-relative offset passed to perf_event_open()
> >>>> --
> >>>> 2.43.5
> >>>>
> >>>>
> >>>> _______________________________________________
> >>>> DTrace-devel mailing list
> >>>> DTrace-devel@oss.oracle.com
> >>>> https://oss.oracle.com/mailman/listinfo/dtrace-devel
next prev parent reply other threads:[~2026-06-08 20:54 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-05 22:12 [PATCH 0/6] dtrace: support user-space symbol resolution via uresolve() Alan Maguire
2026-06-05 22:12 ` [PATCH 1/6] libdtrace: Pack declared bitfields into C storage units Alan Maguire
2026-06-06 1:54 ` Kris Van Hees
2026-06-07 15:35 ` Alan Maguire
2026-06-05 22:12 ` [PATCH 2/6] libdtrace: do not taint scalar alloca member loads Alan Maguire
2026-06-05 22:12 ` [PATCH 3/6] dtrace: add 'probeoff' variable to record relative offset of probes Alan Maguire
2026-06-06 1:38 ` [DTrace-devel] " Kris Van Hees
2026-06-07 15:40 ` Alan Maguire
2026-06-05 22:12 ` [PATCH 4/6] dtrace: Add uresolve(symbol_name[, optional_fallback]) for symbol lookup Alan Maguire
2026-06-06 1:08 ` [DTrace-devel] " Kris Van Hees
2026-06-08 10:43 ` Alan Maguire
2026-06-08 20:27 ` Kris Van Hees
2026-06-08 20:38 ` Alan Maguire
2026-06-08 20:54 ` Kris Van Hees [this message]
2026-06-05 22:12 ` [PATCH 5/6] test,funcs: add uresolve() tests Alan Maguire
2026-06-05 22:12 ` [PATCH 6/6] docs: document uresolve() in the user guide Alan Maguire
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aicr_xS0ZiXkCKdO@oracle.com \
--to=kris.van.hees@oracle.com \
--cc=alan.maguire@oracle.com \
--cc=codex@openai.com \
--cc=dtrace-devel@oss.oracle.com \
--cc=dtrace@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox