1a85fe12eSEd Maste /*- 2a85fe12eSEd Maste * Copyright (c) 2013, Joseph Koshy 3a85fe12eSEd Maste * All rights reserved. 4a85fe12eSEd Maste * 5a85fe12eSEd Maste * Redistribution and use in source and binary forms, with or without 6a85fe12eSEd Maste * modification, are permitted provided that the following conditions 7a85fe12eSEd Maste * are met: 8a85fe12eSEd Maste * 1. Redistributions of source code must retain the above copyright 9a85fe12eSEd Maste * notice, this list of conditions and the following disclaimer 10a85fe12eSEd Maste * in this position and unchanged. 11a85fe12eSEd Maste * 2. Redistributions in binary form must reproduce the above copyright 12a85fe12eSEd Maste * notice, this list of conditions and the following disclaimer in the 13a85fe12eSEd Maste * documentation and/or other materials provided with the distribution. 14a85fe12eSEd Maste * 15a85fe12eSEd Maste * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 16a85fe12eSEd Maste * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17a85fe12eSEd Maste * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18a85fe12eSEd Maste * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 19a85fe12eSEd Maste * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20a85fe12eSEd Maste * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21a85fe12eSEd Maste * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22a85fe12eSEd Maste * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23a85fe12eSEd Maste * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24a85fe12eSEd Maste * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25a85fe12eSEd Maste */ 26a85fe12eSEd Maste 27a85fe12eSEd Maste #include <sys/param.h> 28a85fe12eSEd Maste #include <sys/queue.h> 29a85fe12eSEd Maste 30a85fe12eSEd Maste #include <assert.h> 31a85fe12eSEd Maste #include <errno.h> 32a85fe12eSEd Maste #include <gelf.h> 33a85fe12eSEd Maste #include <stdlib.h> 34a85fe12eSEd Maste #include <string.h> 35a85fe12eSEd Maste 36a85fe12eSEd Maste #include "libelftc.h" 37a85fe12eSEd Maste #include "_libelftc.h" 38a85fe12eSEd Maste 39a85fe12eSEd Maste ELFTC_VCSID("$Id: elftc_string_table.c 2869 2013-01-06 13:29:18Z jkoshy $"); 40a85fe12eSEd Maste 41a85fe12eSEd Maste #define ELFTC_STRING_TABLE_DEFAULT_SIZE (4*1024) 42a85fe12eSEd Maste #define ELFTC_STRING_TABLE_EXPECTED_STRING_SIZE 16 43a85fe12eSEd Maste #define ELFTC_STRING_TABLE_EXPECTED_CHAIN_LENGTH 8 44a85fe12eSEd Maste #define ELFTC_STRING_TABLE_POOL_SIZE_INCREMENT (4*1024) 45a85fe12eSEd Maste 46a85fe12eSEd Maste struct _Elftc_String_Table_Entry { 47*b90eaf94SMark Johnston ssize_t ste_idx; 48a85fe12eSEd Maste SLIST_ENTRY(_Elftc_String_Table_Entry) ste_next; 49a85fe12eSEd Maste }; 50a85fe12eSEd Maste 51a85fe12eSEd Maste #define ELFTC_STRING_TABLE_COMPACTION_FLAG 0x1 52a85fe12eSEd Maste #define ELFTC_STRING_TABLE_LENGTH(st) ((st)->st_len >> 1) 53a85fe12eSEd Maste #define ELFTC_STRING_TABLE_CLEAR_COMPACTION_FLAG(st) do { \ 54a85fe12eSEd Maste (st)->st_len &= ~ELFTC_STRING_TABLE_COMPACTION_FLAG; \ 55a85fe12eSEd Maste } while (0) 56a85fe12eSEd Maste #define ELFTC_STRING_TABLE_SET_COMPACTION_FLAG(st) do { \ 57a85fe12eSEd Maste (st)->st_len |= ELFTC_STRING_TABLE_COMPACTION_FLAG; \ 58a85fe12eSEd Maste } while (0) 59a85fe12eSEd Maste #define ELFTC_STRING_TABLE_UPDATE_LENGTH(st, len) do { \ 60a85fe12eSEd Maste (st)->st_len = \ 61a85fe12eSEd Maste ((st)->st_len & \ 62a85fe12eSEd Maste ELFTC_STRING_TABLE_COMPACTION_FLAG) | \ 63a85fe12eSEd Maste ((len) << 1); \ 64a85fe12eSEd Maste } while (0) 65a85fe12eSEd Maste 66a85fe12eSEd Maste struct _Elftc_String_Table { 67*b90eaf94SMark Johnston size_t st_len; /* length and flags */ 68a85fe12eSEd Maste int st_nbuckets; 69*b90eaf94SMark Johnston size_t st_string_pool_size; 70a85fe12eSEd Maste char *st_string_pool; 71a85fe12eSEd Maste SLIST_HEAD(_Elftc_String_Table_Bucket, 72a85fe12eSEd Maste _Elftc_String_Table_Entry) st_buckets[]; 73a85fe12eSEd Maste }; 74a85fe12eSEd Maste 75a85fe12eSEd Maste static struct _Elftc_String_Table_Entry * 76a85fe12eSEd Maste elftc_string_table_find_hash_entry(Elftc_String_Table *st, const char *string, 77a85fe12eSEd Maste int *rhashindex) 78a85fe12eSEd Maste { 79a85fe12eSEd Maste struct _Elftc_String_Table_Entry *ste; 80a85fe12eSEd Maste int hashindex; 81a85fe12eSEd Maste char *s; 82a85fe12eSEd Maste 83a85fe12eSEd Maste hashindex = libelftc_hash_string(string) % st->st_nbuckets; 84a85fe12eSEd Maste 85a85fe12eSEd Maste if (rhashindex) 86a85fe12eSEd Maste *rhashindex = hashindex; 87a85fe12eSEd Maste 88a85fe12eSEd Maste SLIST_FOREACH(ste, &st->st_buckets[hashindex], ste_next) { 89*b90eaf94SMark Johnston s = st->st_string_pool + labs(ste->ste_idx); 90a85fe12eSEd Maste 91a85fe12eSEd Maste assert(s > st->st_string_pool && 92a85fe12eSEd Maste s < st->st_string_pool + st->st_string_pool_size); 93a85fe12eSEd Maste 94a85fe12eSEd Maste if (strcmp(s, string) == 0) 95a85fe12eSEd Maste return (ste); 96a85fe12eSEd Maste } 97a85fe12eSEd Maste 98a85fe12eSEd Maste return (NULL); 99a85fe12eSEd Maste } 100a85fe12eSEd Maste 101a85fe12eSEd Maste static int 102a85fe12eSEd Maste elftc_string_table_add_to_pool(Elftc_String_Table *st, const char *string) 103a85fe12eSEd Maste { 104a85fe12eSEd Maste char *newpool; 105*b90eaf94SMark Johnston size_t len, newsize, stlen; 106a85fe12eSEd Maste 107a85fe12eSEd Maste len = strlen(string) + 1; /* length, including the trailing NUL */ 108a85fe12eSEd Maste stlen = ELFTC_STRING_TABLE_LENGTH(st); 109a85fe12eSEd Maste 110a85fe12eSEd Maste /* Resize the pool, if needed. */ 111a85fe12eSEd Maste if (stlen + len >= st->st_string_pool_size) { 112a85fe12eSEd Maste newsize = roundup(st->st_string_pool_size + 113a85fe12eSEd Maste ELFTC_STRING_TABLE_POOL_SIZE_INCREMENT, 114a85fe12eSEd Maste ELFTC_STRING_TABLE_POOL_SIZE_INCREMENT); 115a85fe12eSEd Maste if ((newpool = realloc(st->st_string_pool, newsize)) == 116a85fe12eSEd Maste NULL) 117a85fe12eSEd Maste return (0); 118a85fe12eSEd Maste st->st_string_pool = newpool; 119a85fe12eSEd Maste st->st_string_pool_size = newsize; 120a85fe12eSEd Maste } 121a85fe12eSEd Maste 12270b0aff9SMark Johnston memcpy(st->st_string_pool + stlen, string, len); 123a85fe12eSEd Maste ELFTC_STRING_TABLE_UPDATE_LENGTH(st, stlen + len); 124a85fe12eSEd Maste 125a85fe12eSEd Maste return (stlen); 126a85fe12eSEd Maste } 127a85fe12eSEd Maste 128a85fe12eSEd Maste Elftc_String_Table * 129*b90eaf94SMark Johnston elftc_string_table_create(size_t sizehint) 130a85fe12eSEd Maste { 131a85fe12eSEd Maste struct _Elftc_String_Table *st; 132*b90eaf94SMark Johnston int n, nbuckets, tablesize; 133a85fe12eSEd Maste 134a85fe12eSEd Maste if (sizehint < ELFTC_STRING_TABLE_DEFAULT_SIZE) 135a85fe12eSEd Maste sizehint = ELFTC_STRING_TABLE_DEFAULT_SIZE; 136a85fe12eSEd Maste 137a85fe12eSEd Maste nbuckets = sizehint / (ELFTC_STRING_TABLE_EXPECTED_CHAIN_LENGTH * 138a85fe12eSEd Maste ELFTC_STRING_TABLE_EXPECTED_STRING_SIZE); 139a85fe12eSEd Maste 140a85fe12eSEd Maste tablesize = sizeof(struct _Elftc_String_Table) + 141a85fe12eSEd Maste nbuckets * sizeof(struct _Elftc_String_Table_Bucket); 142a85fe12eSEd Maste 143a85fe12eSEd Maste if ((st = malloc(tablesize)) == NULL) 144a85fe12eSEd Maste return (NULL); 145a85fe12eSEd Maste if ((st->st_string_pool = malloc(sizehint)) == NULL) { 146a85fe12eSEd Maste free(st); 147a85fe12eSEd Maste return (NULL); 148a85fe12eSEd Maste } 149a85fe12eSEd Maste 150a85fe12eSEd Maste for (n = 0; n < nbuckets; n++) 151a85fe12eSEd Maste SLIST_INIT(&st->st_buckets[n]); 152a85fe12eSEd Maste 153a85fe12eSEd Maste st->st_len = 0; 154a85fe12eSEd Maste st->st_nbuckets = nbuckets; 155a85fe12eSEd Maste st->st_string_pool_size = sizehint; 156a85fe12eSEd Maste *st->st_string_pool = '\0'; 157a85fe12eSEd Maste ELFTC_STRING_TABLE_UPDATE_LENGTH(st, 1); 158a85fe12eSEd Maste 159a85fe12eSEd Maste return (st); 160a85fe12eSEd Maste } 161a85fe12eSEd Maste 162a85fe12eSEd Maste void 163a85fe12eSEd Maste elftc_string_table_destroy(Elftc_String_Table *st) 164a85fe12eSEd Maste { 165a85fe12eSEd Maste int n; 166a85fe12eSEd Maste struct _Elftc_String_Table_Entry *s, *t; 167a85fe12eSEd Maste 168a85fe12eSEd Maste for (n = 0; n < st->st_nbuckets; n++) 169a85fe12eSEd Maste SLIST_FOREACH_SAFE(s, &st->st_buckets[n], ste_next, t) 170a85fe12eSEd Maste free(s); 171a85fe12eSEd Maste free(st->st_string_pool); 172a85fe12eSEd Maste free(st); 173a85fe12eSEd Maste } 174a85fe12eSEd Maste 175a85fe12eSEd Maste Elftc_String_Table * 176*b90eaf94SMark Johnston elftc_string_table_from_section(Elf_Scn *scn, size_t sizehint) 177a85fe12eSEd Maste { 178a85fe12eSEd Maste Elf_Data *d; 179a85fe12eSEd Maste GElf_Shdr sh; 180a85fe12eSEd Maste const char *s, *end; 181a85fe12eSEd Maste Elftc_String_Table *st; 182*b90eaf94SMark Johnston size_t len; 183a85fe12eSEd Maste 184a85fe12eSEd Maste /* Verify the type of the section passed in. */ 185a85fe12eSEd Maste if (gelf_getshdr(scn, &sh) == NULL || 186a85fe12eSEd Maste sh.sh_type != SHT_STRTAB) { 187a85fe12eSEd Maste errno = EINVAL; 188a85fe12eSEd Maste return (NULL); 189a85fe12eSEd Maste } 190a85fe12eSEd Maste 191a85fe12eSEd Maste if ((d = elf_getdata(scn, NULL)) == NULL || 192a85fe12eSEd Maste d->d_size == 0) { 193a85fe12eSEd Maste errno = EINVAL; 194a85fe12eSEd Maste return (NULL); 195a85fe12eSEd Maste } 196a85fe12eSEd Maste 197a85fe12eSEd Maste if ((st = elftc_string_table_create(sizehint)) == NULL) 198a85fe12eSEd Maste return (NULL); 199a85fe12eSEd Maste 200a85fe12eSEd Maste s = d->d_buf; 201a85fe12eSEd Maste 202a85fe12eSEd Maste /* 203a85fe12eSEd Maste * Verify that the first byte of the data buffer is '\0'. 204a85fe12eSEd Maste */ 205a85fe12eSEd Maste if (*s != '\0') { 206a85fe12eSEd Maste errno = EINVAL; 207a85fe12eSEd Maste goto fail; 208a85fe12eSEd Maste } 209a85fe12eSEd Maste 210a85fe12eSEd Maste end = s + d->d_size; 211a85fe12eSEd Maste 212a85fe12eSEd Maste /* 213a85fe12eSEd Maste * Skip the first '\0' and insert the strings in the buffer, 214a85fe12eSEd Maste * in order. 215a85fe12eSEd Maste */ 216a85fe12eSEd Maste for (s += 1; s < end; s += len) { 217a85fe12eSEd Maste if (elftc_string_table_insert(st, s) == 0) 218a85fe12eSEd Maste goto fail; 219a85fe12eSEd Maste 220a85fe12eSEd Maste len = strlen(s) + 1; /* Include space for the trailing NUL. */ 221a85fe12eSEd Maste } 222a85fe12eSEd Maste 223a85fe12eSEd Maste return (st); 224a85fe12eSEd Maste 225a85fe12eSEd Maste fail: 226a85fe12eSEd Maste if (st) 227a85fe12eSEd Maste (void) elftc_string_table_destroy(st); 228a85fe12eSEd Maste 229a85fe12eSEd Maste return (NULL); 230a85fe12eSEd Maste } 231a85fe12eSEd Maste 232a85fe12eSEd Maste const char * 233a85fe12eSEd Maste elftc_string_table_image(Elftc_String_Table *st, size_t *size) 234a85fe12eSEd Maste { 235a85fe12eSEd Maste char *r, *s, *end; 236a85fe12eSEd Maste struct _Elftc_String_Table_Entry *ste; 237a85fe12eSEd Maste struct _Elftc_String_Table_Bucket *head; 238*b90eaf94SMark Johnston size_t copied, offset, length, newsize; 239*b90eaf94SMark Johnston int hashindex; 240a85fe12eSEd Maste 241a85fe12eSEd Maste /* 242a85fe12eSEd Maste * For the common case of a string table has not seen 243a85fe12eSEd Maste * a string deletion, we can just export the current 244a85fe12eSEd Maste * pool. 245a85fe12eSEd Maste */ 246a85fe12eSEd Maste if ((st->st_len & ELFTC_STRING_TABLE_COMPACTION_FLAG) == 0) { 247a85fe12eSEd Maste if (size) 248a85fe12eSEd Maste *size = ELFTC_STRING_TABLE_LENGTH(st); 249a85fe12eSEd Maste return (st->st_string_pool); 250a85fe12eSEd Maste } 251a85fe12eSEd Maste 252a85fe12eSEd Maste /* 253a85fe12eSEd Maste * Otherwise, compact the string table in-place. 254a85fe12eSEd Maste */ 255a85fe12eSEd Maste assert(*st->st_string_pool == '\0'); 256a85fe12eSEd Maste 257a85fe12eSEd Maste newsize = 1; 258a85fe12eSEd Maste end = st->st_string_pool + ELFTC_STRING_TABLE_LENGTH(st); 259a85fe12eSEd Maste 260a85fe12eSEd Maste for (r = s = st->st_string_pool + 1; 261a85fe12eSEd Maste s < end; 262a85fe12eSEd Maste s += length, r += copied) { 263a85fe12eSEd Maste 264a85fe12eSEd Maste copied = 0; 265a85fe12eSEd Maste length = strlen(s) + 1; 266a85fe12eSEd Maste 267a85fe12eSEd Maste ste = elftc_string_table_find_hash_entry(st, s, 268a85fe12eSEd Maste &hashindex); 269a85fe12eSEd Maste head = &st->st_buckets[hashindex]; 270a85fe12eSEd Maste 271a85fe12eSEd Maste assert(ste != NULL); 272a85fe12eSEd Maste 273a85fe12eSEd Maste /* Ignore deleted strings. */ 274a85fe12eSEd Maste if (ste->ste_idx < 0) { 275a85fe12eSEd Maste SLIST_REMOVE(head, ste, _Elftc_String_Table_Entry, 276a85fe12eSEd Maste ste_next); 277a85fe12eSEd Maste free(ste); 278a85fe12eSEd Maste continue; 279a85fe12eSEd Maste } 280a85fe12eSEd Maste 281a85fe12eSEd Maste /* Move 'live' strings up. */ 282a85fe12eSEd Maste offset = newsize; 283a85fe12eSEd Maste newsize += length; 284a85fe12eSEd Maste copied = length; 285a85fe12eSEd Maste 286a85fe12eSEd Maste if (r == s) /* Nothing removed yet. */ 287a85fe12eSEd Maste continue; 288a85fe12eSEd Maste 289a85fe12eSEd Maste memmove(r, s, copied); 290a85fe12eSEd Maste 291a85fe12eSEd Maste /* Update the index for this entry. */ 292a85fe12eSEd Maste ste->ste_idx = offset; 293a85fe12eSEd Maste } 294a85fe12eSEd Maste 295a85fe12eSEd Maste ELFTC_STRING_TABLE_CLEAR_COMPACTION_FLAG(st); 296a85fe12eSEd Maste ELFTC_STRING_TABLE_UPDATE_LENGTH(st, newsize); 297a85fe12eSEd Maste 298a85fe12eSEd Maste if (size) 299a85fe12eSEd Maste *size = newsize; 300a85fe12eSEd Maste 301a85fe12eSEd Maste return (st->st_string_pool); 302a85fe12eSEd Maste } 303a85fe12eSEd Maste 304a85fe12eSEd Maste size_t 305a85fe12eSEd Maste elftc_string_table_insert(Elftc_String_Table *st, const char *string) 306a85fe12eSEd Maste { 307a85fe12eSEd Maste struct _Elftc_String_Table_Entry *ste; 308*b90eaf94SMark Johnston ssize_t idx; 309*b90eaf94SMark Johnston int hashindex; 310a85fe12eSEd Maste 311a85fe12eSEd Maste hashindex = 0; 312a85fe12eSEd Maste 313a85fe12eSEd Maste ste = elftc_string_table_find_hash_entry(st, string, &hashindex); 314a85fe12eSEd Maste 315a85fe12eSEd Maste assert(hashindex >= 0 && hashindex < st->st_nbuckets); 316a85fe12eSEd Maste 317a85fe12eSEd Maste if (ste == NULL) { 318a85fe12eSEd Maste if ((ste = malloc(sizeof(*ste))) == NULL) 319a85fe12eSEd Maste return (0); 320a85fe12eSEd Maste if ((ste->ste_idx = elftc_string_table_add_to_pool(st, 321a85fe12eSEd Maste string)) == 0) { 322a85fe12eSEd Maste free(ste); 323a85fe12eSEd Maste return (0); 324a85fe12eSEd Maste } 325a85fe12eSEd Maste 326a85fe12eSEd Maste SLIST_INSERT_HEAD(&st->st_buckets[hashindex], ste, ste_next); 327a85fe12eSEd Maste } 328a85fe12eSEd Maste 329a85fe12eSEd Maste idx = ste->ste_idx; 330a85fe12eSEd Maste if (idx < 0) /* Undelete. */ 331*b90eaf94SMark Johnston ste->ste_idx = idx = -idx; 332a85fe12eSEd Maste 333a85fe12eSEd Maste return (idx); 334a85fe12eSEd Maste } 335a85fe12eSEd Maste 336a85fe12eSEd Maste size_t 337a85fe12eSEd Maste elftc_string_table_lookup(Elftc_String_Table *st, const char *string) 338a85fe12eSEd Maste { 339a85fe12eSEd Maste struct _Elftc_String_Table_Entry *ste; 340*b90eaf94SMark Johnston ssize_t idx; 341*b90eaf94SMark Johnston int hashindex; 342a85fe12eSEd Maste 343a85fe12eSEd Maste ste = elftc_string_table_find_hash_entry(st, string, &hashindex); 344a85fe12eSEd Maste 345a85fe12eSEd Maste assert(hashindex >= 0 && hashindex < st->st_nbuckets); 346a85fe12eSEd Maste 347a85fe12eSEd Maste if (ste == NULL || (idx = ste->ste_idx) < 0) 348a85fe12eSEd Maste return (0); 349a85fe12eSEd Maste 350a85fe12eSEd Maste return (idx); 351a85fe12eSEd Maste } 352a85fe12eSEd Maste 353a85fe12eSEd Maste int 354a85fe12eSEd Maste elftc_string_table_remove(Elftc_String_Table *st, const char *string) 355a85fe12eSEd Maste { 356a85fe12eSEd Maste struct _Elftc_String_Table_Entry *ste; 357*b90eaf94SMark Johnston ssize_t idx; 358a85fe12eSEd Maste 359a85fe12eSEd Maste ste = elftc_string_table_find_hash_entry(st, string, NULL); 360a85fe12eSEd Maste 361a85fe12eSEd Maste if (ste == NULL || (idx = ste->ste_idx) < 0) 362a85fe12eSEd Maste return (ELFTC_FAILURE); 363a85fe12eSEd Maste 364*b90eaf94SMark Johnston assert(idx > 0 && (size_t)idx < ELFTC_STRING_TABLE_LENGTH(st)); 365a85fe12eSEd Maste 366*b90eaf94SMark Johnston ste->ste_idx = -idx; 367a85fe12eSEd Maste 368a85fe12eSEd Maste ELFTC_STRING_TABLE_SET_COMPACTION_FLAG(st); 369a85fe12eSEd Maste 370a85fe12eSEd Maste return (ELFTC_SUCCESS); 371a85fe12eSEd Maste } 372a85fe12eSEd Maste 373a85fe12eSEd Maste const char * 374a85fe12eSEd Maste elftc_string_table_to_string(Elftc_String_Table *st, size_t offset) 375a85fe12eSEd Maste { 376a85fe12eSEd Maste const char *s; 377a85fe12eSEd Maste 378a85fe12eSEd Maste s = st->st_string_pool + offset; 379a85fe12eSEd Maste 380a85fe12eSEd Maste /* 381a85fe12eSEd Maste * Check for: 382a85fe12eSEd Maste * - An offset value within pool bounds. 383a85fe12eSEd Maste * - A non-NUL byte at the specified offset. 384a85fe12eSEd Maste * - The end of the prior string at offset - 1. 385a85fe12eSEd Maste */ 386a85fe12eSEd Maste if (offset == 0 || offset >= ELFTC_STRING_TABLE_LENGTH(st) || 387a85fe12eSEd Maste *s == '\0' || *(s - 1) != '\0') { 388a85fe12eSEd Maste errno = EINVAL; 389a85fe12eSEd Maste return (NULL); 390a85fe12eSEd Maste } 391a85fe12eSEd Maste 392a85fe12eSEd Maste return (s); 393a85fe12eSEd Maste } 394