[DTrace-devel] [PATCH 15/26] libcommon: DOF parsing
Kris Van Hees
kris.van.hees at oracle.com
Mon Oct 24 04:36:44 UTC 2022
This commit adds a DOF parser, derived from DTrace v1 but simplified.
The parser is intended to run in a strict-mode seccomp jail, and does no
I/O except via pre-provided file descriptors. The host side should do
I/O using the functions in dof_parser_host.h.
The seccomp parser child returns output as a stream of dof_parsed_t
records, which are variable-length records with an early size member
(the reader code in dof_parser_host.c takes care of reading the right
amount of data). Errors are returned as PIT_ERR records in this stream,
or signified by a simple crash (in which case the invoker can simply
restart it).
The parser has a notably noisy debugging mechanism which can be turned
on via make dof_dbg=yes (it is separate from make debugging=yes because
it makes the daemon emit lots of stuff on stderr, which you're unlikely
to want unless you're actually debugging USDT itself).
Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
Makeoptions | 8 +-
libcommon/Build | 2 +-
libcommon/dof_parser.c | 1120 +++++++++++++++++++++++++++++++++++
libcommon/dof_parser.h | 148 +++++
libcommon/dof_parser_host.c | 132 +++++
5 files changed, 1408 insertions(+), 2 deletions(-)
create mode 100644 libcommon/dof_parser.c
create mode 100644 libcommon/dof_parser.h
create mode 100644 libcommon/dof_parser_host.c
diff --git a/Makeoptions b/Makeoptions
index 011440a3..715d0a2e 100644
--- a/Makeoptions
+++ b/Makeoptions
@@ -1,11 +1,12 @@
# The implementation of the configurable make options.
#
# Oracle Linux DTrace.
-# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
# Licensed under the Universal Permissive License v 1.0 as shown at
# http://oss.oracle.com/licenses/upl.
debugging ?= no
+dof_dbg ?= no
coverage ?= no
verbose ?= no
@@ -14,12 +15,17 @@ help::
@printf "make debugging=yes [targets] Disable optimization to make debugger use easier\n" >&2
@printf "make coverage=yes [targets] Turn on coverage support in the testsuite\n" >&2
@printf "make verbose=yes [target] Enable verbose building\n" >&2
+ @printf "make dof_dbg=yes [targets] Turn on especially noisy DOF parser debugging\n" >&2
@printf "\n" >&2
ifneq ($(debugging),no)
override CFLAGS += -O0 -g
endif
+ifneq ($(dof_dbg),no)
+override CFLAGS += -DDOF_DEBUG
+endif
+
ifneq ($(coverage),no)
override CFLAGS += -O0 --coverage
override LDFLAGS += --coverage
diff --git a/libcommon/Build b/libcommon/Build
index 30d8be70..c481cea3 100644
--- a/libcommon/Build
+++ b/libcommon/Build
@@ -9,5 +9,5 @@ LIBS += libcommon
libcommon_TARGET = libcommon
libcommon_DIR := $(current-dir)
libcommon_CPPFLAGS := -Ilibcommon -Ilibproc
-libcommon_SOURCES = uprobes.c dt_list.c
+libcommon_SOURCES = dof_parser.c dof_parser_host.c uprobes.c dt_list.c
libcommon_LIBSOURCES = libcommon
diff --git a/libcommon/dof_parser.c b/libcommon/dof_parser.c
new file mode 100644
index 00000000..a5e4d8a6
--- /dev/null
+++ b/libcommon/dof_parser.c
@@ -0,0 +1,1120 @@
+/*
+ * Oracle Linux DTrace; DOF parser.
+ * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+#include <sys/compiler.h>
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "dof_parser.h"
+
+#define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0)
+
+size_t dof_maxsize = 256 * 1024 * 1024;
+
+typedef struct dtrace_helper_probedesc {
+ char *dthpb_prov;
+ char *dthpb_mod;
+ char *dthpb_func;
+ char *dthpb_name;
+ uint64_t dthpb_base;
+ uint32_t *dthpb_offs;
+ uint32_t *dthpb_enoffs;
+ uint32_t dthpb_noffs;
+ uint32_t dthpb_nenoffs;
+ uint8_t *dthpb_args;
+ uint8_t dthpb_xargc;
+ uint8_t dthpb_nargc;
+ char *dthpb_xtypes;
+ char *dthpb_ntypes;
+} dtrace_helper_probedesc_t;
+
+static void dt_dbg_dof(const char *fmt, ...)
+{
+#ifdef DOF_DEBUG
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+#endif
+}
+
+_dt_printflike_(3, 4)
+static void dof_error(int out, int err_no, const char *fmt, ...)
+{
+ dof_parsed_t *parsed;
+ size_t sz;
+ char *msg;
+ va_list ap;
+
+ /*
+ * Not much we can do on OOM of errors other than abort, forcing a
+ * parser restart, which hopefully will have enough memory to report the
+ * error properly.
+ */
+ va_start(ap, fmt);
+ if (vasprintf(&msg, fmt, ap) < 0)
+ abort();
+ va_end(ap);
+
+ sz = offsetof(dof_parsed_t, err.err) + strlen(msg) + 1;
+ parsed = malloc(sz);
+
+ if (!parsed)
+ abort();
+
+ memset(parsed, 0, sz);
+ parsed->size = sz;
+ parsed->type = PIT_ERR;
+ parsed->err.err_no = err_no;
+ strcpy(parsed->err.err, msg);
+
+ dof_parser_write_one(out, parsed, parsed->size);
+ free(parsed);
+ free(msg);
+}
+
+dof_helper_t *
+dof_copyin_helper(int in, int out, int *ok)
+{
+ dof_helper_t *dh;
+ size_t i;
+
+ /*
+ * First get the header, which gives the size of everything else.
+ */
+ dh = malloc(sizeof(dof_helper_t));
+ if (!dh)
+ abort();
+
+ memset(dh, 0, sizeof(dof_helper_t));
+
+ for (i = 0; i < sizeof(dof_helper_t);) {
+ size_t ret;
+
+ ret = read(in, ((char *) dh) + i, sizeof(dof_helper_t) - i);
+
+ if (ret < 0) {
+ switch (errno) {
+ case EINTR:
+ continue;
+ default:
+ goto err;
+ }
+ }
+
+ /*
+ * EOF: parsing done, process shutting down or message
+ * truncated. Fail, in any case.
+ */
+ if (ret == 0)
+ goto err;
+
+ i += ret;
+ }
+
+ *ok = 1;
+ return dh;
+
+err:
+ *ok = 0;
+ free(dh);
+ return NULL;
+}
+
+dof_hdr_t *
+dof_copyin_dof(int in, int out, int *ok)
+{
+ dof_hdr_t *dof;
+ size_t i, sz;
+
+ *ok = 1;
+
+ /*
+ * First get the header, which gives the size of everything else.
+ */
+ dof = malloc(sizeof(dof_hdr_t));
+ if (!dof)
+ abort();
+
+ memset(dof, 0, sizeof(dof_hdr_t));
+
+ for (i = 0, sz = sizeof(dof_hdr_t); i < sz;) {
+ size_t ret;
+
+ ret = read(in, ((char *) dof) + i, sz - i);
+
+ if (ret < 0) {
+ switch (errno) {
+ case EINTR:
+ continue;
+ default:
+ goto err;
+ }
+ }
+
+ /*
+ * EOF: parsing done, process shutting down or message
+ * truncated. Fail, in any case.
+ */
+ if (ret == 0)
+ goto err;
+
+ /* Allocate more room if needed for the reply. */
+ if (i < sizeof(dof_hdr_t) &&
+ i + ret >= sizeof(dof_hdr_t)) {
+ dof_hdr_t *new_dof;
+
+ if (dof->dofh_loadsz >= dof_maxsize) {
+ dof_error(out, E2BIG, "load size %zi exceeds maximum %zi",
+ dof->dofh_loadsz, dof_maxsize);
+ return NULL;
+ }
+
+ if (dof->dofh_loadsz < sizeof(dof_hdr_t)) {
+ dof_error(out, EINVAL, "invalid load size %zi, "
+ "smaller than header size %zi", dof->dofh_loadsz,
+ sizeof(dof_hdr_t));
+ return NULL;
+ }
+
+ new_dof = realloc(dof, dof->dofh_loadsz);
+ if (!new_dof)
+ abort();
+
+ memset(((char *)new_dof) + i + ret, 0, new_dof->dofh_loadsz - (i + ret));
+ dof = new_dof;
+ sz = dof->dofh_loadsz;
+ }
+
+ i += ret;
+ }
+
+ return dof;
+
+err:
+ *ok = 0;
+ free(dof);
+ return NULL;
+}
+
+static void dof_destroy(dof_helper_t *dhp, dof_hdr_t *dof)
+{
+ free(dhp);
+ free(dof);
+}
+
+/*
+ * Return the dof_sec_t pointer corresponding to a given section index. If the
+ * index is not valid, dof_error() is called and NULL is returned. If a type
+ * other than DOF_SECT_NONE is specified, the header is checked against this
+ * type and NULL is returned if the types do not match.
+ */
+static dof_sec_t *dof_sect(int out, dof_hdr_t *dof,
+ uint32_t sectype, dof_secidx_t i)
+{
+ dof_sec_t *sec;
+
+ sec = (dof_sec_t *)(uintptr_t) ((uintptr_t)dof +
+ dof->dofh_secoff +
+ i * dof->dofh_secsize);
+
+ if (i >= dof->dofh_secnum) {
+ dof_error(out, EINVAL, "referenced section index %u is "
+ "invalid, above %u", i, dof->dofh_secnum);
+ return NULL;
+ }
+
+ if (!(sec->dofs_flags & DOF_SECF_LOAD)) {
+ dof_error(out, EINVAL, "referenced section %u is not loadable", i);
+ return NULL;
+ }
+
+ if (sectype != DOF_SECT_NONE && sectype != sec->dofs_type) {
+ dof_error(out, EINVAL, "referenced section %u is the wrong type, "
+ "%u, not %u", i, sec->dofs_type, sectype);
+ return NULL;
+ }
+
+ return sec;
+}
+
+/*
+ * Apply the relocations from the specified 'sec' (a DOF_SECT_URELHDR) to the
+ * specified DOF. At present, this amounts to simply adding 'ubase' to the
+ * site of any user SETX relocations to account for load object base address.
+ * In the future, if we need other relocations, this function can be extended.
+ */
+static int
+dof_relocate(int out, dof_hdr_t *dof, dof_sec_t *sec, uint64_t ubase)
+{
+ uintptr_t daddr = (uintptr_t)dof;
+ dof_relohdr_t *dofr;
+ dof_sec_t *ss, *rs, *ts;
+ dof_relodesc_t *r;
+ uint_t i, n;
+
+ dofr = (dof_relohdr_t *)(uintptr_t) (daddr + sec->dofs_offset);
+
+ if (sec->dofs_size < sizeof(dof_relohdr_t) ||
+ sec->dofs_align != sizeof(dof_secidx_t)) {
+ dof_error(out, EINVAL, "invalid relocation header: "
+ "size %zi (expected %zi); alignment %u (expected %zi)",
+ sec->dofs_size, sizeof(dof_relohdr_t),
+ sec->dofs_align, sizeof(dof_secidx_t));
+ return -1;
+ }
+
+ ss = dof_sect(out, dof, DOF_SECT_STRTAB, dofr->dofr_strtab);
+ rs = dof_sect(out, dof, DOF_SECT_RELTAB, dofr->dofr_relsec);
+ ts = dof_sect(out, dof, DOF_SECT_NONE, dofr->dofr_tgtsec);
+
+ if (ss == NULL || rs == NULL || ts == NULL)
+ return -1; /* dof_error() has been called already */
+
+ if (rs->dofs_entsize < sizeof(dof_relodesc_t) ||
+ rs->dofs_align != sizeof(uint64_t)) {
+ dof_error(out, EINVAL, "invalid relocation section: entsize %i "
+ "(expected %zi); alignment %u (expected %zi)",
+ rs->dofs_entsize, sizeof(dof_relodesc_t),
+ rs->dofs_align, sizeof(uint64_t));
+ return -1;
+ }
+
+ r = (dof_relodesc_t *)(uintptr_t)(daddr + rs->dofs_offset);
+ n = rs->dofs_size / rs->dofs_entsize;
+
+ for (i = 0; i < n; i++) {
+ uintptr_t taddr = daddr + ts->dofs_offset + r->dofr_offset;
+
+ switch (r->dofr_type) {
+ case DOF_RELO_NONE:
+ break;
+ case DOF_RELO_SETX:
+ if (r->dofr_offset >= ts->dofs_size ||
+ r->dofr_offset + sizeof(uint64_t) >
+ ts->dofs_size) {
+ dof_error(out, EINVAL, "bad relocation offset: "
+ "offset %zi, section size %zi)",
+ r->dofr_offset, ts->dofs_size);
+ return -1;
+ }
+
+ if (!IS_ALIGNED(taddr, sizeof(uint64_t))) {
+ dof_error(out, EINVAL, "misaligned setx relo");
+ return -1;
+ }
+
+ /*
+ * This is a bit ugly but it is necessary for because
+ * some linkers retain the relocation records for the
+ * .SUNW_dof section in shared libraries. In that
+ * case, the runtime loader already performed the
+ * relocation, so we do not have to do anything here.
+ *
+ * We check for this situation by comparing the target
+ * address against the base address (ubase). If it is
+ * larger, we assume the relocation already took place.
+ */
+ if (*(uint64_t *)taddr > ubase)
+ dt_dbg_dof(" Relocation by runtime " \
+ "loader: 0x%llx (base 0x%llx)\n",
+ *(uint64_t *)taddr, ubase);
+ else {
+ dt_dbg_dof(" Relocate 0x%llx + 0x%llx " \
+ "= 0x%llx\n",
+ *(uint64_t *)taddr, ubase,
+ *(uint64_t *)taddr + ubase);
+
+ *(uint64_t *)taddr += ubase;
+ }
+
+ break;
+ default:
+ dof_error(out, EINVAL, "invalid relocation type %i",
+ r->dofr_type);
+ return -1;
+ }
+
+ r = (dof_relodesc_t *)((uintptr_t)r + rs->dofs_entsize);
+ }
+
+ return 0;
+}
+
+/*
+ * The dof_hdr_t passed to dof_slurp() should be a partially validated
+ * header: it should be at the front of a memory region that is at least
+ * sizeof(dof_hdr_t) in size -- and then at least dof_hdr.dofh_loadsz in
+ * size. It need not be validated in any other way.
+ */
+static int
+dof_slurp(int out, dof_hdr_t *dof, uint64_t ubase)
+{
+ uint64_t len = dof->dofh_loadsz, seclen;
+ uintptr_t daddr = (uintptr_t)dof;
+ uint_t i;
+
+ if (_dt_unlikely_(dof->dofh_loadsz < sizeof(dof_hdr_t))) {
+ dof_error(out, EINVAL, "load size %zi smaller than header %zi",
+ dof->dofh_loadsz, sizeof(dof_hdr_t));
+ return -1;
+ }
+
+ dt_dbg_dof(" DOF 0x%p Slurping...\n", dof);
+
+ dt_dbg_dof(" DOF 0x%p Validating...\n", dof);
+
+ /*
+ * Check the DOF header identification bytes. In addition to checking
+ * valid settings, we also verify that unused bits/bytes are zeroed so
+ * we can use them later without fear of regressing existing binaries.
+ */
+ if (memcmp(&dof->dofh_ident[DOF_ID_MAG0], DOF_MAG_STRING,
+ DOF_MAG_STRLEN) != 0) {
+ dof_error(out, EINVAL, "DOF magic string mismatch: %c%c%c%c "
+ "versus %c%c%c%c\n", dof->dofh_ident[DOF_ID_MAG0],
+ dof->dofh_ident[DOF_ID_MAG1],
+ dof->dofh_ident[DOF_ID_MAG2],
+ dof->dofh_ident[DOF_ID_MAG3],
+ DOF_MAG_STRING[0],
+ DOF_MAG_STRING[1],
+ DOF_MAG_STRING[2],
+ DOF_MAG_STRING[3]);
+ return -1;
+ }
+
+ if (dof->dofh_ident[DOF_ID_MODEL] != DOF_MODEL_ILP32 &&
+ dof->dofh_ident[DOF_ID_MODEL] != DOF_MODEL_LP64) {
+ dof_error(out, EINVAL, "DOF has invalid data model: %i",
+ dof->dofh_ident[DOF_ID_MODEL]);
+ return -1;
+ }
+
+ if (dof->dofh_ident[DOF_ID_ENCODING] != DOF_ENCODE_NATIVE) {
+ dof_error(out, EINVAL, "DOF encoding mismatch: %i, expected %i",
+ dof->dofh_ident[DOF_ID_ENCODING], DOF_ENCODE_NATIVE);
+ return -1;
+ }
+
+ if (dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_1 &&
+ dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_2) {
+ dof_error(out, EINVAL, "DOF version mismatch: %i",
+ dof->dofh_ident[DOF_ID_VERSION]);
+ return -1;
+ }
+
+ if (dof->dofh_ident[DOF_ID_DIFVERS] != DIF_VERSION_2) {
+ dof_error(out, EINVAL, "DOF uses unsupported instruction set %i",
+ dof->dofh_ident[DOF_ID_DIFVERS]);
+ return -1;
+ }
+
+ if (dof->dofh_ident[DOF_ID_DIFIREG] > DIF_DIR_NREGS) {
+ dof_error(out, EINVAL, "DOF uses too many integer registers: %i > %i",
+ dof->dofh_ident[DOF_ID_DIFIREG], DIF_DIR_NREGS);
+ return -1;
+ }
+
+ if (dof->dofh_ident[DOF_ID_DIFTREG] > DIF_DTR_NREGS) {
+ dof_error(out, EINVAL, "DOF uses too many tuple registers: %i > %i",
+ dof->dofh_ident[DOF_ID_DIFTREG], DIF_DTR_NREGS);
+ return -1;
+ }
+
+ for (i = DOF_ID_PAD; i < DOF_ID_SIZE; i++) {
+ if (dof->dofh_ident[i] != 0) {
+ dof_error(out, EINVAL, "DOF has invalid ident byte set: %i = %i",
+ i, dof->dofh_ident[i]);
+ return -1;
+ }
+ }
+
+ if (dof->dofh_flags & ~DOF_FL_VALID) {
+ dof_error(out, EINVAL, "DOF has invalid flag bits set: %xi", dof->dofh_flags);
+ return -1;
+ }
+
+ if (dof->dofh_secsize == 0) {
+ dof_error(out, EINVAL, "zero section header size");
+ return -1;
+ }
+
+ /*
+ * Check that the section headers don't exceed the amount of DOF
+ * data. Note that we cast the section size and number of sections
+ * to uint64_t's to prevent possible overflow in the multiplication.
+ */
+ seclen = (uint64_t)dof->dofh_secnum * (uint64_t)dof->dofh_secsize;
+
+ if (dof->dofh_secoff > len || seclen > len ||
+ dof->dofh_secoff + seclen > len) {
+ dof_error(out, EINVAL, "truncated section headers: %zi, %zi, %zi",
+ dof->dofh_secoff, len, seclen);
+ return -1;
+ }
+
+ if (!IS_ALIGNED(dof->dofh_secoff, sizeof(uint64_t))) {
+ dof_error(out, EINVAL, "misaligned section headers");
+ return -1;
+ }
+
+ if (!IS_ALIGNED(dof->dofh_secsize, sizeof(uint64_t))) {
+ dof_error(out, EINVAL, "misaligned section size");
+ return -1;
+ }
+
+ /*
+ * Take an initial pass through the section headers to be sure that
+ * the headers don't have stray offsets.
+ */
+ dt_dbg_dof(" DOF 0x%p Checking section offsets...\n", dof);
+
+ for (i = 0; i < dof->dofh_secnum; i++) {
+ dof_sec_t *sec;
+
+ sec = (dof_sec_t *)(daddr + (uintptr_t)dof->dofh_secoff +
+ i * dof->dofh_secsize);
+
+ if (DOF_SEC_ISLOADABLE(sec->dofs_type) &&
+ !(sec->dofs_flags & DOF_SECF_LOAD)) {
+ dof_error(out, EINVAL, "loadable section %i with load flag unset",
+ i);
+ return -1;
+ }
+
+ /*
+ * Just ignore non-loadable sections.
+ */
+ if (!(sec->dofs_flags & DOF_SECF_LOAD))
+ continue;
+
+ if (sec->dofs_align & (sec->dofs_align - 1)) {
+ dof_error(out, EINVAL, "bad section %i alignment %x", i,
+ sec->dofs_align);
+ return -1;
+ }
+
+ if (sec->dofs_offset & (sec->dofs_align - 1)) {
+ dof_error(out, EINVAL, "misaligned section %i: %lx, "
+ "stated alignment %xi", i, sec->dofs_offset,
+ sec->dofs_align);
+ return -1;
+ }
+
+ if (sec->dofs_offset > len || sec->dofs_size > len ||
+ sec->dofs_offset + sec->dofs_size > len) {
+ dof_error(out, EINVAL, "corrupt section %i header: "
+ "offset %lx, size %lx, len %lx", i,
+ sec->dofs_offset, sec->dofs_size, len);
+ return -1;
+ }
+
+ if (sec->dofs_type == DOF_SECT_STRTAB && *((char *)daddr +
+ sec->dofs_offset + sec->dofs_size - 1) != '\0') {
+ dof_error(out, EINVAL, "section %i: non-0-terminated "
+ "string table", i);
+ return -1;
+ }
+ }
+
+ /*
+ * Take a second pass through the sections and locate and perform any
+ * relocations that are present. We do this after the first pass to
+ * be sure that all sections have had their headers validated.
+ */
+ dt_dbg_dof(" DOF 0x%p Performing relocations...\n", dof);
+
+ for (i = 0; i < dof->dofh_secnum; i++) {
+ dof_sec_t *sec;
+
+ sec = (dof_sec_t *)(daddr + (uintptr_t)dof->dofh_secoff +
+ i * dof->dofh_secsize);
+
+ /*
+ * Skip sections that are not loadable.
+ */
+ if (!(sec->dofs_flags & DOF_SECF_LOAD))
+ continue;
+
+ switch (sec->dofs_type) {
+ case DOF_SECT_URELHDR:
+ if (dof_relocate(out, dof, sec, ubase) != 0)
+ return -1;
+ break;
+ }
+ }
+
+ dt_dbg_dof(" DOF 0x%p Done slurping\n", dof);
+
+ return 0;
+}
+
+static int
+helper_provider_validate(int out, dof_hdr_t *dof, dof_sec_t *sec)
+{
+ uintptr_t daddr = (uintptr_t)dof;
+ dof_sec_t *str_sec, *prb_sec, *arg_sec, *off_sec, *enoff_sec;
+ dof_provider_t *prov;
+ dof_probe_t *prb;
+ uint8_t *arg;
+ char *strtab, *typestr;
+ dof_stridx_t typeidx;
+ size_t typesz;
+ uint_t nprobes, j, k;
+
+ if (_dt_unlikely_(sec->dofs_type != DOF_SECT_PROVIDER)) {
+ dof_error(out, EINVAL, "DOF is not provider DOF: %i", sec->dofs_type);
+ return -1;
+ }
+
+ if (sec->dofs_offset & (sizeof(uint_t) - 1)) {
+ dof_error(out, EINVAL, "misaligned section offset: %lx",
+ sec->dofs_offset);
+ return -1;
+ }
+
+ /*
+ * The section needs to be large enough to contain the DOF provider
+ * structure appropriate for the given version.
+ */
+ if (sec->dofs_size <
+ ((dof->dofh_ident[DOF_ID_VERSION] == DOF_VERSION_1)
+ ? offsetof(dof_provider_t, dofpv_prenoffs)
+ : sizeof(dof_provider_t))) {
+ dof_error(out, EINVAL, "provider section too small: %lx",
+ sec->dofs_size);
+ return -1;
+ }
+
+ prov = (dof_provider_t *)(uintptr_t)(daddr + sec->dofs_offset);
+ str_sec = dof_sect(out, dof, DOF_SECT_STRTAB, prov->dofpv_strtab);
+ prb_sec = dof_sect(out, dof, DOF_SECT_PROBES, prov->dofpv_probes);
+ arg_sec = dof_sect(out, dof, DOF_SECT_PRARGS, prov->dofpv_prargs);
+ off_sec = dof_sect(out, dof, DOF_SECT_PROFFS, prov->dofpv_proffs);
+
+ if (str_sec == NULL || prb_sec == NULL ||
+ arg_sec == NULL || off_sec == NULL)
+ return -1;
+
+ enoff_sec = NULL;
+
+ if (dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_1 &&
+ prov->dofpv_prenoffs != DOF_SECT_NONE) {
+ enoff_sec = dof_sect(out, dof, DOF_SECT_PRENOFFS,
+ prov->dofpv_prenoffs);
+
+ if (enoff_sec == NULL)
+ return -1;
+ }
+
+ strtab = (char *)(uintptr_t)(daddr + str_sec->dofs_offset);
+
+ if (prov->dofpv_name >= str_sec->dofs_size) {
+ dof_error(out, EINVAL, "invalid provider name offset: %u > %zi",
+ prov->dofpv_name, str_sec->dofs_size);
+ return -1;
+ }
+
+ if (strlen(strtab + prov->dofpv_name) >= DTRACE_PROVNAMELEN) {
+ dof_error(out, EINVAL, "provider name too long: %s",
+ strtab + prov->dofpv_name);
+ return -1;
+ }
+
+ if (prb_sec->dofs_entsize == 0 ||
+ prb_sec->dofs_entsize > prb_sec->dofs_size) {
+ dof_error(out, EINVAL, "invalid entry size %x, max %lx",
+ prb_sec->dofs_entsize, prb_sec->dofs_size);
+ return -1;
+ }
+
+ if (prb_sec->dofs_entsize & (sizeof(uintptr_t) - 1)) {
+ dof_error(out, EINVAL, "misaligned entry size %x",
+ prb_sec->dofs_entsize);
+ return -1;
+ }
+
+ if (off_sec->dofs_entsize != sizeof(uint32_t)) {
+ dof_error(out, EINVAL, "invalid entry size %x",
+ off_sec->dofs_entsize);
+ return -1;
+ }
+
+ if (off_sec->dofs_offset & (sizeof(uint32_t) - 1)) {
+ dof_error(out, EINVAL, "misaligned section offset %lx",
+ off_sec->dofs_offset);
+ return -1;
+ }
+
+ if (arg_sec->dofs_entsize != sizeof(uint8_t)) {
+ dof_error(out, EINVAL, "invalid entry size %x",
+ arg_sec->dofs_entsize);
+ return -1;
+ }
+
+ arg = (uint8_t *)(uintptr_t)(daddr + arg_sec->dofs_offset);
+ nprobes = prb_sec->dofs_size / prb_sec->dofs_entsize;
+
+ dt_dbg_dof(" DOF 0x%p %s::: with %d probes\n",
+ dof, strtab + prov->dofpv_name, nprobes);
+
+ /*
+ * Take a pass through the probes to check for errors.
+ */
+ for (j = 0; j < nprobes; j++) {
+ prb = (dof_probe_t *)(uintptr_t)
+ (daddr + prb_sec->dofs_offset +
+ j * prb_sec->dofs_entsize);
+
+ if (prb->dofpr_func >= str_sec->dofs_size) {
+ dof_error(out, EINVAL, "invalid function name: "
+ "strtab offset %x, max %lx", prb->dofpr_func,
+ str_sec->dofs_size);
+ return -1;
+ }
+
+ if (strlen(strtab + prb->dofpr_func) >= DTRACE_FUNCNAMELEN) {
+ dof_error(out, EINVAL, "function name %s too long",
+ strtab + prb->dofpr_func);
+ return -1;
+ }
+
+ if (prb->dofpr_name >= str_sec->dofs_size) {
+ dof_error(out, EINVAL, "invalid probe name: "
+ "strtab offset %x, max %lx", prb->dofpr_name,
+ str_sec->dofs_size);
+ return -1;
+ }
+
+ if (strlen(strtab + prb->dofpr_name) >= DTRACE_NAMELEN) {
+ dof_error(out, EINVAL, "probe name %s too long",
+ strtab + prb->dofpr_name);
+ return -1;
+ }
+
+ /*
+ * The offset count must not wrap the index, and the offsets
+ * must also not overflow the section's data.
+ */
+ if (prb->dofpr_offidx + prb->dofpr_noffs < prb->dofpr_offidx ||
+ (prb->dofpr_offidx + prb->dofpr_noffs) *
+ off_sec->dofs_entsize > off_sec->dofs_size) {
+ dof_error(out, EINVAL, "invalid probe offset %x "
+ "(offset count %x, section entsize %x, size %lx)",
+ prb->dofpr_offidx, prb->dofpr_noffs,
+ off_sec->dofs_entsize, off_sec->dofs_size);
+ return -1;
+ }
+
+ if (dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_1) {
+ /*
+ * If there's no is-enabled offset section, make sure
+ * there aren't any is-enabled offsets. Otherwise
+ * perform the same checks as for probe offsets
+ * (immediately above).
+ */
+ if (enoff_sec == NULL) {
+ if (prb->dofpr_enoffidx != 0 ||
+ prb->dofpr_nenoffs != 0) {
+ dof_error(out, EINVAL,
+ "is-enabled offsets with null section");
+ return -1;
+ }
+ } else if (prb->dofpr_enoffidx + prb->dofpr_nenoffs <
+ prb->dofpr_enoffidx ||
+ (prb->dofpr_enoffidx + prb->dofpr_nenoffs) *
+ enoff_sec->dofs_entsize >
+ enoff_sec->dofs_size) {
+ dof_error(out, EINVAL, "invalid is-enabled offset %x "
+ "(offset count %x, section entsize %x, size %lx)",
+ prb->dofpr_enoffidx, prb->dofpr_nenoffs,
+ enoff_sec->dofs_entsize, enoff_sec->dofs_size);
+ return -1;
+ }
+
+ if (prb->dofpr_noffs + prb->dofpr_nenoffs == 0) {
+ dof_error(out, EINVAL, "zero probe and is-enabled offsets");
+ return -1;
+ }
+ } else if (prb->dofpr_noffs == 0) {
+ dof_error(out, EINVAL, "zero probe offsets");
+ return -1;
+ }
+
+ if (prb->dofpr_argidx + prb->dofpr_xargc < prb->dofpr_argidx ||
+ (prb->dofpr_argidx + prb->dofpr_xargc) *
+ arg_sec->dofs_entsize > arg_sec->dofs_size) {
+ dof_error(out, EINVAL, "invalid args, idx %x "
+ "(offset count %x, section entsize %x, size %lx)",
+ prb->dofpr_argidx, prb->dofpr_xargc,
+ arg_sec->dofs_entsize, arg_sec->dofs_size);
+ return -1;
+ }
+
+ typeidx = prb->dofpr_nargv;
+ typestr = strtab + prb->dofpr_nargv;
+ for (k = 0; k < prb->dofpr_nargc; k++) {
+ if (typeidx >= str_sec->dofs_size) {
+ dof_error(out, EINVAL, "bad native argument type "
+ "for arg %i: %x", k, typeidx);
+ return -1;
+ }
+
+ typesz = strlen(typestr) + 1;
+ if (typesz > DTRACE_ARGTYPELEN) {
+ dof_error(out, EINVAL, "native argument type for arg %i "
+ "too long: %s", k, typestr);
+ return -1;
+ }
+
+ typeidx += typesz;
+ typestr += typesz;
+ }
+
+ typeidx = prb->dofpr_xargv;
+ typestr = strtab + prb->dofpr_xargv;
+ for (k = 0; k < prb->dofpr_xargc; k++) {
+ if (arg[prb->dofpr_argidx + k] > prb->dofpr_nargc) {
+ dof_error(out, EINVAL, "bad native argument index "
+ "for arg %i: %i (max %i)", k,
+ arg[prb->dofpr_argidx + k],
+ prb->dofpr_nargc);
+ return -1;
+ }
+
+ if (typeidx >= str_sec->dofs_size) {
+ dof_error(out, EINVAL, "bad translated argument type "
+ "for arg %i: %x", k, typeidx);
+ return -1;
+ }
+
+ typesz = strlen(typestr) + 1;
+ if (typesz > DTRACE_ARGTYPELEN) {
+ dof_error(out, EINVAL, "translated argument type for arg %i "
+ "too long: %s", k, typestr);
+ return -1;
+ }
+
+ typeidx += typesz;
+ typestr += typesz;
+ }
+
+ dt_dbg_dof(" Probe %d %s:%s:%s:%s with %d offsets, "
+ "%d is-enabled offsets\n", j,
+ strtab + prov->dofpv_name, strtab + prov->dofpv_name,
+ strtab + prb->dofpr_func, strtab + prb->dofpr_name,
+ prb->dofpr_noffs, prb->dofpr_nenoffs);
+ }
+
+ return 0;
+}
+
+static void
+emit_tp(int out, uint64_t base, uint64_t offs, int is_enabled)
+{
+ dof_parsed_t tp;
+
+ memset(&tp, 0, sizeof(tp));
+
+ tp.size = sizeof(dof_parsed_t);
+ tp.type = PIT_TRACEPOINT;
+ tp.tracepoint.addr = base + offs;
+ tp.tracepoint.is_enabled = is_enabled;
+ dof_parser_write_one(out, &tp, tp.size);
+
+ dt_dbg_dof(" Tracepoint at 0x%lx (0x%llx + 0x%x)%s\n",
+ base + offs, base, offs, is_enabled ? " (is_enabled)" : "");
+}
+
+static int
+uint32_cmp(const void *ap, const void *bp)
+{
+ return (*(const uint32_t *)ap - *(const uint32_t *)bp);
+}
+
+static int
+validate_probe(int out, dtrace_helper_probedesc_t *dhpb)
+{
+ int i;
+
+ /*
+ * The offsets must be unique.
+ */
+ qsort(dhpb->dthpb_offs, dhpb->dthpb_noffs, sizeof(uint32_t),
+ uint32_cmp);
+ for (i = 1; i < dhpb->dthpb_noffs; i++) {
+ if (dhpb->dthpb_base + dhpb->dthpb_offs[i] <=
+ dhpb->dthpb_base + dhpb->dthpb_offs[i - 1]) {
+ dof_error(out, EINVAL, "non-unique USDT offsets at %i: %li <= %li",
+ i, dhpb->dthpb_base + dhpb->dthpb_offs[i],
+ dhpb->dthpb_base + dhpb->dthpb_offs[i - 1]);
+ return -1;
+ }
+ }
+
+ qsort(dhpb->dthpb_enoffs, dhpb->dthpb_nenoffs, sizeof(uint32_t),
+ uint32_cmp);
+ for (i = 1; i < dhpb->dthpb_nenoffs; i++) {
+ if (dhpb->dthpb_base + dhpb->dthpb_enoffs[i] <=
+ dhpb->dthpb_base + dhpb->dthpb_enoffs[i - 1]) {
+ dof_error(out, EINVAL, "non-unique is-enabled USDT offsets "
+ "at %i: %li <= %li", i,
+ dhpb->dthpb_base + dhpb->dthpb_enoffs[i],
+ dhpb->dthpb_base + dhpb->dthpb_enoffs[i - 1]);
+ return -1;
+ }
+ }
+
+ if (dhpb->dthpb_noffs == 0 && dhpb->dthpb_nenoffs == 0) {
+ dof_error(out, EINVAL, "USDT probe with zero tracepoints");
+ return -1;
+ }
+ return 0;
+}
+
+static void
+emit_probe(int out, dtrace_helper_probedesc_t *dhpb)
+{
+ int i;
+
+ /* XXX TODO translated args
+ pp->ftp_nargs = dhpb->dthpb_xargc;
+ pp->ftp_xtypes = dhpb->dthpb_xtypes;
+ pp->ftp_ntypes = dhpb->dthpb_ntypes;
+ */
+
+ /*
+ * Return info on each tracepoint in turn.
+ */
+ for (i = 0; i < dhpb->dthpb_noffs; i++)
+ emit_tp(out, dhpb->dthpb_base, dhpb->dthpb_offs[i], 0);
+
+ /*
+ * Then create a tracepoint for each is-enabled point.
+ *
+ * XXX original code looped over ntps here, which is noffs + enoffs.
+ * This seems surely wrong!
+ */
+ for (i = 0; i < dhpb->dthpb_nenoffs; i++)
+ emit_tp(out, dhpb->dthpb_base, dhpb->dthpb_enoffs[i], 1);
+
+ /*
+ * XXX later:
+ * If the arguments are shuffled around we set the argument remapping
+ * table. Later, when the probe fires, we only remap the arguments
+ * if the table is non-NULL.
+ *
+ for (i = 0; i < dhpb->dthpb_xargc; i++) {
+ if (dhpb->dthpb_args[i] != i) {
+ pp->ftp_argmap = dhpb->dthpb_args;
+ break;
+ }
+ } */
+}
+
+static void
+helper_provide_one(int out, dof_helper_t *dhp,
+ dof_hdr_t *dof, dof_sec_t *sec)
+{
+ uintptr_t daddr = (uintptr_t)dof;
+ uint32_t *off, *enoff;
+ char *strtab;
+ uint_t i;
+ void *arg;
+
+ dof_sec_t *str_sec, *prb_sec, *arg_sec, *off_sec,
+ *enoff_sec;
+ dof_provider_t *prov;
+ dof_probe_t *probe;
+
+ char *prov_name;
+ dof_parsed_t *prov_msg;
+ size_t prov_msg_size;
+
+ memset(&prov_msg, 0, sizeof(prov_msg));
+
+ prov = (dof_provider_t *)(uintptr_t)(daddr + sec->dofs_offset);
+ str_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff +
+ prov->dofpv_strtab *
+ dof->dofh_secsize);
+ prb_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff +
+ prov->dofpv_probes *
+ dof->dofh_secsize);
+ arg_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff +
+ prov->dofpv_prargs *
+ dof->dofh_secsize);
+ off_sec = (dof_sec_t *)(uintptr_t)(daddr + dof->dofh_secoff +
+ prov->dofpv_proffs *
+ dof->dofh_secsize);
+
+ strtab = (char *)(uintptr_t)(daddr + str_sec->dofs_offset);
+ off = (uint32_t *)(uintptr_t)(daddr + off_sec->dofs_offset);
+ arg = (uint8_t *)(uintptr_t)(daddr + arg_sec->dofs_offset);
+ enoff = NULL;
+
+ /*
+ * See helper_provider_validate().
+ */
+ if (dof->dofh_ident[DOF_ID_VERSION] != DOF_VERSION_1 &&
+ prov->dofpv_prenoffs != DOF_SECT_NONE) {
+ enoff_sec = (dof_sec_t *)(uintptr_t)
+ (daddr + dof->dofh_secoff +
+ prov->dofpv_prenoffs * dof->dofh_secsize);
+ enoff = (uint32_t *)(uintptr_t)
+ (daddr + enoff_sec->dofs_offset);
+ }
+
+ prov_name = strtab + prov->dofpv_name;
+ prov_msg_size = offsetof(dof_parsed_t, provider.name) +
+ strlen(prov_name) + 1;
+
+ prov_msg = malloc(prov_msg_size);
+ if (!prov_msg) {
+ dof_error(out, ENOMEM, "Out of memory allocating provider msg");
+ return;
+ }
+
+ memset(prov_msg, 0, prov_msg_size);
+
+ prov_msg->size = prov_msg_size;
+ prov_msg->type = PIT_PROVIDER;
+ prov_msg->provider.nprobes = prb_sec->dofs_size / prb_sec->dofs_entsize;
+ stpcpy(prov_msg->provider.name, prov_name);
+ dof_parser_write_one(out, prov_msg, prov_msg->size);
+
+ /*
+ * Pass back info on the probes and their associated tracepoints.
+ */
+ for (i = 0; i < prov_msg->provider.nprobes; i++) {
+ dof_parsed_t *probe_msg;
+ size_t probe_msg_size;
+ dtrace_helper_probedesc_t dhpb;
+ char *ptr;
+
+ probe = (dof_probe_t *)(uintptr_t)(daddr +
+ prb_sec->dofs_offset +
+ i * prb_sec->dofs_entsize);
+
+ dhpb.dthpb_mod = dhp->dofhp_mod;
+ dhpb.dthpb_func = strtab + probe->dofpr_func;
+ dhpb.dthpb_name = strtab + probe->dofpr_name;
+ dhpb.dthpb_base = probe->dofpr_addr;
+ dhpb.dthpb_offs = off + probe->dofpr_offidx;
+ dhpb.dthpb_noffs = probe->dofpr_noffs;
+
+ if (enoff != NULL) {
+ dhpb.dthpb_enoffs = enoff + probe->dofpr_enoffidx;
+ dhpb.dthpb_nenoffs = probe->dofpr_nenoffs;
+ } else {
+ dhpb.dthpb_enoffs = NULL;
+ dhpb.dthpb_nenoffs = 0;
+ }
+
+ dhpb.dthpb_args = ((unsigned char *) arg) + probe->dofpr_argidx;
+ dhpb.dthpb_nargc = probe->dofpr_nargc;
+ dhpb.dthpb_xargc = probe->dofpr_xargc;
+ dhpb.dthpb_ntypes = strtab + probe->dofpr_nargv;
+ dhpb.dthpb_xtypes = strtab + probe->dofpr_xargv;
+
+ probe_msg_size = offsetof(dof_parsed_t, probe.desc) +
+ strlen(dhpb.dthpb_mod) + 1 +
+ strlen(dhpb.dthpb_func) + 1 +
+ strlen(dhpb.dthpb_name) + 1;
+
+ probe_msg = malloc(probe_msg_size);
+ if (!probe_msg) {
+ dof_error(out, ENOMEM, "Out of memory allocating probe");
+ return;
+ }
+
+ memset(probe_msg, 0, probe_msg_size);
+
+ probe_msg->size = probe_msg_size;
+ probe_msg->type = PIT_PROBE;
+ probe_msg->probe.ntp = dhpb.dthpb_noffs + dhpb.dthpb_nenoffs;
+ ptr = stpcpy(probe_msg->probe.desc, dhpb.dthpb_mod);
+ ptr++;
+ ptr = stpcpy(ptr, dhpb.dthpb_func);
+ ptr++;
+ strcpy(ptr, dhpb.dthpb_name);
+ dof_parser_write_one(out, probe_msg, probe_msg_size);
+
+ dt_dbg_dof(" Creating probe %s:%s:%s:%s\n",
+ strtab + prov->dofpv_name, dhpb.dthpb_mod,
+ dhpb.dthpb_func, dhpb.dthpb_name);
+
+ if (validate_probe(out, &dhpb) == 0)
+ emit_probe(out, &dhpb);
+ free(probe_msg);
+ }
+}
+
+void
+dof_parse_providers(int out, dof_helper_t *dhp, dof_hdr_t *dof)
+{
+ int i, rv;
+ uintptr_t daddr = (uintptr_t)dof;
+ int count = 0;
+
+ dt_dbg_dof("DOF 0x%p from helper {'%s', %p, %p}...\n",
+ dof, dhp ? dhp->dofhp_mod : "<none>", dhp, dof);
+
+ rv = dof_slurp(out, dof, dhp->dofhp_addr);
+ if (rv != 0) {
+ dof_destroy(dhp, dof);
+ return;
+ }
+
+ /*
+ * Look for helper providers, validate their descriptions, and
+ * parse them.
+ */
+ if (dhp != NULL) {
+ dt_dbg_dof(" DOF 0x%p Validating and parsing providers...\n", dof);
+
+ for (i = 0; i < dof->dofh_secnum; i++) {
+ dof_sec_t *sec;
+
+ sec = (dof_sec_t *)(uintptr_t)
+ (daddr + dof->dofh_secoff +
+ i * dof->dofh_secsize);
+
+ if (sec->dofs_type != DOF_SECT_PROVIDER)
+ continue;
+
+ if (helper_provider_validate(out, dof, sec) != 0) {
+ dof_destroy(dhp, dof);
+ return;
+ }
+ count++;
+ helper_provide_one(out, dhp, dof, sec);
+ }
+ }
+
+ /*
+ * If nothing was written, emit an empty result to wake up
+ * the caller.
+ */
+ if (count == 0) {
+ dof_parsed_t empty;
+
+ memset(&empty, 0, sizeof(dof_parsed_t));
+
+ empty.size = sizeof(dof_parsed_t);
+ empty.type = PIT_PROVIDER;
+ empty.provider.nprobes = 0;
+ dof_parser_write_one(out, &empty, empty.size);
+ }
+
+ dof_destroy(dhp, dof);
+}
diff --git a/libcommon/dof_parser.h b/libcommon/dof_parser.h
new file mode 100644
index 00000000..bb587dcb
--- /dev/null
+++ b/libcommon/dof_parser.h
@@ -0,0 +1,148 @@
+/*
+ * Oracle Linux DTrace; DOF parser interface with the outside world
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+#ifndef _DOF_PARSER_H
+#define _DOF_PARSER_H
+
+#include <inttypes.h>
+#include <stddef.h>
+
+#include <dtrace/dof.h>
+#include <dtrace/helpers.h>
+
+/*
+ * Result of DOF probe parsing (currently used only for probe creation. We receive a probes info
+ * structure, followed by N probe info structures each of which is followed by
+ * possibly many tracepoint info structures, all tagged. Things not useful for
+ * probe creation (like args, translated types, etc) are not returned.
+ *
+ * On error, a PIT_ERR structure is returned with an error message.
+ */
+
+typedef enum dof_parsed_info {
+ PIT_PROVIDER = 0,
+ PIT_PROBE = 1,
+ PIT_TRACEPOINT = 2,
+ PIT_ERR = 3
+} dof_parsed_info_t;
+
+typedef struct dof_parsed {
+ /*
+ * Size of this instance of this structure.
+ */
+ size_t size;
+
+ dof_parsed_info_t type;
+
+ __extension__ union {
+ struct dpi_provider_info {
+ /*
+ * Number of probes that follow.
+ */
+ size_t nprobes;
+
+ /*
+ * A \0-terminated string (prv).
+ */
+ char name[1];
+ } provider;
+
+ struct dpi_probe_info {
+ /*
+ * Number of tracepoints that follow.
+ */
+ size_t ntp;
+
+ /*
+ * Three \0-separated strings (mod/fun/prb).
+ */
+ char desc[1];
+ } probe;
+
+ struct dpi_tracepoint_info {
+ /*
+ * Offset of this tracepoint.
+ */
+ uint64_t addr;
+
+ /*
+ * True if this is an is-enabled probe.
+ */
+ uint32_t is_enabled;
+
+ /*
+ * XXX Not yet implemented: name, args
+ */
+ } tracepoint;
+
+ struct dpi_err {
+ /*
+ * An errno value.
+ */
+ int err_no;
+
+ /*
+ * A \0-terminated string.
+ */
+ char err[1];
+ } err;
+ };
+} dof_parsed_t;
+
+/*
+ * Host-side: in dof_parser_host.c. The host is the
+ * non-jailed process that talks to the jailed parser.
+ */
+
+/*
+ * Write the DOF to the parser pipe OUT.
+ *
+ * Returns 0 on success or a positive errno value on error.
+ */
+int dof_parser_host_write(int out, dof_helper_t *dh, dof_hdr_t *dof);
+
+/*
+ * Read a single DOF structure from a parser pipe. Wait at most TIMEOUT seconds
+ * to do so.
+ *
+ * Returns NULL and sets errno on error.
+ */
+dof_parsed_t *dof_parser_host_read(int in, int timeout);
+
+/* Parser-side: in dof_parser.c. */
+
+/*
+ * Get a dof_helper_t from the input fd.
+ *
+ * Set OK to zero if no further parsing is possible.
+ */
+dof_helper_t *dof_copyin_helper(int in, int out, int *ok);
+
+/*
+ * Get a buffer of DOF from the input fd and sanity-check it.
+ *
+ * Set OK to zero if no further parsing is possible.
+ */
+dof_hdr_t *dof_copyin_dof(int in, int out, int *ok);
+
+/*
+ * Parse DOF info out of the passed-in dof_helper_t and dof_hdr_t DOF buffer,
+ * and pass it out of OUT in the form of a stream of dof_parser_info_t.
+ */
+void dof_parse_providers(int out, dof_helper_t *dhp, dof_hdr_t *dof);
+
+/*
+ * Shared host and parser-side.
+ */
+/*
+ * Write something to the parser pipe OUT.
+ *
+ * Returns 0 on success or a positive errno value on error.
+ */
+int dof_parser_write_one(int out, const void *buf, size_t size);
+
+#endif /* _DOF_PARSER_H */
diff --git a/libcommon/dof_parser_host.c b/libcommon/dof_parser_host.c
new file mode 100644
index 00000000..feb71902
--- /dev/null
+++ b/libcommon/dof_parser_host.c
@@ -0,0 +1,132 @@
+/*
+ * Oracle Linux DTrace; DOF-consumption and USDT-probe-creation daemon.
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * Licensed under the Universal Permissive License v 1.0 as shown at
+ * http://oss.oracle.com/licenses/upl.
+ */
+
+#include <errno.h>
+#include <poll.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dof_parser.h"
+
+/*
+ * Write BUF to the parser pipe OUT.
+ *
+ * Returns 0 on success or a positive errno value on error.
+ */
+int
+dof_parser_write_one(int out, const void *buf_, size_t size)
+{
+ size_t i;
+ char *buf = (char *) buf_;
+
+ for (i = 0; i < size; ) {
+ size_t ret;
+
+ ret = write(out, buf + i, size - i);
+ if (ret < 0) {
+ switch (errno) {
+ case EINTR:
+ continue;
+ default:
+ return errno;
+ }
+ }
+
+ i += ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Write the DOF to the parser pipe OUT.
+ *
+ * Returns 0 on success or a positive errno value on error.
+ */
+int
+dof_parser_host_write(int out, dof_helper_t *dh, dof_hdr_t *dof)
+{
+ int err;
+
+ if ((err = dof_parser_write_one(out, (char *)dh,
+ sizeof(dof_helper_t))) < 0)
+ return err;
+
+ return dof_parser_write_one(out, (char *)dof,
+ dof->dofh_loadsz);
+}
+
+/*
+ * Read a single DOF structure from a parser pipe. Wait at most TIMEOUT seconds
+ * to do so.
+ *
+ * Returns NULL and sets errno on error.
+ */
+dof_parsed_t *
+dof_parser_host_read(int in, int timeout)
+{
+ size_t i, sz;
+ dof_parsed_t *reply;
+ struct pollfd fd;
+
+ fd.fd = in;
+ fd.events = POLLIN;
+
+ reply = malloc(sizeof(dof_parsed_t));
+ if (!reply)
+ goto err;
+ memset(reply, 0, sizeof(dof_parsed_t));
+
+ /*
+ * On the first read, only read in the size. Decide how much to read
+ * only after that, both to make sure we don't underread and to make
+ * sure we don't *overread* and concatenate part of another message
+ * onto this one.
+ */
+ for (i = 0, sz = offsetof(dof_parsed_t, type); i < sz;) {
+ size_t ret;
+
+ if ((ret = poll(&fd, 1, timeout * 1000)) <= 0)
+ goto err;
+
+ ret = read(in, ((char *) reply) + i, sz - i);
+
+ if (ret <= 0)
+ goto err;
+
+ /*
+ * Fix up the size once it's received. Might be large enough
+ * that we've done the initial size read...
+ */
+ if (i < offsetof(dof_parsed_t, type) &&
+ i + ret >= offsetof(dof_parsed_t, type))
+ sz = reply->size;
+
+ /* Allocate more room if needed for the reply. */
+ if (sz > sizeof(dof_parsed_t)) {
+ dof_parsed_t *new_reply;
+
+ new_reply = realloc(reply, reply->size);
+ if (!new_reply)
+ goto err;
+
+ memset(((char *) new_reply) + i + ret, 0, new_reply->size - (i + ret));
+ reply = new_reply;
+ }
+
+ i += ret;
+ }
+
+ return reply;
+
+err:
+ free(reply);
+ return NULL;
+}
+
--
2.31.1
More information about the DTrace-devel
mailing list