[DTrace-devel] [PATCH v3] Add rodata support

Kris Van Hees kris.van.hees at oracle.com
Thu Aug 17 14:53:22 UTC 2023


This patch provides for read-only data to be placed in its own memory
block (much like the .rodata ELF section).  It lays the groundwork for
future enhancements that support .rodata sections in precompiled BPF
code (as may be generated for static data items), and for the storage
of read-only data that is not constrainted by the maximum string size.

It's first user will be the inet_ntoa6() implementation that needs to
store a read-only lookup table.

The string table can be considered a special case of read-only data
(as evidenced by the clear duplication of code between dt_rodata.c and
dt_strtab.c) and a future patch will convert the strtab implementation
to be a wrapper for a rodata-style memory block.

Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
 libdtrace/Build       |   1 +
 libdtrace/dt_bpf.c    |  29 +++-
 libdtrace/dt_bpf.h    |  38 +++---
 libdtrace/dt_cc.c     |   6 +
 libdtrace/dt_cg.c     |  11 ++
 libdtrace/dt_dctx.h   |   2 +
 libdtrace/dt_dlibs.c  |   2 +
 libdtrace/dt_impl.h   |  10 +-
 libdtrace/dt_open.c   |   1 +
 libdtrace/dt_rodata.c | 307 ++++++++++++++++++++++++++++++++++++++++++
 libdtrace/dt_rodata.h |  36 +++++
 libdtrace/dt_subr.c   |  22 +++
 12 files changed, 439 insertions(+), 26 deletions(-)
 create mode 100644 libdtrace/dt_rodata.c
 create mode 100644 libdtrace/dt_rodata.h

diff --git a/libdtrace/Build b/libdtrace/Build
index a392c5f3..d1b00933 100644
--- a/libdtrace/Build
+++ b/libdtrace/Build
@@ -61,6 +61,7 @@ libdtrace-build_SOURCES = dt_aggregate.c \
 			  dt_provider_sdt.c \
 			  dt_provider_tp.c \
 			  dt_regset.c \
+			  dt_rodata.c \
 			  dt_string.c \
 			  dt_strtab.c \
 			  dt_subr.c \
diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c
index 54086077..f5ab8a2a 100644
--- a/libdtrace/dt_bpf.c
+++ b/libdtrace/dt_bpf.c
@@ -695,10 +695,15 @@ gmap_create_scratchmem(dtrace_hdl_t *dtp)
  * String table map.  This is a global map with a singleton element (key 0)
  * that contains the entire string table as a concatenation of all unique
  * strings (each terminated with a NUL byte).  The string table size is taken
- * from the DTrace consumer handle (dt_strlen).  Extra memory is allocated as a
- * memory block of zeros for initializing memory regions.  Its size is at least
- * the maximum string size to ensure the BPF verifier can validate all access
- * requests for dynamic references to string constants.
+ * from the DTrace consumer handle (dt_strlen).  The read-only data is appended
+ * to the string table, as is a memory block of zeros for initializing memory
+ * regions.  Its size is at least
+ *
+ * In order to ensure the BPF verifier can validate all access requests for
+ * dybamic references to string constants, the size of the read-only data plus
+ * the size of the block of zeros must at least match the maximum string size.
+ * The size of the block of zeros must at least match the maximum read-only
+ * item size.
  */
 static int
 gmap_create_strtab(dtrace_hdl_t *dtp)
@@ -711,13 +716,20 @@ gmap_create_strtab(dtrace_hdl_t *dtp)
 	int		fd, rc, err;
 
 	dtp->dt_strlen = dt_strtab_size(dtp->dt_ccstab);
-	dtp->dt_zerooffset = P2ROUNDUP(dtp->dt_strlen, 8);
+	dtp->dt_rooffset = P2ROUNDUP(dtp->dt_strlen, 8);
+	dtp->dt_rosize = dt_rodata_size(dtp->dt_rodata);
+	dtp->dt_zerooffset = P2ROUNDUP(dtp->dt_rooffset + dtp->dt_rosize, 8);
+	dtp->dt_zerosize = 0;
+	sz = dt_rodata_max_item_size(dtp->dt_rodata);
 
 	/*
 	 * Ensure the zero-filled memory at the end of the strtab is large
 	 * enough to accomodate all needs for such a memory block.
 	 */
-	dtp->dt_zerosize = strsize + 1;
+	if (dtp->dt_rosize < strsize + 1)
+		dtp->dt_zerosize = strsize + 1 - dtp->dt_rosize;
+	if (dtp->dt_zerosize < sz)
+		dtp->dt_zerosize = sz;
 	if (dtp->dt_zerosize < dtp->dt_maxdvarsize)
 		dtp->dt_zerosize = dtp->dt_maxdvarsize;
 	if (dtp->dt_zerosize < dtp->dt_maxtuplesize)
@@ -730,6 +742,7 @@ gmap_create_strtab(dtrace_hdl_t *dtp)
 	if (strtab == NULL)
 		return dt_set_errno(dtp, EDT_NOMEM);
 
+	/* Copy the string table data. */
 	dt_strtab_write(dtp->dt_ccstab, (dt_strtab_write_f *)dt_strtab_copystr,
 			strtab);
 
@@ -745,6 +758,10 @@ gmap_create_strtab(dtrace_hdl_t *dtp)
 		buf += len + 1;
 	}
 
+	/* Copy the read-only data. */
+	dt_rodata_write(dtp->dt_rodata, (dt_rodata_copy_f *)dt_rodata_copy,
+			strtab + dtp->dt_rooffset);
+
 	fd = create_gmap(dtp, "strtab", BPF_MAP_TYPE_ARRAY, sizeof(uint32_t),
 			 sz, 1);
 	if (fd == -1)
diff --git a/libdtrace/dt_bpf.h b/libdtrace/dt_bpf.h
index f414e78e..e64ca3c1 100644
--- a/libdtrace/dt_bpf.h
+++ b/libdtrace/dt_bpf.h
@@ -19,25 +19,27 @@ struct dtrace_hdl;
 extern "C" {
 #endif
 
-#define DT_CONST_EPID	1
-#define DT_CONST_PRID	2
-#define DT_CONST_CLID	3
-#define DT_CONST_ARGC	4
-#define DT_CONST_STBSZ	5
-#define DT_CONST_STRSZ	6
-#define DT_CONST_STKSIZ	7
-#define DT_CONST_BOOTTM	8
-#define DT_CONST_NSPEC	9
-#define DT_CONST_NCPUS	10
-#define DT_CONST_PC	11
-#define DT_CONST_TUPSZ	12
-#define DT_CONST_TASK_PID	13
-#define DT_CONST_TASK_TGID	14
+#define DT_CONST_EPID			1
+#define DT_CONST_PRID			2
+#define DT_CONST_CLID			3
+#define DT_CONST_ARGC			4
+#define DT_CONST_STBSZ			5
+#define DT_CONST_STRSZ			6
+#define DT_CONST_STKSIZ			7
+#define DT_CONST_BOOTTM			8
+#define DT_CONST_NSPEC			9
+#define DT_CONST_NCPUS			10
+#define DT_CONST_PC			11
+#define DT_CONST_TUPSZ			12
+#define DT_CONST_TASK_PID		13
+#define DT_CONST_TASK_TGID		14
 #define DT_CONST_TASK_REAL_PARENT	15
-#define DT_CONST_TASK_COMM	16
-#define DT_CONST_MUTEX_OWNER	17
-#define DT_CONST_RWLOCK_CNTS	18
-#define DT_CONST_ZERO_OFF	19
+#define DT_CONST_TASK_COMM		16
+#define DT_CONST_MUTEX_OWNER		17
+#define DT_CONST_RWLOCK_CNTS		18
+#define DT_CONST_RODATA_OFF		19
+#define DT_CONST_RODATA_SIZE		20
+#define DT_CONST_ZERO_OFF		21
 
 #define DT_BPF_LOG_SIZE_DEFAULT	(UINT32_MAX >> 8)
 #define DT_BPF_LOG_SIZE_SMALL	4096
diff --git a/libdtrace/dt_cc.c b/libdtrace/dt_cc.c
index 88d7f1fe..2293892c 100644
--- a/libdtrace/dt_cc.c
+++ b/libdtrace/dt_cc.c
@@ -2468,6 +2468,12 @@ dt_link_construct(dtrace_hdl_t *dtp, const dt_probe_t *prp, dtrace_difo_t *dp,
 				nrp->dofr_data = total_offset;
 				continue;
 			}
+			case DT_CONST_RODATA_OFF:
+				nrp->dofr_data = dtp->dt_rooffset;
+				continue;
+			case DT_CONST_RODATA_SIZE:
+				nrp->dofr_data = dtp->dt_rosize;
+				continue;
 			case DT_CONST_ZERO_OFF:
 				nrp->dofr_data = dtp->dt_zerooffset;
 				continue;
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index bb6cff4a..6a1413f0 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -56,6 +56,7 @@ dt_cg_tramp_prologue_act(dt_pcb_t *pcb, dt_activity_t act)
 	dt_ident_t	*mem = dt_dlib_get_map(dtp, "mem");
 	dt_ident_t	*state = dt_dlib_get_map(dtp, "state");
 	dt_ident_t	*prid = dt_dlib_get_var(pcb->pcb_hdl, "PRID");
+	dt_ident_t	*ro_off = dt_dlib_get_var(dtp, "RODATA_OFF");
 	uint_t		lbl_exit = pcb->pcb_exitlbl;
 
 	assert(aggs != NULL);
@@ -206,6 +207,16 @@ dt_cg_tramp_prologue_act(dt_pcb_t *pcb, dt_activity_t act)
 	} while(0)
 
 	DT_CG_STORE_MAP_PTR("strtab", DCTX_STRTAB);
+
+	/*
+	 * We know that a pointer to the strtab data is in %r0 (because that is
+	 * where the DT_CG_STORE_MAP_PTR() macro left it, and we know that dctx
+	 * is in %r9.  So, we can just add RODATA_OFF to %r0, and store that in
+	 * [%r9 + DCTX_RODATA].
+	 */
+	emite(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, -1), ro_off);
+	emit(dlp,  BPF_STORE(BPF_DW, BPF_REG_9, DCTX_RODATA, BPF_REG_0));
+
 	if (dtp->dt_options[DTRACEOPT_SCRATCHSIZE] > 0)
 		DT_CG_STORE_MAP_PTR("scratchmem", DCTX_SCRATCHMEM);
 	if (dt_idhash_datasize(dtp->dt_globals) > 0)
diff --git a/libdtrace/dt_dctx.h b/libdtrace/dt_dctx.h
index 315bdc37..86bfe6f2 100644
--- a/libdtrace/dt_dctx.h
+++ b/libdtrace/dt_dctx.h
@@ -58,6 +58,7 @@ typedef struct dt_dctx {
 	char		*mem;		/* General scratch memory */
 	char		*scratchmem;	/* Scratch space for alloca, etc */
 	char		*strtab;	/* String constant table */
+	char		*rodata;	/* Read-only data */
 	char		*agg;		/* Aggregation data */
 	char		*gvars;		/* Global variables */
 	char		*lvars;		/* Local variables */
@@ -70,6 +71,7 @@ typedef struct dt_dctx {
 #define DCTX_MEM	offsetof(dt_dctx_t, mem)
 #define DCTX_SCRATCHMEM	offsetof(dt_dctx_t, scratchmem)
 #define DCTX_STRTAB	offsetof(dt_dctx_t, strtab)
+#define DCTX_RODATA	offsetof(dt_dctx_t, rodata)
 #define DCTX_AGG	offsetof(dt_dctx_t, agg)
 #define DCTX_GVARS	offsetof(dt_dctx_t, gvars)
 #define DCTX_LVARS	offsetof(dt_dctx_t, lvars)
diff --git a/libdtrace/dt_dlibs.c b/libdtrace/dt_dlibs.c
index 1005e027..02a24ad2 100644
--- a/libdtrace/dt_dlibs.c
+++ b/libdtrace/dt_dlibs.c
@@ -91,6 +91,8 @@ static const dt_ident_t		dt_bpf_symbols[] = {
 	DT_BPF_SYMBOL_ID(TASK_COMM, DT_IDENT_SCALAR, DT_CONST_TASK_COMM),
 	DT_BPF_SYMBOL_ID(MUTEX_OWNER, DT_IDENT_SCALAR, DT_CONST_MUTEX_OWNER),
 	DT_BPF_SYMBOL_ID(RWLOCK_CNTS, DT_IDENT_SCALAR, DT_CONST_RWLOCK_CNTS),
+	DT_BPF_SYMBOL_ID(RODATA_OFF, DT_IDENT_SCALAR, DT_CONST_RODATA_OFF),
+	DT_BPF_SYMBOL_ID(RODATA_SIZE, DT_IDENT_SCALAR, DT_CONST_RODATA_SIZE),
 	DT_BPF_SYMBOL_ID(ZERO_OFF, DT_IDENT_SCALAR, DT_CONST_ZERO_OFF),
 
 	/* End-of-list marker */
diff --git a/libdtrace/dt_impl.h b/libdtrace/dt_impl.h
index 42a7ccaa..adc8d8db 100644
--- a/libdtrace/dt_impl.h
+++ b/libdtrace/dt_impl.h
@@ -34,6 +34,7 @@ extern "C" {
 #include <dt_bpf_maps.h>
 #include <dt_parser.h>
 #include <dt_regset.h>
+#include <dt_rodata.h>
 #include <dt_strtab.h>
 #include <dt_symtab.h>
 #include <dt_ident.h>
@@ -306,14 +307,17 @@ struct dtrace_hdl {
 	dt_idhash_t *dt_tls;	/* hash table of thread-local identifiers */
 	dt_idhash_t *dt_bpfsyms;/* hash table of BPF identifiers */
 	dt_strtab_t *dt_ccstab;	/* global string table (during compilation) */
+	dt_rodata_t *dt_rodata;	/* global read-only data */
 	uint_t dt_strlen;	/* global string table (runtime) size */
+	uint_t dt_rooffset;	/* read-only data offset */
+	uint_t dt_rosize;	/* read-only data size */
+	uint_t dt_zerooffset;	/* zero region, offset */
+	uint_t dt_zerosize;	/* zero region, size */
 	uint_t dt_maxreclen;	/* largest record size across programs */
 	uint_t dt_maxdvarsize;	/* largest dynamic variable across programs */
 	uint_t dt_maxtuplesize;	/* largest tuple across programs */
 	uint_t dt_maxlvaralloc;	/* largest lvar alloc across pcbs */
 	uint_t dt_maxaggdsize;	/* largest aggregation data sizw */
-	uint_t dt_zerosize;	/* zero region, size */
-	uint_t dt_zerooffset;	/* zero region, offset */
 	dt_tstring_t *dt_tstrings; /* temporary string slots */
 	dt_list_t dt_modlist;	/* linked list of dt_module_t's */
 	dt_htab_t *dt_mods;	/* hash table of dt_module_t's */
@@ -725,6 +729,8 @@ extern int dt_version_defined(dt_version_t);
 
 extern int dt_str2kver(const char *, dt_version_t *);
 
+extern uint32_t dt_gen_hval(const char *, uint32_t, size_t);
+
 /*
  * Miscellaneous internal libdtrace interfaces.  The definitions below are for
  * libdtrace routines that do not yet merit their own separate header file.
diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c
index b6a0b623..a90601b0 100644
--- a/libdtrace/dt_open.c
+++ b/libdtrace/dt_open.c
@@ -888,6 +888,7 @@ dt_vopen(int version, int flags, int *errp,
 	    DIF_VAR_OTHER_UBASE, DIF_VAR_OTHER_MAX);
 
 	dtp->dt_ccstab = dt_strtab_create(BUFSIZ);
+	dtp->dt_rodata = dt_rodata_create(BUFSIZ);
 	dtp->dt_strlen = 0;
 
 	if (dtp->dt_macros == NULL || dtp->dt_aggs == NULL ||
diff --git a/libdtrace/dt_rodata.c b/libdtrace/dt_rodata.c
new file mode 100644
index 00000000..87f2724c
--- /dev/null
+++ b/libdtrace/dt_rodata.c
@@ -0,0 +1,307 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2006, 2023, 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/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <dt_rodata.h>
+#include <dt_string.h>
+#include <dt_impl.h>
+
+typedef struct dt_rohash	dt_rohash_t;
+struct dt_rohash {
+	const char	*data;		/* pointer to actual data */
+	size_t		buf;		/* index of data buffer */
+	size_t		off;		/* offset in bytes of this item */
+	size_t		len;		/* length in bytes of this item */
+	dt_rohash_t	*next;		/* next item in hash chain */
+};
+
+struct dt_rodata {
+	dt_rohash_t	**hash;		/* array of hash buckets */
+	size_t		hashsz;		/* size of hash bucket array */
+	char		**bufs;		/* array of buffer pointers */
+	char		*ptr;		/* pointer to current buffer location */
+	size_t		nbufs;		/* size of buffer pointer array */
+	size_t		bufsz;		/* size of individual buffer */
+	size_t		count;		/* total number of items in rodata */
+	size_t		size;		/* total size of items in bytes */
+	size_t		item_size;	/* maximum item size */
+};
+
+static int
+dt_rodata_grow(dt_rodata_t *dp)
+{
+	char	*ptr, **bufs;
+
+	ptr = malloc(dp->bufsz);
+	if (ptr == NULL)
+		return -1;
+
+	bufs = realloc(dp->bufs, (dp->nbufs + 1) * sizeof(char *));
+	if (bufs == NULL) {
+		free(ptr);
+		return -1;
+	}
+
+	dp->nbufs++;
+	dp->bufs = bufs;
+	dp->ptr = ptr;
+	dp->bufs[dp->nbufs - 1] = dp->ptr;
+
+	return 0;
+}
+
+dt_rodata_t *
+dt_rodata_create(size_t bufsz)
+{
+	dt_rodata_t	*dp = malloc(sizeof(dt_rodata_t));
+	size_t		nbuckets = _dtrace_strbuckets;
+
+	assert(bufsz != 0);
+
+	if (dp == NULL)
+		return NULL;
+
+	memset(dp, 0, sizeof(dt_rodata_t));
+	dp->hash = malloc(nbuckets * sizeof(dt_rohash_t *));
+	if (dp->hash == NULL)
+		goto err;
+
+	memset(dp->hash, 0, nbuckets * sizeof(dt_rohash_t *));
+	dp->hashsz = nbuckets;
+	dp->bufs = NULL;
+	dp->ptr = NULL;
+	dp->nbufs = 0;
+	dp->bufsz = bufsz;
+
+	if (dt_rodata_grow(dp) == -1)
+		goto err;
+
+	/* Pre-populate the read-only data with a 0-byte at offset 0.  */
+	*dp->ptr++ = 0;
+	dp->size = 1;
+	dp->count = 1;
+
+	return dp;
+
+err:
+	dt_rodata_destroy(dp);
+	return NULL;
+}
+
+void
+dt_rodata_destroy(dt_rodata_t *dp)
+{
+	dt_rohash_t	*hp, *hq;
+	size_t		i;
+
+	if (dp == NULL)
+		return;
+
+	for (i = 0; i < dp->hashsz; i++) {
+		for (hp = dp->hash[i]; hp != NULL; hp = hq) {
+			hq = hp->next;
+			free(hp);
+		}
+	}
+
+	for (i = 0; i < dp->nbufs; i++)
+		free(dp->bufs[i]);
+
+	if (dp->hash != NULL)
+		free(dp->hash);
+	if (dp->bufs != NULL)
+		free(dp->bufs);
+
+	free(dp);
+}
+
+static int
+dt_rodata_compare(dt_rodata_t *dp, dt_rohash_t *hp, const char *ptr, size_t len)
+{
+	size_t		b = hp->buf;
+	const char	*buf = hp->data;
+	size_t		resid, n;
+	int		rv;
+
+	while (len != 0) {
+		if (buf == dp->bufs[b] + dp->bufsz)
+			buf = dp->bufs[++b];
+
+		resid = dp->bufs[b] + dp->bufsz - buf;
+		n = MIN(resid, len);
+
+		if ((rv = memcmp(buf, ptr, n)) != 0)
+			return rv;
+
+		buf += n;
+		ptr += n;
+		len -= n;
+	}
+
+	return 0;
+}
+
+static int
+dt_rodata_copyin(dt_rodata_t *dp, const char *ptr, size_t len)
+{
+	char	*old_p = dp->ptr;
+	size_t	old_n = dp->nbufs;
+	size_t	b = dp->nbufs - 1;
+	size_t	resid, n;
+
+	while (len != 0) {
+		if (dp->ptr == dp->bufs[b] + dp->bufsz) {
+			if (dt_rodata_grow(dp) == -1)
+				goto err;
+			b++;
+		}
+
+		resid = dp->bufs[b] + dp->bufsz - dp->ptr;
+		n = MIN(resid, len);
+		memcpy(dp->ptr, ptr, n);
+
+		dp->ptr += n;
+		ptr += n;
+		len -= n;
+	}
+
+	return 0;
+
+err:
+	while (dp->nbufs != old_n)
+		free(dp->bufs[--dp->nbufs]);
+
+	dp->ptr = old_p;
+	return -1;
+}
+
+static ssize_t
+dt_rodata_xindex(dt_rodata_t *dp, const char *ptr, size_t len, uint32_t h)
+{
+	dt_rohash_t	*hp;
+
+	for (hp = dp->hash[h]; hp != NULL; hp = hp->next) {
+		if (dt_rodata_compare(dp, hp, ptr, len) == 0)
+			return hp->off;
+	}
+
+	return -1;
+}
+
+ssize_t
+dt_rodata_index(dt_rodata_t *dp, const char *ptr, size_t len)
+{
+	ssize_t		rc;
+	uint32_t	h;
+
+	if (ptr == NULL || ptr[0] == '\0')
+		return 0;	/* NULL or a single 0-byte are at offset 0. */
+
+	h = dt_gen_hval(ptr, len, len) % dp->hashsz;
+	rc = dt_rodata_xindex(dp, ptr, len, h);
+
+	return rc;
+}
+
+ssize_t
+dt_rodata_insert(dt_rodata_t *dp, const char *ptr, size_t len)
+{
+	dt_rohash_t	*hp;
+	ssize_t		off;
+	uint32_t	h;
+
+	if (ptr == NULL || ptr[0] == '\0')
+		return 0;	/* NULL or a single 0-byte are at offset 0. */
+
+	h = dt_gen_hval(ptr, len, len) % dp->hashsz;
+	off = dt_rodata_xindex(dp, ptr, len, h);
+	if (off != -1)
+		return off;
+
+	/*
+	 * Create a new hash bucket, initialize it, and insert it at the front
+	 * of the hash chain for the appropriate bucket.
+	 */
+	if ((hp = malloc(sizeof(dt_rohash_t))) == NULL)
+		return -1L;
+
+	hp->data = dp->ptr;
+	hp->buf = dp->nbufs - 1;
+	hp->off = dp->size;
+	hp->len = len;
+	hp->next = dp->hash[h];
+
+	/*
+	 * Now copy the data into our buffer list, and then update the global
+	 * counts of items and bytes.  Return the item's byte offset.
+	 */
+	if (dt_rodata_copyin(dp, ptr, len) == -1) {
+		free(hp);
+		return -1L;
+	}
+
+	dp->count++;
+	dp->size += len;
+	dp->hash[h] = hp;
+	if (dp->item_size < len)
+		dp->item_size = len;
+
+	return hp->off;
+}
+
+size_t
+dt_rodata_size(const dt_rodata_t *dp)
+{
+	return dp->size;
+}
+
+/*
+ * Return the size of the largest read-only data item.
+ */
+size_t
+dt_rodata_max_item_size(const dt_rodata_t *dp)
+{
+	return dp->item_size;
+}
+
+/*
+ * Copy data from the compile-time read-only data into a DIFO-style read-only
+ * storage memory block.
+ */
+ssize_t
+dt_rodata_copy(const char *s, size_t n, size_t off, char *buf)
+{
+	memcpy(buf + off, s, n);
+	return n;
+}
+
+ssize_t
+dt_rodata_write(const dt_rodata_t *dp, dt_rodata_copy_f *func, void *private)
+{
+	ssize_t	res, total = 0;
+	size_t	i;
+	size_t	n;
+
+	for (i = 0; i < dp->nbufs; i++, total += res) {
+		if (i == dp->nbufs - 1)
+			n = dp->ptr - dp->bufs[i];
+		else
+			n = dp->bufsz;
+
+		if ((res = func(dp->bufs[i], n, total, private)) <= 0)
+			break;
+	}
+
+	if (total == 0 && dp->size != 0)
+		return -1;
+
+	return total;
+}
diff --git a/libdtrace/dt_rodata.h b/libdtrace/dt_rodata.h
new file mode 100644
index 00000000..2aad9bde
--- /dev/null
+++ b/libdtrace/dt_rodata.h
@@ -0,0 +1,36 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2006, 2021, 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	_DT_RODATA_H
+#define	_DT_RODATA_H
+
+#include <sys/types.h>
+#include <sys/dtrace_types.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef struct dt_rodata	dt_rodata_t;
+
+typedef ssize_t dt_rodata_copy_f(const char *, size_t, size_t, void *);
+
+extern dt_rodata_t *dt_rodata_create(size_t);
+extern void dt_rodata_destroy(dt_rodata_t *);
+extern ssize_t dt_rodata_index(dt_rodata_t *, const char *, size_t);
+extern ssize_t dt_rodata_insert(dt_rodata_t *, const char *, size_t);
+extern size_t dt_rodata_size(const dt_rodata_t *);
+extern size_t dt_rodata_max_item_size(const dt_rodata_t *);
+extern ssize_t dt_rodata_copy(const char *, size_t, size_t, char *);
+extern ssize_t dt_rodata_write(const dt_rodata_t *, dt_rodata_copy_f *,
+			       void *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _DT_RODATA_H */
diff --git a/libdtrace/dt_subr.c b/libdtrace/dt_subr.c
index 300c1a8d..6af2e0c8 100644
--- a/libdtrace/dt_subr.c
+++ b/libdtrace/dt_subr.c
@@ -985,3 +985,25 @@ dt_str2kver(const char *kverstr, dt_version_t *vp)
 
 	return 0;
 }
+
+/*
+ * Compute a 32-bit hash value for a memory block of given size.
+ */
+uint32_t dt_gen_hval(const char *p, uint32_t hval, size_t len)
+{
+	uint32_t	g;
+
+	if (!p || len == 0)
+		return hval;
+
+	while (len--) {
+		hval = (hval << 4) + *p++;
+		g = hval & 0xf0000000;
+		if (g != 0) {
+			hval ^= (g >> 24);
+			hval ^= g;
+		}
+	}
+
+	return hval;
+}
-- 
2.40.1




More information about the DTrace-devel mailing list