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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.