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

Kris Van Hees kris.van.hees at oracle.com
Sat Aug 5 14:47:50 UTC 2023


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