diff options
-rw-r--r-- | libctf/ChangeLog | 51 | ||||
-rw-r--r-- | libctf/Makefile.am | 2 | ||||
-rw-r--r-- | libctf/Makefile.in | 12 | ||||
-rw-r--r-- | libctf/ctf-create.c | 183 | ||||
-rw-r--r-- | libctf/ctf-impl.h | 43 | ||||
-rw-r--r-- | libctf/ctf-open.c | 2 | ||||
-rw-r--r-- | libctf/ctf-string.c | 330 | ||||
-rw-r--r-- | libctf/ctf-util.c | 35 |
8 files changed, 520 insertions, 138 deletions
diff --git a/libctf/ChangeLog b/libctf/ChangeLog index 21b62e1..d07d5dc 100644 --- a/libctf/ChangeLog +++ b/libctf/ChangeLog @@ -1,5 +1,56 @@ 2019-06-28 Nick Alcock <nick.alcock@oracle.com> + * ctf-impl.h: (struct ctf_strs_writable): New, non-const version of + struct ctf_strs. + (struct ctf_dtdef): Note that dtd_data.ctt_name is unpopulated. + (struct ctf_str_atom): New, disambiguated single string. + (struct ctf_str_atom_ref): New, points to some other location that + references this string's offset. + (struct ctf_file): New members ctf_str_atoms and ctf_str_num_refs. + Remove member ctf_dtvstrlen: we no longer track the total strlen + as we add strings. + (ctf_str_create_atoms): Declare new function in ctf-string.c. + (ctf_str_free_atoms): Likewise. + (ctf_str_add): Likewise. + (ctf_str_add_ref): Likewise. + (ctf_str_rollback): Likewise. + (ctf_str_purge_refs): Likewise. + (ctf_str_write_strtab): Likewise. + (ctf_realloc): Declare new function in ctf-util.c. + + * ctf-open.c (ctf_bufopen): Create the atoms table. + (ctf_file_close): Destroy it. + * ctf-create.c (ctf_update): Copy-and-free it on update. No longer + special-case the position of the parname string. Construct the + strtab by calling ctf_str_add_ref and ctf_str_write_strtab after the + rest of each buffer element is constructed, not via open-coding: + realloc the CTF buffer and append the strtab to it. No longer + maintain ctf_dtvstrlen. Sort the variable entry table later, after + strtab construction. + (ctf_copy_membnames): Remove: integrated into ctf_copy_{s,l,e}members. + (ctf_copy_smembers): Drop the string offset: call ctf_str_add_ref + after buffer element construction instead. + (ctf_copy_lmembers): Likewise. + (ctf_copy_emembers): Likewise. + (ctf_create): No longer maintain the ctf_dtvstrlen. + (ctf_dtd_delete): Likewise. + (ctf_dvd_delete): Likewise. + (ctf_add_generic): Likewise. + (ctf_add_enumerator): Likewise. + (ctf_add_member_offset): Likewise. + (ctf_add_variable): Likewise. + (membadd): Likewise. + * ctf-util.c (ctf_realloc): New, wrapper around realloc that aborts + if there are active ctf_str_num_refs. + (ctf_strraw): Move to ctf-string.c. + (ctf_strptr): Likewise. + * ctf-string.c: New file, strtab manipulation. + + * Makefile.am (libctf_a_SOURCES): Add it. + * Makefile.in: Regenerate. + +2019-06-28 Nick Alcock <nick.alcock@oracle.com> + * ctf-impl.h (ctf_hash_iter_f): New. (ctf_dynhash_iter): New declaration. (ctf_dynhash_iter_remove): New declaration. diff --git a/libctf/Makefile.am b/libctf/Makefile.am index 926c991..43fc78a 100644 --- a/libctf/Makefile.am +++ b/libctf/Makefile.am @@ -34,7 +34,7 @@ noinst_LIBRARIES = libctf.a libctf_a_SOURCES = ctf-archive.c ctf-dump.c ctf-create.c ctf-decl.c ctf-error.c \ ctf-hash.c ctf-labels.c ctf-lookup.c ctf-open.c ctf-open-bfd.c \ - ctf-subr.c ctf-types.c ctf-util.c + ctf-string.c ctf-subr.c ctf-types.c ctf-util.c if NEED_CTF_QSORT_R libctf_a_SOURCES += ctf-qsort_r.c endif diff --git a/libctf/Makefile.in b/libctf/Makefile.in index 4fea156..c898eb4 100644 --- a/libctf/Makefile.in +++ b/libctf/Makefile.in @@ -132,14 +132,15 @@ libctf_a_AR = $(AR) $(ARFLAGS) libctf_a_LIBADD = am__libctf_a_SOURCES_DIST = ctf-archive.c ctf-dump.c ctf-create.c \ ctf-decl.c ctf-error.c ctf-hash.c ctf-labels.c ctf-lookup.c \ - ctf-open.c ctf-open-bfd.c ctf-subr.c ctf-types.c ctf-util.c \ - ctf-qsort_r.c + ctf-open.c ctf-open-bfd.c ctf-string.c ctf-subr.c ctf-types.c \ + ctf-util.c ctf-qsort_r.c @NEED_CTF_QSORT_R_TRUE@am__objects_1 = ctf-qsort_r.$(OBJEXT) am_libctf_a_OBJECTS = ctf-archive.$(OBJEXT) ctf-dump.$(OBJEXT) \ ctf-create.$(OBJEXT) ctf-decl.$(OBJEXT) ctf-error.$(OBJEXT) \ ctf-hash.$(OBJEXT) ctf-labels.$(OBJEXT) ctf-lookup.$(OBJEXT) \ - ctf-open.$(OBJEXT) ctf-open-bfd.$(OBJEXT) ctf-subr.$(OBJEXT) \ - ctf-types.$(OBJEXT) ctf-util.$(OBJEXT) $(am__objects_1) + ctf-open.$(OBJEXT) ctf-open-bfd.$(OBJEXT) ctf-string.$(OBJEXT) \ + ctf-subr.$(OBJEXT) ctf-types.$(OBJEXT) ctf-util.$(OBJEXT) \ + $(am__objects_1) libctf_a_OBJECTS = $(am_libctf_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) @@ -331,7 +332,7 @@ AM_CFLAGS = -std=gnu99 @ac_libctf_warn_cflags@ @warn@ @c_warn@ @WARN_PEDANTIC@ @ noinst_LIBRARIES = libctf.a libctf_a_SOURCES = ctf-archive.c ctf-dump.c ctf-create.c ctf-decl.c \ ctf-error.c ctf-hash.c ctf-labels.c ctf-lookup.c ctf-open.c \ - ctf-open-bfd.c ctf-subr.c ctf-types.c ctf-util.c \ + ctf-open-bfd.c ctf-string.c ctf-subr.c ctf-types.c ctf-util.c \ $(am__append_1) all: config.h $(MAKE) $(AM_MAKEFLAGS) all-am @@ -412,6 +413,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-open-bfd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-open.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-qsort_r.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-string.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-subr.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-types.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-util.Po@am__quote@ diff --git a/libctf/ctf-create.c b/libctf/ctf-create.c index 86695f5..6ab0cf3 100644 --- a/libctf/ctf-create.c +++ b/libctf/ctf-create.c @@ -29,10 +29,8 @@ /* To create an empty CTF container, we just declare a zeroed header and call ctf_bufopen() on it. If ctf_bufopen succeeds, we mark the new container r/w - and initialize the dynamic members. We set dtvstrlen to 1 to reserve the - first byte of the string table for a \0 byte, and we start assigning type - IDs at 1 because type ID 0 is used as a sentinel and a not-found - indicator. */ + and initialize the dynamic members. We start assigning type IDs at 1 because + type ID 0 is used as a sentinel and a not-found indicator. */ ctf_file_t * ctf_create (int *errp) @@ -82,7 +80,6 @@ ctf_create (int *errp) fp->ctf_dtbyname = dtbyname; fp->ctf_dthash = dthash; fp->ctf_dvhash = dvhash; - fp->ctf_dtvstrlen = 1; fp->ctf_dtnextid = 1; fp->ctf_dtoldid = 0; fp->ctf_snapshots = 0; @@ -101,25 +98,24 @@ ctf_create (int *errp) } static unsigned char * -ctf_copy_smembers (ctf_dtdef_t *dtd, uint32_t soff, unsigned char *t) +ctf_copy_smembers (ctf_file_t *fp, ctf_dtdef_t *dtd, unsigned char *t) { ctf_dmdef_t *dmd = ctf_list_next (&dtd->dtd_u.dtu_members); ctf_member_t ctm; for (; dmd != NULL; dmd = ctf_list_next (dmd)) { - if (dmd->dmd_name) - { - ctm.ctm_name = soff; - soff += strlen (dmd->dmd_name) + 1; - } - else - ctm.ctm_name = 0; + ctf_member_t *copied; + ctm.ctm_name = 0; ctm.ctm_type = (uint32_t) dmd->dmd_type; ctm.ctm_offset = (uint32_t) dmd->dmd_offset; memcpy (t, &ctm, sizeof (ctm)); + copied = (ctf_member_t *) t; + if (dmd->dmd_name) + ctf_str_add_ref (fp, dmd->dmd_name, &copied->ctm_name); + t += sizeof (ctm); } @@ -127,26 +123,25 @@ ctf_copy_smembers (ctf_dtdef_t *dtd, uint32_t soff, unsigned char *t) } static unsigned char * -ctf_copy_lmembers (ctf_dtdef_t *dtd, uint32_t soff, unsigned char *t) +ctf_copy_lmembers (ctf_file_t *fp, ctf_dtdef_t *dtd, unsigned char *t) { ctf_dmdef_t *dmd = ctf_list_next (&dtd->dtd_u.dtu_members); ctf_lmember_t ctlm; for (; dmd != NULL; dmd = ctf_list_next (dmd)) { - if (dmd->dmd_name) - { - ctlm.ctlm_name = soff; - soff += strlen (dmd->dmd_name) + 1; - } - else - ctlm.ctlm_name = 0; + ctf_lmember_t *copied; + ctlm.ctlm_name = 0; ctlm.ctlm_type = (uint32_t) dmd->dmd_type; ctlm.ctlm_offsethi = CTF_OFFSET_TO_LMEMHI (dmd->dmd_offset); ctlm.ctlm_offsetlo = CTF_OFFSET_TO_LMEMLO (dmd->dmd_offset); memcpy (t, &ctlm, sizeof (ctlm)); + copied = (ctf_lmember_t *) t; + if (dmd->dmd_name) + ctf_str_add_ref (fp, dmd->dmd_name, &copied->ctlm_name); + t += sizeof (ctlm); } @@ -154,41 +149,25 @@ ctf_copy_lmembers (ctf_dtdef_t *dtd, uint32_t soff, unsigned char *t) } static unsigned char * -ctf_copy_emembers (ctf_dtdef_t *dtd, uint32_t soff, unsigned char *t) +ctf_copy_emembers (ctf_file_t *fp, ctf_dtdef_t *dtd, unsigned char *t) { ctf_dmdef_t *dmd = ctf_list_next (&dtd->dtd_u.dtu_members); ctf_enum_t cte; for (; dmd != NULL; dmd = ctf_list_next (dmd)) { - cte.cte_name = soff; + ctf_enum_t *copied; + cte.cte_value = dmd->dmd_value; - soff += strlen (dmd->dmd_name) + 1; memcpy (t, &cte, sizeof (cte)); + copied = (ctf_enum_t *) t; + ctf_str_add_ref (fp, dmd->dmd_name, &copied->cte_name); t += sizeof (cte); } return t; } -static unsigned char * -ctf_copy_membnames (ctf_dtdef_t *dtd, unsigned char *s) -{ - ctf_dmdef_t *dmd = ctf_list_next (&dtd->dtd_u.dtu_members); - size_t len; - - for (; dmd != NULL; dmd = ctf_list_next (dmd)) - { - if (dmd->dmd_name == NULL) - continue; /* Skip anonymous members. */ - len = strlen (dmd->dmd_name) + 1; - memcpy (s, dmd->dmd_name, len); - s += len; - } - - return s; -} - /* Sort a newly-constructed static variable array. */ static int @@ -220,15 +199,16 @@ int ctf_update (ctf_file_t *fp) { ctf_file_t ofp, *nfp; - ctf_header_t hdr; + ctf_header_t hdr, *hdrp; ctf_dtdef_t *dtd; ctf_dvdef_t *dvd; ctf_varent_t *dvarents; + ctf_strs_writable_t strtab; - unsigned char *s, *s0, *t; + unsigned char *t; unsigned long i; size_t buf_size, type_size, nvars; - void *buf; + unsigned char *buf, *newbuf; int err; if (!(fp->ctf_flags & LCTF_RDWR)) @@ -247,9 +227,6 @@ ctf_update (ctf_file_t *fp) hdr.cth_magic = CTF_MAGIC; hdr.cth_version = CTF_VERSION; - if (fp->ctf_flags & LCTF_CHILD) - hdr.cth_parname = 1; /* parname added just below. */ - /* Iterate through the dynamic type definition list and compute the size of the CTF type section we will need to generate. */ @@ -298,15 +275,13 @@ ctf_update (ctf_file_t *fp) for (nvars = 0, dvd = ctf_list_next (&fp->ctf_dvdefs); dvd != NULL; dvd = ctf_list_next (dvd), nvars++); - /* Fill in the string table and type offset and size, compute the size - of the entire CTF buffer we need, and then allocate a new buffer and - memcpy the finished header to the start of the buffer. */ + /* Compute the size of the CTF buffer we need, sans only the string table, + then allocate a new buffer and memcpy the finished header to the start of + the buffer. (We will adjust this later with strtab length info.) */ hdr.cth_typeoff = hdr.cth_varoff + (nvars * sizeof (ctf_varent_t)); hdr.cth_stroff = hdr.cth_typeoff + type_size; - hdr.cth_strlen = fp->ctf_dtvstrlen; - if (fp->ctf_parname != NULL) - hdr.cth_strlen += strlen (fp->ctf_parname) + 1; + hdr.cth_strlen = 0; buf_size = sizeof (ctf_header_t) + hdr.cth_stroff + hdr.cth_strlen; @@ -315,63 +290,45 @@ ctf_update (ctf_file_t *fp) memcpy (buf, &hdr, sizeof (ctf_header_t)); t = (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_varoff; - s = s0 = (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_stroff; - - s[0] = '\0'; - s++; - if (fp->ctf_parname != NULL) - { - memcpy (s, fp->ctf_parname, strlen (fp->ctf_parname) + 1); - s += strlen (fp->ctf_parname) + 1; - } + hdrp = (ctf_header_t *) buf; + if ((fp->ctf_flags & LCTF_CHILD) && (fp->ctf_parname != NULL)) + ctf_str_add_ref (fp, fp->ctf_parname, &hdrp->cth_parname); - /* Work over the variable list, translating everything into - ctf_varent_t's and filling out the string table, then sort the buffer - of ctf_varent_t's. */ + /* Work over the variable list, translating everything into ctf_varent_t's and + prepping the string table. */ dvarents = (ctf_varent_t *) t; for (i = 0, dvd = ctf_list_next (&fp->ctf_dvdefs); dvd != NULL; dvd = ctf_list_next (dvd), i++) { ctf_varent_t *var = &dvarents[i]; - size_t len = strlen (dvd->dvd_name) + 1; - var->ctv_name = (uint32_t) (s - s0); + ctf_str_add_ref (fp, dvd->dvd_name, &var->ctv_name); var->ctv_type = dvd->dvd_type; - memcpy (s, dvd->dvd_name, len); - s += len; } assert (i == nvars); - ctf_qsort_r (dvarents, nvars, sizeof (ctf_varent_t), ctf_sort_var, s0); t += sizeof (ctf_varent_t) * nvars; assert (t == (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_typeoff); - /* We now take a final lap through the dynamic type definition list and - copy the appropriate type records and strings to the output buffer. */ + /* We now take a final lap through the dynamic type definition list and copy + the appropriate type records to the output buffer, noting down the + strings as we go. */ for (dtd = ctf_list_next (&fp->ctf_dtdefs); dtd != NULL; dtd = ctf_list_next (dtd)) { - uint32_t kind = LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info); uint32_t vlen = LCTF_INFO_VLEN (fp, dtd->dtd_data.ctt_info); ctf_array_t cta; uint32_t encoding; size_t len; + ctf_stype_t *copied; - if (dtd->dtd_name != NULL) - { - dtd->dtd_data.ctt_name = (uint32_t) (s - s0); - len = strlen (dtd->dtd_name) + 1; - memcpy (s, dtd->dtd_name, len); - s += len; - } - else - dtd->dtd_data.ctt_name = 0; + dtd->dtd_data.ctt_name = 0; if (dtd->dtd_data.ctt_size != CTF_LSIZE_SENT) len = sizeof (ctf_stype_t); @@ -379,6 +336,9 @@ ctf_update (ctf_file_t *fp) len = sizeof (ctf_type_t); memcpy (t, &dtd->dtd_data, len); + copied = (ctf_stype_t *) t; /* name is at the start: constant offset. */ + if (dtd->dtd_name) + ctf_str_add_ref (fp, dtd->dtd_name, &copied->ctt_name); t += len; switch (kind) @@ -432,24 +392,47 @@ ctf_update (ctf_file_t *fp) case CTF_K_STRUCT: case CTF_K_UNION: if (dtd->dtd_data.ctt_size < CTF_LSTRUCT_THRESH) - t = ctf_copy_smembers (dtd, (uint32_t) (s - s0), t); + t = ctf_copy_smembers (fp, dtd, t); else - t = ctf_copy_lmembers (dtd, (uint32_t) (s - s0), t); - s = ctf_copy_membnames (dtd, s); + t = ctf_copy_lmembers (fp, dtd, t); break; case CTF_K_ENUM: - t = ctf_copy_emembers (dtd, (uint32_t) (s - s0), t); - s = ctf_copy_membnames (dtd, s); + t = ctf_copy_emembers (fp, dtd, t); break; } } assert (t == (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_stroff); + /* Construct the final string table and fill out all the string refs with the + final offsets. Then purge the refs list, because we're about to move this + strtab onto the end of the buf, invalidating all the offsets. */ + strtab = ctf_str_write_strtab (fp); + ctf_str_purge_refs (fp); + + /* Now the string table is constructed, we can sort the buffer of + ctf_varent_t's. */ + ctf_qsort_r (dvarents, nvars, sizeof (ctf_varent_t), ctf_sort_var, + strtab.cts_strs); + + if ((newbuf = ctf_realloc (fp, buf, buf_size + strtab.cts_len)) == NULL) + { + ctf_free (buf); + ctf_free (strtab.cts_strs); + return (ctf_set_errno (fp, EAGAIN)); + } + buf = newbuf; + memcpy (buf + buf_size, strtab.cts_strs, strtab.cts_len); + hdrp = (ctf_header_t *) buf; + hdrp->cth_strlen = strtab.cts_len; + buf_size += hdrp->cth_strlen; + ctf_free (strtab.cts_strs); + /* Finally, we are ready to ctf_simple_open() the new container. If this is successful, we then switch nfp and fp and free the old container. */ - if ((nfp = ctf_simple_open (buf, buf_size, NULL, 0, 0, NULL, 0, &err)) == NULL) + if ((nfp = ctf_simple_open ((char *) buf, buf_size, NULL, 0, 0, NULL, + 0, &err)) == NULL) { ctf_free (buf); return (ctf_set_errno (fp, err)); @@ -466,7 +449,6 @@ ctf_update (ctf_file_t *fp) nfp->ctf_dtbyname = fp->ctf_dtbyname; nfp->ctf_dvhash = fp->ctf_dvhash; nfp->ctf_dvdefs = fp->ctf_dvdefs; - nfp->ctf_dtvstrlen = fp->ctf_dtvstrlen; nfp->ctf_dtnextid = fp->ctf_dtnextid; nfp->ctf_dtoldid = fp->ctf_dtnextid - 1; nfp->ctf_snapshots = fp->ctf_snapshots + 1; @@ -476,6 +458,9 @@ ctf_update (ctf_file_t *fp) fp->ctf_dtbyname = NULL; fp->ctf_dthash = NULL; + ctf_str_free_atoms (nfp); + nfp->ctf_str_atoms = fp->ctf_str_atoms; + fp->ctf_str_atoms = NULL; memset (&fp->ctf_dtdefs, 0, sizeof (ctf_list_t)); fp->ctf_dvhash = NULL; @@ -559,10 +544,7 @@ ctf_dtd_delete (ctf_file_t *fp, ctf_dtdef_t *dtd) dmd != NULL; dmd = nmd) { if (dmd->dmd_name != NULL) - { - fp->ctf_dtvstrlen -= strlen (dmd->dmd_name) + 1; ctf_free (dmd->dmd_name); - } nmd = ctf_list_next (dmd); ctf_free (dmd); } @@ -579,8 +561,6 @@ ctf_dtd_delete (ctf_file_t *fp, ctf_dtdef_t *dtd) name = ctf_prefixed_name (kind, dtd->dtd_name); ctf_dynhash_remove (fp->ctf_dtbyname, name); free (name); - - fp->ctf_dtvstrlen -= strlen (dtd->dtd_name) + 1; ctf_free (dtd->dtd_name); } @@ -638,8 +618,6 @@ void ctf_dvd_delete (ctf_file_t *fp, ctf_dvdef_t *dvd) { ctf_dynhash_remove (fp->ctf_dvhash, dvd->dvd_name); - - fp->ctf_dtvstrlen -= strlen (dvd->dvd_name) + 1; ctf_free (dvd->dvd_name); ctf_list_delete (&fp->ctf_dvdefs, dvd); @@ -763,9 +741,6 @@ ctf_add_generic (ctf_file_t *fp, uint32_t flag, const char *name, dtd->dtd_name = s; dtd->dtd_type = type; - if (s != NULL) - fp->ctf_dtvstrlen += strlen (s) + 1; - if (ctf_dtd_insert (fp, dtd) < 0) { ctf_free (dtd); @@ -1272,7 +1247,6 @@ ctf_add_enumerator (ctf_file_t *fp, ctf_id_t enid, const char *name, dtd->dtd_data.ctt_info = CTF_TYPE_INFO (kind, root, vlen + 1); ctf_list_append (&dtd->dtd_u.dtu_members, dmd); - fp->ctf_dtvstrlen += strlen (s) + 1; fp->ctf_flags |= LCTF_DIRTY; return 0; @@ -1392,9 +1366,6 @@ ctf_add_member_offset (ctf_file_t *fp, ctf_id_t souid, const char *name, dtd->dtd_data.ctt_info = CTF_TYPE_INFO (kind, root, vlen + 1); ctf_list_append (&dtd->dtd_u.dtu_members, dmd); - if (s != NULL) - fp->ctf_dtvstrlen += strlen (s) + 1; - fp->ctf_flags |= LCTF_DIRTY; return 0; } @@ -1456,7 +1427,6 @@ ctf_add_variable (ctf_file_t *fp, const char *name, ctf_id_t ref) return -1; /* errno is set for us. */ } - fp->ctf_dtvstrlen += strlen (name) + 1; fp->ctf_flags |= LCTF_DIRTY; return 0; } @@ -1536,9 +1506,6 @@ membadd (const char *name, ctf_id_t type, unsigned long offset, void *arg) ctf_list_append (&ctb->ctb_dtd->dtd_u.dtu_members, dmd); - if (s != NULL) - ctb->ctb_file->ctf_dtvstrlen += strlen (s) + 1; - ctb->ctb_file->ctf_flags |= LCTF_DIRTY; return 0; } diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h index 2601e1b..b51118c 100644 --- a/libctf/ctf-impl.h +++ b/libctf/ctf-impl.h @@ -71,6 +71,12 @@ typedef struct ctf_strs size_t cts_len; /* Size of string table in bytes. */ } ctf_strs_t; +typedef struct ctf_strs_writable +{ + char *cts_strs; /* Base address of string table. */ + size_t cts_len; /* Size of string table in bytes. */ +} ctf_strs_writable_t; + typedef struct ctf_dmodel { const char *ctd_name; /* Data model name. */ @@ -147,7 +153,7 @@ typedef struct ctf_dtdef ctf_list_t dtd_list; /* List forward/back pointers. */ char *dtd_name; /* Name associated with definition (if any). */ ctf_id_t dtd_type; /* Type identifier for this definition. */ - ctf_type_t dtd_data; /* Type node (see <ctf.h>). */ + ctf_type_t dtd_data; /* Type node: name left unpopulated. */ union { ctf_list_t dtu_members; /* struct, union, or enum */ @@ -173,6 +179,30 @@ typedef struct ctf_bundle ctf_dtdef_t *ctb_dtd; /* CTF dynamic type definition (if any). */ } ctf_bundle_t; +/* Atoms associate strings with a list of the CTF items that reference that + string, so that ctf_update() can instantiate all the strings using the + ctf_str_atoms and then reassociate them with the real string later. + + Strings can be interned into ctf_str_atom without having refs associated + with them, for values that are returned to callers, etc. Items are only + removed from this table on ctf_close(), but on every ctf_update(), all the + csa_refs in all entries are purged. */ + +typedef struct ctf_str_atom +{ + const char *csa_str; /* Backpointer to string (hash key). */ + ctf_list_t csa_refs; /* This string's refs. */ + unsigned long csa_snapshot_id; /* Snapshot ID at time of creation. */ +} ctf_str_atom_t; + +/* The refs of a single string in the atoms table. */ + +typedef struct ctf_str_atom_ref +{ + ctf_list_t caf_list; /* List forward/back pointers. */ + uint32_t *caf_ref; /* A single ref to this string. */ +} ctf_str_atom_ref_t; + /* The ctf_file is the structure used to represent a CTF container to library clients, who see it only as an opaque pointer. Modifications can therefore be made freely to this structure without regard to client versioning. The @@ -198,6 +228,8 @@ struct ctf_file ctf_hash_t *ctf_names; /* Hash table of remaining type names. */ ctf_lookup_t ctf_lookups[5]; /* Pointers to hashes for name lookup. */ ctf_strs_t ctf_str[2]; /* Array of string table base and bounds. */ + ctf_dynhash_t *ctf_str_atoms; /* Hash table of ctf_str_atoms_t. */ + uint64_t ctf_str_num_refs; /* Number of refs to cts_str_atoms. */ const unsigned char *ctf_base; /* Base of CTF header + uncompressed buffer. */ const unsigned char *ctf_buf; /* Uncompressed CTF data buffer. */ size_t ctf_size; /* Size of CTF header + uncompressed data. */ @@ -223,7 +255,6 @@ struct ctf_file ctf_list_t ctf_dtdefs; /* List of dynamic type definitions. */ ctf_dynhash_t *ctf_dvhash; /* Hash of dynamic variable mappings. */ ctf_list_t ctf_dvdefs; /* List of dynamic variable definitions. */ - size_t ctf_dtvstrlen; /* Total length of dynamic type+var strings. */ unsigned long ctf_dtnextid; /* Next dynamic type id to assign. */ unsigned long ctf_dtoldid; /* Oldest id that has been committed. */ unsigned long ctf_snapshots; /* ctf_snapshot() plus ctf_update() count. */ @@ -341,6 +372,13 @@ extern char *ctf_decl_buf (ctf_decl_t *cd); extern const char *ctf_strraw (ctf_file_t *, uint32_t); extern const char *ctf_strptr (ctf_file_t *, uint32_t); +extern int ctf_str_create_atoms (ctf_file_t *); +extern void ctf_str_free_atoms (ctf_file_t *); +extern const char *ctf_str_add (ctf_file_t *, const char *); +extern const char *ctf_str_add_ref (ctf_file_t *, const char *, uint32_t *); +extern void ctf_str_rollback (ctf_file_t *, ctf_snapshot_id_t); +extern void ctf_str_purge_refs (ctf_file_t *); +extern ctf_strs_writable_t ctf_str_write_strtab (ctf_file_t *); extern struct ctf_archive *ctf_arc_open_internal (const char *, int *); extern struct ctf_archive *ctf_arc_bufopen (const void *, size_t, int *); @@ -356,6 +394,7 @@ extern ssize_t ctf_pread (int fd, void *buf, ssize_t count, off_t offset); _libctf_malloc_ extern void *ctf_alloc (size_t); extern void ctf_free (void *); +extern void *ctf_realloc (ctf_file_t *, void *, size_t); _libctf_malloc_ extern char *ctf_strdup (const char *); diff --git a/libctf/ctf-open.c b/libctf/ctf-open.c index 777a6b5..8fc854a 100644 --- a/libctf/ctf-open.c +++ b/libctf/ctf-open.c @@ -1373,6 +1373,7 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, memset (fp, 0, sizeof (ctf_file_t)); ctf_set_version (fp, &hp, hp.cth_version); + ctf_str_create_atoms (fp); if (_libctf_unlikely_ (hp.cth_version < CTF_VERSION_2)) fp->ctf_parmax = CTF_MAX_PTYPE_V1; @@ -1528,6 +1529,7 @@ ctf_file_close (ctf_file_t *fp) ctf_dvd_delete (fp, dvd); } ctf_dynhash_destroy (fp->ctf_dvhash); + ctf_str_free_atoms (fp); ctf_free (fp->ctf_tmp_typeslice); diff --git a/libctf/ctf-string.c b/libctf/ctf-string.c new file mode 100644 index 0000000..27bd7c2 --- /dev/null +++ b/libctf/ctf-string.c @@ -0,0 +1,330 @@ +/* CTF string table management. + Copyright (C) 2019 Free Software Foundation, Inc. + + This file is part of libctf. + + libctf is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 3, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not see + <http://www.gnu.org/licenses/>. */ + +#include <ctf-impl.h> +#include <string.h> + +/* Convert an encoded CTF string name into a pointer to a C string by looking + up the appropriate string table buffer and then adding the offset. */ +const char * +ctf_strraw (ctf_file_t *fp, uint32_t name) +{ + ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID (name)]; + + if (ctsp->cts_strs != NULL && CTF_NAME_OFFSET (name) < ctsp->cts_len) + return (ctsp->cts_strs + CTF_NAME_OFFSET (name)); + + /* String table not loaded or corrupt offset. */ + return NULL; +} + +/* Return a guaranteed-non-NULL pointer to the string with the given CTF + name. */ +const char * +ctf_strptr (ctf_file_t *fp, uint32_t name) +{ + const char *s = ctf_strraw (fp, name); + return (s != NULL ? s : "(?)"); +} + +/* Remove all refs to a given atom. */ +static void +ctf_str_purge_atom_refs (ctf_str_atom_t *atom) +{ + ctf_str_atom_ref_t *ref, *next; + + for (ref = ctf_list_next (&atom->csa_refs); ref != NULL; ref = next) + { + next = ctf_list_next (ref); + ctf_list_delete (&atom->csa_refs, ref); + ctf_free (ref); + } +} + +/* Free an atom (only called on ctf_close().) */ +static void +ctf_str_free_atom (void *a) +{ + ctf_str_atom_t *atom = a; + + ctf_str_purge_atom_refs (atom); + ctf_free (atom); +} + +/* Create the atoms table. There is always at least one atom in it, the null + string. */ +int +ctf_str_create_atoms (ctf_file_t *fp) +{ + fp->ctf_str_atoms = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string, + ctf_free, ctf_str_free_atom); + if (fp->ctf_str_atoms == NULL) + return -ENOMEM; + + ctf_str_add (fp, ""); + return 0; +} + +/* Destroy the atoms table. */ +void +ctf_str_free_atoms (ctf_file_t *fp) +{ + ctf_dynhash_destroy (fp->ctf_str_atoms); +} + +/* Add a string to the atoms table and return it, or return an existing string + if present, copying the passed-in string. Returns NULL only when out of + memory (and do not touch the passed-in string in that case). Possibly + augment the ref list with the passed-in ref. */ +static const char * +ctf_str_add_ref_internal (ctf_file_t *fp, const char *str, + int add_ref, uint32_t *ref) +{ + char *newstr = NULL; + ctf_str_atom_t *atom = NULL; + ctf_str_atom_ref_t *aref = NULL; + + atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str); + + if (add_ref) + { + if ((aref = ctf_alloc (sizeof (struct ctf_str_atom_ref))) == NULL) + return NULL; + aref->caf_ref = ref; + } + + if (atom) + { + if (add_ref) + { + ctf_list_append (&atom->csa_refs, aref); + fp->ctf_str_num_refs++; + } + return atom->csa_str; + } + + if ((atom = ctf_alloc (sizeof (struct ctf_str_atom))) == NULL) + goto oom; + memset (atom, 0, sizeof (struct ctf_str_atom)); + + if ((newstr = ctf_strdup (str)) == NULL) + goto oom; + + if (ctf_dynhash_insert (fp->ctf_str_atoms, newstr, atom) < 0) + goto oom; + + atom->csa_str = newstr; + atom->csa_snapshot_id = fp->ctf_snapshots; + if (add_ref) + { + ctf_list_append (&atom->csa_refs, aref); + fp->ctf_str_num_refs++; + } + return newstr; + + oom: + ctf_free (atom); + ctf_free (aref); + ctf_free (newstr); + return NULL; +} + +/* Add a string to the atoms table and return it, without augmenting the ref + list for this string. */ +const char * +ctf_str_add (ctf_file_t *fp, const char *str) +{ + if (str) + return ctf_str_add_ref_internal (fp, str, FALSE, 0); + return NULL; +} + +/* A ctf_dynhash_iter_remove() callback that removes atoms later than a given + snapshot ID. */ +static int +ctf_str_rollback_atom (void *key _libctf_unused_, void *value, void *arg) +{ + ctf_str_atom_t *atom = (ctf_str_atom_t *) value; + ctf_snapshot_id_t *id = (ctf_snapshot_id_t *) arg; + + return (atom->csa_snapshot_id > id->snapshot_id); +} + +/* Roll back, deleting all atoms created after a particular ID. */ +void +ctf_str_rollback (ctf_file_t *fp, ctf_snapshot_id_t id) +{ + ctf_dynhash_iter_remove (fp->ctf_str_atoms, ctf_str_rollback_atom, &id); +} + +/* Like ctf_str_add(), but additionally augment the atom's refs list with the + passed-in ref, whether or not the string is already present. There is no + attempt to deduplicate the refs list (but duplicates are harmless). */ +const char * +ctf_str_add_ref (ctf_file_t *fp, const char *str, uint32_t *ref) +{ + if (str) + return ctf_str_add_ref_internal (fp, str, TRUE, ref); + return NULL; +} + +/* An adaptor around ctf_purge_atom_refs. */ +static void +ctf_str_purge_one_atom_refs (void *key _libctf_unused_, void *value, + void *arg _libctf_unused_) +{ + ctf_str_atom_t *atom = (ctf_str_atom_t *) value; + ctf_str_purge_atom_refs (atom); +} + +/* Remove all the recorded refs from the atoms table. */ +void +ctf_str_purge_refs (ctf_file_t *fp) +{ + if (fp->ctf_str_num_refs > 0) + ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_purge_one_atom_refs, NULL); + fp->ctf_str_num_refs = 0; +} + +/* Update a list of refs to the specified value. */ +static void +ctf_str_update_refs (ctf_str_atom_t *refs, uint32_t value) +{ + ctf_str_atom_ref_t *ref; + + for (ref = ctf_list_next (&refs->csa_refs); ref != NULL; + ref = ctf_list_next (ref)) + *(ref->caf_ref) = value; +} + +/* State shared across the strtab write process. */ +typedef struct ctf_strtab_write_state +{ + /* Strtab we are writing, and the number of strings in it. */ + ctf_strs_writable_t *strtab; + size_t strtab_count; + + /* Pointers to (existing) atoms in the atoms table, for qsorting. */ + ctf_str_atom_t **sorttab; + + /* Loop counter for sorttab population. */ + size_t i; + + /* The null-string atom (skipped during population). */ + ctf_str_atom_t *nullstr; +} ctf_strtab_write_state_t; + +/* Count the number of entries in the strtab, and its length. */ +static void +ctf_str_count_strtab (void *key _libctf_unused_, void *value, + void *arg) +{ + ctf_str_atom_t *atom = (ctf_str_atom_t *) value; + ctf_strtab_write_state_t *s = (ctf_strtab_write_state_t *) arg; + + s->strtab->cts_len += strlen (atom->csa_str) + 1; + s->strtab_count++; +} + +/* Populate the sorttab with pointers to the strtab atoms. */ +static void +ctf_str_populate_sorttab (void *key _libctf_unused_, void *value, + void *arg) +{ + ctf_str_atom_t *atom = (ctf_str_atom_t *) value; + ctf_strtab_write_state_t *s = (ctf_strtab_write_state_t *) arg; + + /* Skip the null string. */ + if (s->nullstr == atom) + return; + + s->sorttab[s->i++] = atom; +} + +/* Sort the strtab. */ +static int +ctf_str_sort_strtab (const void *a, const void *b) +{ + ctf_str_atom_t **one = (ctf_str_atom_t **) a; + ctf_str_atom_t **two = (ctf_str_atom_t **) b; + + return (strcmp ((*one)->csa_str, (*two)->csa_str)); +} + +/* Write out and return a strtab containing all strings with recorded refs, + adjusting the refs to refer to the corresponding string. The returned + strtab may be NULL on error. */ +ctf_strs_writable_t +ctf_str_write_strtab (ctf_file_t *fp) +{ + ctf_strs_writable_t strtab; + ctf_str_atom_t *nullstr; + uint32_t cur_stroff = 0; + ctf_strtab_write_state_t s; + ctf_str_atom_t **sorttab; + size_t i; + + memset (&strtab, 0, sizeof (struct ctf_strs_writable)); + memset (&s, 0, sizeof (struct ctf_strtab_write_state)); + s.strtab = &strtab; + + nullstr = ctf_dynhash_lookup (fp->ctf_str_atoms, ""); + if (!nullstr) + { + ctf_dprintf ("Internal error: null string not found in strtab.\n"); + strtab.cts_strs = NULL; + return strtab; + } + + ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_count_strtab, &s); + + ctf_dprintf ("%lu bytes of strings in strtab.\n", + (unsigned long) strtab.cts_len); + + /* Sort the strtab. Force the null string to be first. */ + sorttab = calloc (s.strtab_count, sizeof (ctf_str_atom_t *)); + if (!sorttab) + return strtab; + + sorttab[0] = nullstr; + s.i = 1; + s.sorttab = sorttab; + s.nullstr = nullstr; + ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_populate_sorttab, &s); + + qsort (&sorttab[1], s.strtab_count - 1, sizeof (ctf_str_atom_t *), + ctf_str_sort_strtab); + + if ((strtab.cts_strs = ctf_alloc (strtab.cts_len)) == NULL) + { + free (sorttab); + return strtab; + } + + /* Update the strtab, and all refs. */ + for (i = 0; i < s.strtab_count; i++) + { + strcpy (&strtab.cts_strs[cur_stroff], sorttab[i]->csa_str); + ctf_str_update_refs (sorttab[i], cur_stroff); + cur_stroff += strlen (sorttab[i]->csa_str) + 1; + } + free (sorttab); + + return strtab; +} diff --git a/libctf/ctf-util.c b/libctf/ctf-util.c index 730f358..b813c0d 100644 --- a/libctf/ctf-util.c +++ b/libctf/ctf-util.c @@ -95,28 +95,6 @@ ctf_sym_to_elf64 (const Elf32_Sym *src, Elf64_Sym *dst) return dst; } -/* Convert an encoded CTF string name into a pointer to a C string by looking - up the appropriate string table buffer and then adding the offset. */ - -const char * -ctf_strraw (ctf_file_t *fp, uint32_t name) -{ - ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID (name)]; - - if (ctsp->cts_strs != NULL && CTF_NAME_OFFSET (name) < ctsp->cts_len) - return (ctsp->cts_strs + CTF_NAME_OFFSET (name)); - - /* String table not loaded or corrupt offset. */ - return NULL; -} - -const char * -ctf_strptr (ctf_file_t *fp, uint32_t name) -{ - const char *s = ctf_strraw (fp, name); - return (s != NULL ? s : "(?)"); -} - /* Same as strdup(3C), but use ctf_alloc() to do the memory allocation. */ _libctf_malloc_ char * @@ -154,6 +132,19 @@ ctf_str_append (char *s, const char *append) return s; } +/* A realloc() that fails noisily if called with any ctf_str_num_users. */ +void * +ctf_realloc (ctf_file_t *fp, void *ptr, size_t size) +{ + if (fp->ctf_str_num_refs > 0) + { + ctf_dprintf ("%p: attempt to realloc() string table with %lu active refs\n", + (void *) fp, (unsigned long) fp->ctf_str_num_refs); + return NULL; + } + return realloc (ptr, size); +} + /* Store the specified error code into errp if it is non-NULL, and then return NULL for the benefit of the caller. */ |