1 1.1 christos /* CTF string table management. 2 1.1.1.3 christos Copyright (C) 2019-2024 Free Software Foundation, Inc. 3 1.1 christos 4 1.1 christos This file is part of libctf. 5 1.1 christos 6 1.1 christos libctf is free software; you can redistribute it and/or modify it under 7 1.1 christos the terms of the GNU General Public License as published by the Free 8 1.1 christos Software Foundation; either version 3, or (at your option) any later 9 1.1 christos version. 10 1.1 christos 11 1.1 christos This program is distributed in the hope that it will be useful, but 12 1.1 christos WITHOUT ANY WARRANTY; without even the implied warranty of 13 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 1.1 christos See the GNU General Public License for more details. 15 1.1 christos 16 1.1 christos You should have received a copy of the GNU General Public License 17 1.1 christos along with this program; see the file COPYING. If not see 18 1.1 christos <http://www.gnu.org/licenses/>. */ 19 1.1 christos 20 1.1.1.3 christos #include <assert.h> 21 1.1 christos #include <ctf-impl.h> 22 1.1 christos #include <string.h> 23 1.1 christos 24 1.1.1.3 christos static ctf_str_atom_t * 25 1.1.1.3 christos ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str, 26 1.1.1.3 christos int flags, uint32_t *ref); 27 1.1.1.3 christos 28 1.1.1.3 christos /* Convert an encoded CTF string name into a pointer to a C string, possibly 29 1.1.1.3 christos using an explicit internal provisional strtab rather than the fp-based 30 1.1.1.3 christos one. */ 31 1.1 christos const char * 32 1.1.1.2 christos ctf_strraw_explicit (ctf_dict_t *fp, uint32_t name, ctf_strs_t *strtab) 33 1.1 christos { 34 1.1 christos ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID (name)]; 35 1.1 christos 36 1.1 christos if ((CTF_NAME_STID (name) == CTF_STRTAB_0) && (strtab != NULL)) 37 1.1 christos ctsp = strtab; 38 1.1 christos 39 1.1.1.3 christos /* If this name is in the external strtab, and there is a synthetic 40 1.1.1.3 christos strtab, use it in preference. (This is used to add the set of strings 41 1.1.1.3 christos -- symbol names, etc -- the linker knows about before the strtab is 42 1.1.1.3 christos written out.) */ 43 1.1 christos 44 1.1 christos if (CTF_NAME_STID (name) == CTF_STRTAB_1 45 1.1 christos && fp->ctf_syn_ext_strtab != NULL) 46 1.1 christos return ctf_dynhash_lookup (fp->ctf_syn_ext_strtab, 47 1.1 christos (void *) (uintptr_t) name); 48 1.1 christos 49 1.1.1.3 christos /* If the name is in the internal strtab, and the name offset is beyond 50 1.1.1.3 christos the end of the ctsp->cts_len but below the ctf_str_prov_offset, this is 51 1.1.1.3 christos a provisional string added by ctf_str_add*() but not yet built into a 52 1.1.1.3 christos real strtab: get the value out of the ctf_prov_strtab. */ 53 1.1 christos 54 1.1 christos if (CTF_NAME_STID (name) == CTF_STRTAB_0 55 1.1 christos && name >= ctsp->cts_len && name < fp->ctf_str_prov_offset) 56 1.1 christos return ctf_dynhash_lookup (fp->ctf_prov_strtab, 57 1.1 christos (void *) (uintptr_t) name); 58 1.1 christos 59 1.1 christos if (ctsp->cts_strs != NULL && CTF_NAME_OFFSET (name) < ctsp->cts_len) 60 1.1 christos return (ctsp->cts_strs + CTF_NAME_OFFSET (name)); 61 1.1 christos 62 1.1 christos /* String table not loaded or corrupt offset. */ 63 1.1 christos return NULL; 64 1.1 christos } 65 1.1 christos 66 1.1 christos /* Convert an encoded CTF string name into a pointer to a C string by looking 67 1.1 christos up the appropriate string table buffer and then adding the offset. */ 68 1.1 christos const char * 69 1.1.1.2 christos ctf_strraw (ctf_dict_t *fp, uint32_t name) 70 1.1 christos { 71 1.1 christos return ctf_strraw_explicit (fp, name, NULL); 72 1.1 christos } 73 1.1 christos 74 1.1 christos /* Return a guaranteed-non-NULL pointer to the string with the given CTF 75 1.1 christos name. */ 76 1.1 christos const char * 77 1.1.1.2 christos ctf_strptr (ctf_dict_t *fp, uint32_t name) 78 1.1 christos { 79 1.1 christos const char *s = ctf_strraw (fp, name); 80 1.1 christos return (s != NULL ? s : "(?)"); 81 1.1 christos } 82 1.1 christos 83 1.1.1.3 christos /* As above, but return info on what is wrong in more detail. 84 1.1.1.3 christos (Used for type lookups.) */ 85 1.1.1.3 christos 86 1.1.1.3 christos const char * 87 1.1.1.3 christos ctf_strptr_validate (ctf_dict_t *fp, uint32_t name) 88 1.1.1.3 christos { 89 1.1.1.3 christos const char *str = ctf_strraw (fp, name); 90 1.1.1.3 christos 91 1.1.1.3 christos if (str == NULL) 92 1.1.1.3 christos { 93 1.1.1.3 christos if (CTF_NAME_STID (name) == CTF_STRTAB_1 94 1.1.1.3 christos && fp->ctf_syn_ext_strtab == NULL 95 1.1.1.3 christos && fp->ctf_str[CTF_NAME_STID (name)].cts_strs == NULL) 96 1.1.1.3 christos { 97 1.1.1.3 christos ctf_set_errno (fp, ECTF_STRTAB); 98 1.1.1.3 christos return NULL; 99 1.1.1.3 christos } 100 1.1.1.3 christos 101 1.1.1.3 christos ctf_set_errno (fp, ECTF_BADNAME); 102 1.1.1.3 christos return NULL; 103 1.1.1.3 christos } 104 1.1.1.3 christos return str; 105 1.1.1.3 christos } 106 1.1.1.3 christos 107 1.1 christos /* Remove all refs to a given atom. */ 108 1.1 christos static void 109 1.1 christos ctf_str_purge_atom_refs (ctf_str_atom_t *atom) 110 1.1 christos { 111 1.1 christos ctf_str_atom_ref_t *ref, *next; 112 1.1.1.4 christos ctf_str_atom_ref_movable_t *movref, *movnext; 113 1.1 christos 114 1.1 christos for (ref = ctf_list_next (&atom->csa_refs); ref != NULL; ref = next) 115 1.1 christos { 116 1.1 christos next = ctf_list_next (ref); 117 1.1 christos ctf_list_delete (&atom->csa_refs, ref); 118 1.1 christos free (ref); 119 1.1 christos } 120 1.1.1.4 christos 121 1.1.1.4 christos for (movref = ctf_list_next (&atom->csa_movable_refs); 122 1.1.1.4 christos movref != NULL; movref = movnext) 123 1.1.1.4 christos { 124 1.1.1.4 christos movnext = ctf_list_next (movref); 125 1.1.1.4 christos ctf_list_delete (&atom->csa_movable_refs, movref); 126 1.1.1.4 christos 127 1.1.1.4 christos ctf_dynhash_remove (movref->caf_movable_refs, movref); 128 1.1.1.4 christos 129 1.1.1.4 christos free (movref); 130 1.1.1.4 christos } 131 1.1 christos } 132 1.1 christos 133 1.1.1.3 christos /* Free an atom. */ 134 1.1 christos static void 135 1.1 christos ctf_str_free_atom (void *a) 136 1.1 christos { 137 1.1 christos ctf_str_atom_t *atom = a; 138 1.1 christos 139 1.1 christos ctf_str_purge_atom_refs (atom); 140 1.1.1.3 christos 141 1.1.1.3 christos if (atom->csa_flags & CTF_STR_ATOM_FREEABLE) 142 1.1.1.3 christos free (atom->csa_str); 143 1.1.1.3 christos 144 1.1 christos free (atom); 145 1.1 christos } 146 1.1 christos 147 1.1 christos /* Create the atoms table. There is always at least one atom in it, the null 148 1.1.1.3 christos string: but also pull in atoms from the internal strtab. (We rely on 149 1.1.1.3 christos calls to ctf_str_add_external to populate external strtab entries, since 150 1.1.1.3 christos these are often not quite the same as what appears in any external 151 1.1.1.3 christos strtab, and the external strtab is often huge and best not aggressively 152 1.1.1.3 christos pulled in.) */ 153 1.1 christos int 154 1.1.1.2 christos ctf_str_create_atoms (ctf_dict_t *fp) 155 1.1 christos { 156 1.1.1.3 christos size_t i; 157 1.1.1.3 christos 158 1.1 christos fp->ctf_str_atoms = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string, 159 1.1.1.3 christos NULL, ctf_str_free_atom); 160 1.1.1.2 christos if (!fp->ctf_str_atoms) 161 1.1 christos return -ENOMEM; 162 1.1 christos 163 1.1 christos if (!fp->ctf_prov_strtab) 164 1.1 christos fp->ctf_prov_strtab = ctf_dynhash_create (ctf_hash_integer, 165 1.1 christos ctf_hash_eq_integer, 166 1.1 christos NULL, NULL); 167 1.1 christos if (!fp->ctf_prov_strtab) 168 1.1 christos goto oom_prov_strtab; 169 1.1 christos 170 1.1.1.3 christos fp->ctf_str_movable_refs = ctf_dynhash_create (ctf_hash_integer, 171 1.1.1.3 christos ctf_hash_eq_integer, 172 1.1.1.3 christos NULL, NULL); 173 1.1.1.3 christos if (!fp->ctf_str_movable_refs) 174 1.1.1.3 christos goto oom_movable_refs; 175 1.1.1.2 christos 176 1.1 christos errno = 0; 177 1.1 christos ctf_str_add (fp, ""); 178 1.1 christos if (errno == ENOMEM) 179 1.1 christos goto oom_str_add; 180 1.1 christos 181 1.1.1.3 christos /* Pull in all the strings in the strtab as new atoms. The provisional 182 1.1.1.3 christos strtab must be empty at this point, so there is no need to populate 183 1.1.1.3 christos atoms from it as well. Types in this subset are frozen and readonly, 184 1.1.1.3 christos so the refs list and movable refs list need not be populated. */ 185 1.1.1.3 christos 186 1.1.1.3 christos for (i = 0; i < fp->ctf_str[CTF_STRTAB_0].cts_len; 187 1.1.1.3 christos i += strlen (&fp->ctf_str[CTF_STRTAB_0].cts_strs[i]) + 1) 188 1.1.1.3 christos { 189 1.1.1.3 christos ctf_str_atom_t *atom; 190 1.1.1.3 christos 191 1.1.1.3 christos if (fp->ctf_str[CTF_STRTAB_0].cts_strs[i] == 0) 192 1.1.1.3 christos continue; 193 1.1.1.3 christos 194 1.1.1.3 christos atom = ctf_str_add_ref_internal (fp, &fp->ctf_str[CTF_STRTAB_0].cts_strs[i], 195 1.1.1.3 christos 0, 0); 196 1.1.1.3 christos 197 1.1.1.3 christos if (!atom) 198 1.1.1.3 christos goto oom_str_add; 199 1.1.1.3 christos 200 1.1.1.3 christos atom->csa_offset = i; 201 1.1.1.3 christos } 202 1.1.1.3 christos 203 1.1.1.4 christos fp->ctf_str_prov_offset = fp->ctf_str[CTF_STRTAB_0].cts_len + 1; 204 1.1.1.4 christos 205 1.1 christos return 0; 206 1.1 christos 207 1.1 christos oom_str_add: 208 1.1.1.3 christos ctf_dynhash_destroy (fp->ctf_str_movable_refs); 209 1.1.1.3 christos fp->ctf_str_movable_refs = NULL; 210 1.1.1.3 christos oom_movable_refs: 211 1.1 christos ctf_dynhash_destroy (fp->ctf_prov_strtab); 212 1.1 christos fp->ctf_prov_strtab = NULL; 213 1.1 christos oom_prov_strtab: 214 1.1 christos ctf_dynhash_destroy (fp->ctf_str_atoms); 215 1.1 christos fp->ctf_str_atoms = NULL; 216 1.1 christos return -ENOMEM; 217 1.1 christos } 218 1.1 christos 219 1.1.1.3 christos /* Destroy the atoms table and associated refs. */ 220 1.1 christos void 221 1.1.1.2 christos ctf_str_free_atoms (ctf_dict_t *fp) 222 1.1 christos { 223 1.1 christos ctf_dynhash_destroy (fp->ctf_prov_strtab); 224 1.1 christos ctf_dynhash_destroy (fp->ctf_str_atoms); 225 1.1.1.3 christos ctf_dynhash_destroy (fp->ctf_str_movable_refs); 226 1.1.1.3 christos if (fp->ctf_dynstrtab) 227 1.1.1.3 christos { 228 1.1.1.3 christos free (fp->ctf_dynstrtab->cts_strs); 229 1.1.1.3 christos free (fp->ctf_dynstrtab); 230 1.1.1.3 christos } 231 1.1 christos } 232 1.1 christos 233 1.1.1.2 christos #define CTF_STR_ADD_REF 0x1 234 1.1.1.3 christos #define CTF_STR_PROVISIONAL 0x2 235 1.1.1.3 christos #define CTF_STR_MOVABLE 0x4 236 1.1.1.3 christos 237 1.1.1.3 christos /* Allocate a ref and bind it into a ref list. */ 238 1.1.1.3 christos 239 1.1.1.3 christos static ctf_str_atom_ref_t * 240 1.1.1.3 christos aref_create (ctf_dict_t *fp, ctf_str_atom_t *atom, uint32_t *ref, int flags) 241 1.1.1.3 christos { 242 1.1.1.3 christos ctf_str_atom_ref_t *aref; 243 1.1.1.3 christos size_t s = sizeof (struct ctf_str_atom_ref); 244 1.1.1.3 christos 245 1.1.1.3 christos if (flags & CTF_STR_MOVABLE) 246 1.1.1.3 christos s = sizeof (struct ctf_str_atom_ref_movable); 247 1.1.1.3 christos 248 1.1.1.3 christos aref = malloc (s); 249 1.1.1.3 christos 250 1.1.1.3 christos if (!aref) 251 1.1.1.3 christos return NULL; 252 1.1.1.3 christos 253 1.1.1.3 christos aref->caf_ref = ref; 254 1.1.1.3 christos 255 1.1.1.3 christos /* Movable refs get a backpointer to them in ctf_str_movable_refs, and a 256 1.1.1.3 christos pointer to ctf_str_movable_refs itself in the ref, for use when freeing 257 1.1.1.3 christos refs: they can be moved later in batches via a call to 258 1.1.1.3 christos ctf_str_move_refs. */ 259 1.1.1.3 christos 260 1.1.1.3 christos if (flags & CTF_STR_MOVABLE) 261 1.1.1.3 christos { 262 1.1.1.3 christos ctf_str_atom_ref_movable_t *movref = (ctf_str_atom_ref_movable_t *) aref; 263 1.1.1.3 christos 264 1.1.1.3 christos movref->caf_movable_refs = fp->ctf_str_movable_refs; 265 1.1.1.3 christos 266 1.1.1.3 christos if (ctf_dynhash_insert (fp->ctf_str_movable_refs, ref, aref) < 0) 267 1.1.1.3 christos { 268 1.1.1.3 christos free (aref); 269 1.1.1.3 christos return NULL; 270 1.1.1.3 christos } 271 1.1.1.4 christos ctf_list_append (&atom->csa_movable_refs, movref); 272 1.1.1.3 christos } 273 1.1.1.4 christos else 274 1.1.1.4 christos ctf_list_append (&atom->csa_refs, aref); 275 1.1.1.3 christos 276 1.1.1.3 christos return aref; 277 1.1.1.3 christos } 278 1.1.1.3 christos 279 1.1.1.3 christos /* Add a string to the atoms table, copying the passed-in string if 280 1.1.1.3 christos necessary. Return the atom added. Return NULL only when out of memory 281 1.1.1.3 christos (and do not touch the passed-in string in that case). 282 1.1.1.3 christos 283 1.1.1.3 christos Possibly add a provisional entry for this string to the provisional 284 1.1.1.3 christos strtab. If the string is in the provisional strtab, update its ref list 285 1.1.1.3 christos with the passed-in ref, causing the ref to be updated when the strtab is 286 1.1.1.3 christos written out. */ 287 1.1.1.2 christos 288 1.1 christos static ctf_str_atom_t * 289 1.1.1.2 christos ctf_str_add_ref_internal (ctf_dict_t *fp, const char *str, 290 1.1.1.2 christos int flags, uint32_t *ref) 291 1.1 christos { 292 1.1 christos char *newstr = NULL; 293 1.1 christos ctf_str_atom_t *atom = NULL; 294 1.1.1.3 christos int added = 0; 295 1.1 christos 296 1.1 christos atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str); 297 1.1 christos 298 1.1.1.3 christos /* Existing atoms get refs added only if they are provisional: 299 1.1.1.3 christos non-provisional strings already have a fixed strtab offset, and just 300 1.1.1.3 christos get their ref updated immediately, since its value cannot change. */ 301 1.1 christos 302 1.1 christos if (atom) 303 1.1 christos { 304 1.1.1.3 christos if (!ctf_dynhash_lookup (fp->ctf_prov_strtab, (void *) (uintptr_t) 305 1.1.1.3 christos atom->csa_offset)) 306 1.1.1.3 christos { 307 1.1.1.3 christos if (flags & CTF_STR_ADD_REF) 308 1.1.1.3 christos { 309 1.1.1.3 christos if (atom->csa_external_offset) 310 1.1.1.3 christos *ref = atom->csa_external_offset; 311 1.1.1.3 christos else 312 1.1.1.3 christos *ref = atom->csa_offset; 313 1.1.1.3 christos } 314 1.1.1.3 christos return atom; 315 1.1.1.3 christos } 316 1.1.1.3 christos 317 1.1.1.2 christos if (flags & CTF_STR_ADD_REF) 318 1.1 christos { 319 1.1.1.3 christos if (!aref_create (fp, atom, ref, flags)) 320 1.1.1.3 christos { 321 1.1.1.3 christos ctf_set_errno (fp, ENOMEM); 322 1.1.1.3 christos return NULL; 323 1.1.1.3 christos } 324 1.1 christos } 325 1.1.1.3 christos 326 1.1 christos return atom; 327 1.1 christos } 328 1.1 christos 329 1.1.1.3 christos /* New atom. */ 330 1.1.1.3 christos 331 1.1 christos if ((atom = malloc (sizeof (struct ctf_str_atom))) == NULL) 332 1.1 christos goto oom; 333 1.1 christos memset (atom, 0, sizeof (struct ctf_str_atom)); 334 1.1 christos 335 1.1.1.3 christos /* Don't allocate new strings if this string is within an mmapped 336 1.1.1.3 christos strtab. */ 337 1.1.1.3 christos 338 1.1.1.3 christos if ((unsigned char *) str < (unsigned char *) fp->ctf_data_mmapped 339 1.1.1.3 christos || (unsigned char *) str > (unsigned char *) fp->ctf_data_mmapped + fp->ctf_data_mmapped_len) 340 1.1.1.3 christos { 341 1.1.1.3 christos if ((newstr = strdup (str)) == NULL) 342 1.1.1.3 christos goto oom; 343 1.1.1.3 christos atom->csa_flags |= CTF_STR_ATOM_FREEABLE; 344 1.1.1.3 christos atom->csa_str = newstr; 345 1.1.1.3 christos } 346 1.1.1.3 christos else 347 1.1.1.3 christos atom->csa_str = (char *) str; 348 1.1 christos 349 1.1.1.3 christos if (ctf_dynhash_insert (fp->ctf_str_atoms, atom->csa_str, atom) < 0) 350 1.1 christos goto oom; 351 1.1.1.3 christos added = 1; 352 1.1 christos 353 1.1 christos atom->csa_snapshot_id = fp->ctf_snapshots; 354 1.1 christos 355 1.1.1.3 christos /* New atoms marked provisional go into the provisional strtab, and get a 356 1.1.1.3 christos ref added. */ 357 1.1.1.3 christos 358 1.1.1.3 christos if (flags & CTF_STR_PROVISIONAL) 359 1.1 christos { 360 1.1 christos atom->csa_offset = fp->ctf_str_prov_offset; 361 1.1 christos 362 1.1 christos if (ctf_dynhash_insert (fp->ctf_prov_strtab, (void *) (uintptr_t) 363 1.1 christos atom->csa_offset, (void *) atom->csa_str) < 0) 364 1.1 christos goto oom; 365 1.1 christos 366 1.1 christos fp->ctf_str_prov_offset += strlen (atom->csa_str) + 1; 367 1.1 christos 368 1.1.1.3 christos if (flags & CTF_STR_ADD_REF) 369 1.1.1.3 christos { 370 1.1.1.3 christos if (!aref_create (fp, atom, ref, flags)) 371 1.1.1.3 christos goto oom; 372 1.1.1.3 christos } 373 1.1 christos } 374 1.1.1.3 christos 375 1.1 christos return atom; 376 1.1 christos 377 1.1 christos oom: 378 1.1.1.3 christos if (added) 379 1.1.1.3 christos ctf_dynhash_remove (fp->ctf_str_atoms, atom->csa_str); 380 1.1 christos free (atom); 381 1.1 christos free (newstr); 382 1.1.1.2 christos ctf_set_errno (fp, ENOMEM); 383 1.1 christos return NULL; 384 1.1 christos } 385 1.1 christos 386 1.1 christos /* Add a string to the atoms table, without augmenting the ref list for this 387 1.1 christos string: return a 'provisional offset' which can be used to return this string 388 1.1 christos until ctf_str_write_strtab is called, or 0 on failure. (Everywhere the 389 1.1 christos provisional offset is assigned to should be added as a ref using 390 1.1 christos ctf_str_add_ref() as well.) */ 391 1.1 christos uint32_t 392 1.1.1.2 christos ctf_str_add (ctf_dict_t *fp, const char *str) 393 1.1 christos { 394 1.1 christos ctf_str_atom_t *atom; 395 1.1.1.2 christos 396 1.1 christos if (!str) 397 1.1.1.2 christos str = ""; 398 1.1 christos 399 1.1.1.3 christos atom = ctf_str_add_ref_internal (fp, str, CTF_STR_PROVISIONAL, 0); 400 1.1 christos if (!atom) 401 1.1 christos return 0; 402 1.1 christos 403 1.1 christos return atom->csa_offset; 404 1.1 christos } 405 1.1 christos 406 1.1 christos /* Like ctf_str_add(), but additionally augment the atom's refs list with the 407 1.1 christos passed-in ref, whether or not the string is already present. There is no 408 1.1 christos attempt to deduplicate the refs list (but duplicates are harmless). */ 409 1.1 christos uint32_t 410 1.1.1.2 christos ctf_str_add_ref (ctf_dict_t *fp, const char *str, uint32_t *ref) 411 1.1 christos { 412 1.1 christos ctf_str_atom_t *atom; 413 1.1.1.2 christos 414 1.1 christos if (!str) 415 1.1.1.2 christos str = ""; 416 1.1.1.2 christos 417 1.1.1.2 christos atom = ctf_str_add_ref_internal (fp, str, CTF_STR_ADD_REF 418 1.1.1.3 christos | CTF_STR_PROVISIONAL, ref); 419 1.1.1.2 christos if (!atom) 420 1.1 christos return 0; 421 1.1 christos 422 1.1.1.2 christos return atom->csa_offset; 423 1.1.1.2 christos } 424 1.1.1.2 christos 425 1.1.1.3 christos /* Like ctf_str_add_ref(), but note that the ref may be moved later on. */ 426 1.1.1.2 christos uint32_t 427 1.1.1.3 christos ctf_str_add_movable_ref (ctf_dict_t *fp, const char *str, uint32_t *ref) 428 1.1.1.2 christos { 429 1.1.1.2 christos ctf_str_atom_t *atom; 430 1.1.1.2 christos 431 1.1.1.2 christos if (!str) 432 1.1.1.2 christos str = ""; 433 1.1.1.2 christos 434 1.1.1.3 christos atom = ctf_str_add_ref_internal (fp, str, CTF_STR_ADD_REF 435 1.1.1.3 christos | CTF_STR_PROVISIONAL 436 1.1.1.3 christos | CTF_STR_MOVABLE, ref); 437 1.1 christos if (!atom) 438 1.1 christos return 0; 439 1.1 christos 440 1.1 christos return atom->csa_offset; 441 1.1 christos } 442 1.1 christos 443 1.1 christos /* Add an external strtab reference at OFFSET. Returns zero if the addition 444 1.1 christos failed, nonzero otherwise. */ 445 1.1 christos int 446 1.1.1.2 christos ctf_str_add_external (ctf_dict_t *fp, const char *str, uint32_t offset) 447 1.1 christos { 448 1.1 christos ctf_str_atom_t *atom; 449 1.1.1.2 christos 450 1.1 christos if (!str) 451 1.1.1.2 christos str = ""; 452 1.1 christos 453 1.1.1.2 christos atom = ctf_str_add_ref_internal (fp, str, 0, 0); 454 1.1 christos if (!atom) 455 1.1 christos return 0; 456 1.1 christos 457 1.1 christos atom->csa_external_offset = CTF_SET_STID (offset, CTF_STRTAB_1); 458 1.1.1.2 christos 459 1.1.1.2 christos if (!fp->ctf_syn_ext_strtab) 460 1.1.1.2 christos fp->ctf_syn_ext_strtab = ctf_dynhash_create (ctf_hash_integer, 461 1.1.1.2 christos ctf_hash_eq_integer, 462 1.1.1.2 christos NULL, NULL); 463 1.1.1.2 christos if (!fp->ctf_syn_ext_strtab) 464 1.1.1.2 christos { 465 1.1.1.2 christos ctf_set_errno (fp, ENOMEM); 466 1.1.1.2 christos return 0; 467 1.1.1.2 christos } 468 1.1.1.2 christos 469 1.1.1.2 christos if (ctf_dynhash_insert (fp->ctf_syn_ext_strtab, 470 1.1.1.2 christos (void *) (uintptr_t) 471 1.1.1.2 christos atom->csa_external_offset, 472 1.1.1.2 christos (void *) atom->csa_str) < 0) 473 1.1.1.2 christos { 474 1.1.1.2 christos /* No need to bother freeing the syn_ext_strtab: it will get freed at 475 1.1.1.2 christos ctf_str_write_strtab time if unreferenced. */ 476 1.1.1.2 christos ctf_set_errno (fp, ENOMEM); 477 1.1.1.2 christos return 0; 478 1.1.1.2 christos } 479 1.1.1.2 christos 480 1.1 christos return 1; 481 1.1 christos } 482 1.1 christos 483 1.1.1.3 christos /* Note that refs have moved from (SRC, LEN) to DEST. We use the movable 484 1.1.1.3 christos refs backpointer for this, because it is done an amortized-constant 485 1.1.1.3 christos number of times during structure member and enumerand addition, and if we 486 1.1.1.3 christos did a linear search this would turn such addition into an O(n^2) 487 1.1.1.3 christos operation. Even this is not linear, but it's better than that. */ 488 1.1.1.3 christos int 489 1.1.1.3 christos ctf_str_move_refs (ctf_dict_t *fp, void *src, size_t len, void *dest) 490 1.1.1.3 christos { 491 1.1.1.3 christos uintptr_t p; 492 1.1.1.3 christos 493 1.1.1.3 christos if (src == dest) 494 1.1.1.3 christos return 0; 495 1.1.1.3 christos 496 1.1.1.3 christos for (p = (uintptr_t) src; p - (uintptr_t) src < len; p++) 497 1.1.1.3 christos { 498 1.1.1.4 christos ctf_str_atom_ref_movable_t *ref; 499 1.1.1.3 christos 500 1.1.1.3 christos if ((ref = ctf_dynhash_lookup (fp->ctf_str_movable_refs, 501 1.1.1.3 christos (ctf_str_atom_ref_t *) p)) != NULL) 502 1.1.1.3 christos { 503 1.1.1.3 christos int out_of_memory; 504 1.1.1.3 christos 505 1.1.1.3 christos ref->caf_ref = (uint32_t *) (((uintptr_t) ref->caf_ref + 506 1.1.1.3 christos (uintptr_t) dest - (uintptr_t) src)); 507 1.1.1.3 christos ctf_dynhash_remove (fp->ctf_str_movable_refs, 508 1.1.1.3 christos (ctf_str_atom_ref_t *) p); 509 1.1.1.3 christos out_of_memory = ctf_dynhash_insert (fp->ctf_str_movable_refs, 510 1.1.1.3 christos ref->caf_ref, ref); 511 1.1.1.3 christos assert (out_of_memory == 0); 512 1.1.1.3 christos } 513 1.1.1.3 christos } 514 1.1.1.3 christos 515 1.1.1.3 christos return 0; 516 1.1.1.3 christos } 517 1.1.1.3 christos 518 1.1 christos /* Remove a single ref. */ 519 1.1 christos void 520 1.1.1.2 christos ctf_str_remove_ref (ctf_dict_t *fp, const char *str, uint32_t *ref) 521 1.1 christos { 522 1.1 christos ctf_str_atom_ref_t *aref, *anext; 523 1.1.1.4 christos ctf_str_atom_ref_movable_t *amovref, *amovnext; 524 1.1 christos ctf_str_atom_t *atom = NULL; 525 1.1 christos 526 1.1 christos atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str); 527 1.1 christos if (!atom) 528 1.1 christos return; 529 1.1 christos 530 1.1 christos for (aref = ctf_list_next (&atom->csa_refs); aref != NULL; aref = anext) 531 1.1 christos { 532 1.1 christos anext = ctf_list_next (aref); 533 1.1 christos if (aref->caf_ref == ref) 534 1.1 christos { 535 1.1 christos ctf_list_delete (&atom->csa_refs, aref); 536 1.1 christos free (aref); 537 1.1 christos } 538 1.1 christos } 539 1.1.1.4 christos 540 1.1.1.4 christos for (amovref = ctf_list_next (&atom->csa_movable_refs); 541 1.1.1.4 christos amovref != NULL; amovref = amovnext) 542 1.1.1.4 christos { 543 1.1.1.4 christos amovnext = ctf_list_next (amovref); 544 1.1.1.4 christos if (amovref->caf_ref == ref) 545 1.1.1.4 christos { 546 1.1.1.4 christos ctf_list_delete (&atom->csa_movable_refs, amovref); 547 1.1.1.4 christos ctf_dynhash_remove (fp->ctf_str_movable_refs, ref); 548 1.1.1.4 christos free (amovref); 549 1.1.1.4 christos } 550 1.1.1.4 christos } 551 1.1 christos } 552 1.1 christos 553 1.1 christos /* A ctf_dynhash_iter_remove() callback that removes atoms later than a given 554 1.1.1.2 christos snapshot ID. External atoms are never removed, because they came from the 555 1.1.1.2 christos linker string table and are still present even if you roll back type 556 1.1.1.2 christos additions. */ 557 1.1 christos static int 558 1.1 christos ctf_str_rollback_atom (void *key _libctf_unused_, void *value, void *arg) 559 1.1 christos { 560 1.1 christos ctf_str_atom_t *atom = (ctf_str_atom_t *) value; 561 1.1 christos ctf_snapshot_id_t *id = (ctf_snapshot_id_t *) arg; 562 1.1 christos 563 1.1.1.2 christos return (atom->csa_snapshot_id > id->snapshot_id) 564 1.1.1.2 christos && (atom->csa_external_offset == 0); 565 1.1 christos } 566 1.1 christos 567 1.1.1.2 christos /* Roll back, deleting all (internal) atoms created after a particular ID. */ 568 1.1 christos void 569 1.1.1.2 christos ctf_str_rollback (ctf_dict_t *fp, ctf_snapshot_id_t id) 570 1.1 christos { 571 1.1 christos ctf_dynhash_iter_remove (fp->ctf_str_atoms, ctf_str_rollback_atom, &id); 572 1.1 christos } 573 1.1 christos 574 1.1 christos /* An adaptor around ctf_purge_atom_refs. */ 575 1.1 christos static void 576 1.1 christos ctf_str_purge_one_atom_refs (void *key _libctf_unused_, void *value, 577 1.1 christos void *arg _libctf_unused_) 578 1.1 christos { 579 1.1 christos ctf_str_atom_t *atom = (ctf_str_atom_t *) value; 580 1.1.1.4 christos 581 1.1 christos ctf_str_purge_atom_refs (atom); 582 1.1 christos } 583 1.1 christos 584 1.1 christos /* Remove all the recorded refs from the atoms table. */ 585 1.1.1.4 christos static void 586 1.1.1.2 christos ctf_str_purge_refs (ctf_dict_t *fp) 587 1.1 christos { 588 1.1.1.3 christos ctf_dynhash_iter (fp->ctf_str_atoms, ctf_str_purge_one_atom_refs, NULL); 589 1.1 christos } 590 1.1 christos 591 1.1 christos /* Update a list of refs to the specified value. */ 592 1.1 christos static void 593 1.1 christos ctf_str_update_refs (ctf_str_atom_t *refs, uint32_t value) 594 1.1 christos { 595 1.1 christos ctf_str_atom_ref_t *ref; 596 1.1.1.4 christos ctf_str_atom_ref_movable_t *movref; 597 1.1 christos 598 1.1 christos for (ref = ctf_list_next (&refs->csa_refs); ref != NULL; 599 1.1 christos ref = ctf_list_next (ref)) 600 1.1.1.3 christos *(ref->caf_ref) = value; 601 1.1.1.4 christos 602 1.1.1.4 christos for (movref = ctf_list_next (&refs->csa_movable_refs); 603 1.1.1.4 christos movref != NULL; movref = ctf_list_next (movref)) 604 1.1.1.4 christos *(movref->caf_ref) = value; 605 1.1 christos } 606 1.1 christos 607 1.1 christos /* Sort the strtab. */ 608 1.1 christos static int 609 1.1 christos ctf_str_sort_strtab (const void *a, const void *b) 610 1.1 christos { 611 1.1 christos ctf_str_atom_t **one = (ctf_str_atom_t **) a; 612 1.1 christos ctf_str_atom_t **two = (ctf_str_atom_t **) b; 613 1.1 christos 614 1.1 christos return (strcmp ((*one)->csa_str, (*two)->csa_str)); 615 1.1 christos } 616 1.1 christos 617 1.1 christos /* Write out and return a strtab containing all strings with recorded refs, 618 1.1.1.3 christos adjusting the refs to refer to the corresponding string. The returned 619 1.1.1.3 christos strtab is already assigned to strtab 0 in this dict, is owned by this 620 1.1.1.3 christos dict, and may be NULL on error. Also populate the synthetic strtab with 621 1.1.1.3 christos mappings from external strtab offsets to names, so we can look them up 622 1.1.1.3 christos with ctf_strptr(). Only external strtab offsets with references are 623 1.1.1.3 christos added. 624 1.1.1.3 christos 625 1.1.1.3 christos As a side effect, replaces the strtab of the current dict with the newly- 626 1.1.1.3 christos generated strtab. This is an exception to the general rule that 627 1.1.1.3 christos serialization does not change the dict passed in, because the alternative 628 1.1.1.3 christos is to copy the entire atoms table on every reserialization just to avoid 629 1.1.1.3 christos modifying the original, which is excessively costly for minimal gain. 630 1.1.1.3 christos 631 1.1.1.3 christos We use the lazy man's approach and double memory costs by always storing 632 1.1.1.3 christos atoms as individually allocated entities whenever they come from anywhere 633 1.1.1.3 christos but a freshly-opened, mmapped dict, even though after serialization there 634 1.1.1.3 christos is another copy in the strtab; this ensures that ctf_strptr()-returned 635 1.1.1.3 christos pointers to them remain valid for the lifetime of the dict. 636 1.1.1.3 christos 637 1.1.1.3 christos This is all rendered more complex because if a dict is ctf_open()ed it 638 1.1.1.3 christos will have a bunch of strings in its strtab already, and their strtab 639 1.1.1.3 christos offsets can never change (without piles of complexity to rescan the 640 1.1.1.3 christos entire dict just to get all the offsets to all of them into the atoms 641 1.1.1.3 christos table). Entries below the existing strtab limit are just copied into the 642 1.1.1.3 christos new dict: entries above it are new, and are are sorted first, then 643 1.1.1.3 christos appended to it. The sorting is purely a compression-efficiency 644 1.1.1.3 christos improvement, and we get nearly as good an improvement from sorting big 645 1.1.1.3 christos chunks like this as we would from sorting the whole thing. */ 646 1.1.1.3 christos 647 1.1.1.3 christos const ctf_strs_writable_t * 648 1.1.1.2 christos ctf_str_write_strtab (ctf_dict_t *fp) 649 1.1 christos { 650 1.1.1.3 christos ctf_strs_writable_t *strtab; 651 1.1.1.3 christos size_t strtab_count = 0; 652 1.1 christos uint32_t cur_stroff = 0; 653 1.1 christos ctf_str_atom_t **sorttab; 654 1.1.1.3 christos ctf_next_t *it = NULL; 655 1.1 christos size_t i; 656 1.1.1.3 christos void *v; 657 1.1.1.3 christos int err; 658 1.1.1.3 christos int new_strtab = 0; 659 1.1 christos int any_external = 0; 660 1.1 christos 661 1.1.1.3 christos strtab = calloc (1, sizeof (ctf_strs_writable_t)); 662 1.1.1.3 christos if (!strtab) 663 1.1.1.3 christos return NULL; 664 1.1.1.3 christos 665 1.1.1.3 christos /* The strtab contains the existing string table at its start: figure out 666 1.1.1.3 christos how many new strings we need to add. We only need to add new strings 667 1.1.1.3 christos that have no external offset, that have refs, and that are found in the 668 1.1.1.3 christos provisional strtab. If the existing strtab is empty we also need to 669 1.1.1.3 christos add the null string at its start. */ 670 1.1 christos 671 1.1.1.3 christos strtab->cts_len = fp->ctf_str[CTF_STRTAB_0].cts_len; 672 1.1.1.3 christos 673 1.1.1.3 christos if (strtab->cts_len == 0) 674 1.1 christos { 675 1.1.1.3 christos new_strtab = 1; 676 1.1.1.3 christos strtab->cts_len++; /* For the \0. */ 677 1.1 christos } 678 1.1 christos 679 1.1.1.3 christos /* Count new entries in the strtab: i.e. entries in the provisional 680 1.1.1.3 christos strtab. Ignore any entry for \0, entries which ended up in the 681 1.1.1.3 christos external strtab, and unreferenced entries. */ 682 1.1 christos 683 1.1.1.3 christos while ((err = ctf_dynhash_next (fp->ctf_prov_strtab, &it, NULL, &v)) == 0) 684 1.1.1.3 christos { 685 1.1.1.3 christos const char *str = (const char *) v; 686 1.1.1.3 christos ctf_str_atom_t *atom; 687 1.1 christos 688 1.1.1.3 christos atom = ctf_dynhash_lookup (fp->ctf_str_atoms, str); 689 1.1.1.3 christos if (!ctf_assert (fp, atom)) 690 1.1.1.3 christos goto err_strtab; 691 1.1.1.3 christos 692 1.1.1.4 christos if (atom->csa_str[0] == 0 || atom->csa_external_offset 693 1.1.1.4 christos || (ctf_list_empty_p (&atom->csa_refs) 694 1.1.1.4 christos && ctf_list_empty_p (&atom->csa_movable_refs))) 695 1.1.1.3 christos continue; 696 1.1.1.3 christos 697 1.1.1.3 christos strtab->cts_len += strlen (atom->csa_str) + 1; 698 1.1.1.3 christos strtab_count++; 699 1.1.1.3 christos } 700 1.1.1.3 christos if (err != ECTF_NEXT_END) 701 1.1.1.3 christos { 702 1.1.1.3 christos ctf_dprintf ("ctf_str_write_strtab: error counting strtab entries: %s\n", 703 1.1.1.3 christos ctf_errmsg (err)); 704 1.1.1.3 christos goto err_strtab; 705 1.1.1.3 christos } 706 1.1.1.3 christos 707 1.1.1.3 christos ctf_dprintf ("%lu bytes of strings in strtab: %lu pre-existing.\n", 708 1.1.1.3 christos (unsigned long) strtab->cts_len, 709 1.1.1.3 christos (unsigned long) fp->ctf_str[CTF_STRTAB_0].cts_len); 710 1.1.1.3 christos 711 1.1.1.3 christos /* Sort the new part of the strtab. */ 712 1.1.1.3 christos 713 1.1.1.3 christos sorttab = calloc (strtab_count, sizeof (ctf_str_atom_t *)); 714 1.1 christos if (!sorttab) 715 1.1.1.3 christos { 716 1.1.1.3 christos ctf_set_errno (fp, ENOMEM); 717 1.1.1.3 christos goto err_strtab; 718 1.1.1.3 christos } 719 1.1.1.3 christos 720 1.1.1.3 christos i = 0; 721 1.1.1.3 christos while ((err = ctf_dynhash_next (fp->ctf_prov_strtab, &it, NULL, &v)) == 0) 722 1.1.1.3 christos { 723 1.1.1.3 christos ctf_str_atom_t *atom; 724 1.1 christos 725 1.1.1.3 christos atom = ctf_dynhash_lookup (fp->ctf_str_atoms, v); 726 1.1.1.3 christos if (!ctf_assert (fp, atom)) 727 1.1.1.3 christos goto err_sorttab; 728 1.1.1.3 christos 729 1.1.1.4 christos if (atom->csa_str[0] == 0 || atom->csa_external_offset 730 1.1.1.4 christos || (ctf_list_empty_p (&atom->csa_refs) 731 1.1.1.4 christos && ctf_list_empty_p (&atom->csa_movable_refs))) 732 1.1.1.3 christos continue; 733 1.1.1.3 christos 734 1.1.1.3 christos sorttab[i++] = atom; 735 1.1.1.3 christos } 736 1.1 christos 737 1.1.1.3 christos qsort (sorttab, strtab_count, sizeof (ctf_str_atom_t *), 738 1.1 christos ctf_str_sort_strtab); 739 1.1 christos 740 1.1.1.3 christos if ((strtab->cts_strs = malloc (strtab->cts_len)) == NULL) 741 1.1.1.3 christos goto err_sorttab; 742 1.1 christos 743 1.1.1.3 christos cur_stroff = fp->ctf_str[CTF_STRTAB_0].cts_len; 744 1.1.1.3 christos 745 1.1.1.3 christos if (new_strtab) 746 1.1 christos { 747 1.1.1.3 christos strtab->cts_strs[0] = 0; 748 1.1.1.3 christos cur_stroff++; 749 1.1.1.3 christos } 750 1.1.1.3 christos else 751 1.1.1.3 christos memcpy (strtab->cts_strs, fp->ctf_str[CTF_STRTAB_0].cts_strs, 752 1.1.1.3 christos fp->ctf_str[CTF_STRTAB_0].cts_len); 753 1.1.1.3 christos 754 1.1.1.3 christos /* Work over the sorttab, add its strings to the strtab, and remember 755 1.1.1.3 christos where they are in the csa_offset for the appropriate atom. No ref 756 1.1.1.3 christos updating is done at this point, because refs might well relate to 757 1.1.1.3 christos already-existing strings, or external strings, which do not need adding 758 1.1.1.3 christos to the strtab and may not be in the sorttab. */ 759 1.1.1.3 christos 760 1.1.1.3 christos for (i = 0; i < strtab_count; i++) 761 1.1.1.3 christos { 762 1.1.1.3 christos sorttab[i]->csa_offset = cur_stroff; 763 1.1.1.3 christos strcpy (&strtab->cts_strs[cur_stroff], sorttab[i]->csa_str); 764 1.1.1.3 christos cur_stroff += strlen (sorttab[i]->csa_str) + 1; 765 1.1.1.3 christos } 766 1.1.1.3 christos free (sorttab); 767 1.1.1.3 christos sorttab = NULL; 768 1.1.1.3 christos 769 1.1.1.3 christos /* Update all refs, then purge them as no longer necessary: also update 770 1.1.1.3 christos the strtab appropriately. */ 771 1.1.1.3 christos 772 1.1.1.3 christos while ((err = ctf_dynhash_next (fp->ctf_str_atoms, &it, NULL, &v)) == 0) 773 1.1.1.3 christos { 774 1.1.1.3 christos ctf_str_atom_t *atom = (ctf_str_atom_t *) v; 775 1.1.1.3 christos uint32_t offset; 776 1.1 christos 777 1.1.1.4 christos if (ctf_list_empty_p (&atom->csa_refs) && 778 1.1.1.4 christos ctf_list_empty_p (&atom->csa_movable_refs)) 779 1.1.1.3 christos continue; 780 1.1.1.3 christos 781 1.1.1.3 christos if (atom->csa_external_offset) 782 1.1.1.3 christos { 783 1.1 christos any_external = 1; 784 1.1.1.3 christos offset = atom->csa_external_offset; 785 1.1 christos } 786 1.1 christos else 787 1.1.1.3 christos offset = atom->csa_offset; 788 1.1.1.3 christos ctf_str_update_refs (atom, offset); 789 1.1 christos } 790 1.1.1.3 christos if (err != ECTF_NEXT_END) 791 1.1.1.3 christos { 792 1.1.1.3 christos ctf_dprintf ("ctf_str_write_strtab: error iterating over atoms while updating refs: %s\n", 793 1.1.1.3 christos ctf_errmsg (err)); 794 1.1.1.3 christos goto err_strtab; 795 1.1.1.3 christos } 796 1.1.1.3 christos ctf_str_purge_refs (fp); 797 1.1 christos 798 1.1 christos if (!any_external) 799 1.1 christos { 800 1.1 christos ctf_dynhash_destroy (fp->ctf_syn_ext_strtab); 801 1.1 christos fp->ctf_syn_ext_strtab = NULL; 802 1.1 christos } 803 1.1 christos 804 1.1.1.3 christos /* Replace the old strtab with the new one in this dict. */ 805 1.1.1.3 christos 806 1.1.1.3 christos if (fp->ctf_dynstrtab) 807 1.1.1.3 christos { 808 1.1.1.3 christos free (fp->ctf_dynstrtab->cts_strs); 809 1.1.1.3 christos free (fp->ctf_dynstrtab); 810 1.1.1.3 christos } 811 1.1.1.3 christos 812 1.1.1.3 christos fp->ctf_dynstrtab = strtab; 813 1.1.1.3 christos fp->ctf_str[CTF_STRTAB_0].cts_strs = strtab->cts_strs; 814 1.1.1.3 christos fp->ctf_str[CTF_STRTAB_0].cts_len = strtab->cts_len; 815 1.1.1.3 christos 816 1.1 christos /* All the provisional strtab entries are now real strtab entries, and 817 1.1 christos ctf_strptr() will find them there. The provisional offset now starts right 818 1.1 christos beyond the new end of the strtab. */ 819 1.1 christos 820 1.1 christos ctf_dynhash_empty (fp->ctf_prov_strtab); 821 1.1.1.3 christos fp->ctf_str_prov_offset = strtab->cts_len + 1; 822 1.1 christos return strtab; 823 1.1 christos 824 1.1.1.3 christos err_sorttab: 825 1.1 christos free (sorttab); 826 1.1.1.3 christos err_strtab: 827 1.1.1.3 christos free (strtab); 828 1.1.1.3 christos return NULL; 829 1.1 christos } 830