Path: blob/main/contrib/elftoolchain/libelftc/elftc_string_table.c
39536 views
/*-1* Copyright (c) 2013, Joseph Koshy2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer9* in this position and unchanged.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR15* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES16* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.17* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,18* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT19* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,20* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY21* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT22* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF23* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.24*/2526#include <sys/param.h>27#include <sys/queue.h>2829#include <assert.h>30#include <errno.h>31#include <gelf.h>32#include <stdlib.h>33#include <string.h>3435#include "libelftc.h"36#include "_libelftc.h"3738ELFTC_VCSID("$Id: elftc_string_table.c 3750 2019-06-28 01:12:10Z emaste $");3940#define ELFTC_STRING_TABLE_DEFAULT_SIZE (4*1024)41#define ELFTC_STRING_TABLE_EXPECTED_STRING_SIZE 1642#define ELFTC_STRING_TABLE_EXPECTED_CHAIN_LENGTH 843#define ELFTC_STRING_TABLE_POOL_SIZE_INCREMENT (4*1024)4445struct _Elftc_String_Table_Entry {46ssize_t ste_idx;47SLIST_ENTRY(_Elftc_String_Table_Entry) ste_next;48};4950#define ELFTC_STRING_TABLE_COMPACTION_FLAG 0x151#define ELFTC_STRING_TABLE_LENGTH(st) ((st)->st_len >> 1)52#define ELFTC_STRING_TABLE_CLEAR_COMPACTION_FLAG(st) do { \53(st)->st_len &= ~ELFTC_STRING_TABLE_COMPACTION_FLAG; \54} while (0)55#define ELFTC_STRING_TABLE_SET_COMPACTION_FLAG(st) do { \56(st)->st_len |= ELFTC_STRING_TABLE_COMPACTION_FLAG; \57} while (0)58#define ELFTC_STRING_TABLE_UPDATE_LENGTH(st, len) do { \59(st)->st_len = \60((st)->st_len & \61ELFTC_STRING_TABLE_COMPACTION_FLAG) | \62((len) << 1); \63} while (0)6465struct _Elftc_String_Table {66size_t st_len; /* length and flags */67int st_nbuckets;68size_t st_string_pool_size;69char *st_string_pool;70SLIST_HEAD(_Elftc_String_Table_Bucket,71_Elftc_String_Table_Entry) st_buckets[];72};7374static struct _Elftc_String_Table_Entry *75elftc_string_table_find_hash_entry(Elftc_String_Table *st, const char *string,76int *rhashindex)77{78struct _Elftc_String_Table_Entry *ste;79int hashindex;80char *s;8182hashindex = libelftc_hash_string(string) % st->st_nbuckets;8384if (rhashindex)85*rhashindex = hashindex;8687SLIST_FOREACH(ste, &st->st_buckets[hashindex], ste_next) {88s = st->st_string_pool + labs(ste->ste_idx);8990assert(s > st->st_string_pool &&91s < st->st_string_pool + st->st_string_pool_size);9293if (strcmp(s, string) == 0)94return (ste);95}9697return (NULL);98}99100static int101elftc_string_table_add_to_pool(Elftc_String_Table *st, const char *string)102{103char *newpool;104size_t len, newsize, stlen;105106len = strlen(string) + 1; /* length, including the trailing NUL */107stlen = ELFTC_STRING_TABLE_LENGTH(st);108109/* Resize the pool, if needed. */110if (stlen + len >= st->st_string_pool_size) {111newsize = roundup(st->st_string_pool_size +112ELFTC_STRING_TABLE_POOL_SIZE_INCREMENT,113ELFTC_STRING_TABLE_POOL_SIZE_INCREMENT);114if ((newpool = realloc(st->st_string_pool, newsize)) ==115NULL)116return (0);117st->st_string_pool = newpool;118st->st_string_pool_size = newsize;119}120121memcpy(st->st_string_pool + stlen, string, len);122ELFTC_STRING_TABLE_UPDATE_LENGTH(st, stlen + len);123124return (stlen);125}126127Elftc_String_Table *128elftc_string_table_create(size_t sizehint)129{130struct _Elftc_String_Table *st;131int n, nbuckets, tablesize;132133if (sizehint < ELFTC_STRING_TABLE_DEFAULT_SIZE)134sizehint = ELFTC_STRING_TABLE_DEFAULT_SIZE;135136nbuckets = sizehint / (ELFTC_STRING_TABLE_EXPECTED_CHAIN_LENGTH *137ELFTC_STRING_TABLE_EXPECTED_STRING_SIZE);138139tablesize = sizeof(struct _Elftc_String_Table) +140nbuckets * sizeof(struct _Elftc_String_Table_Bucket);141142if ((st = malloc(tablesize)) == NULL)143return (NULL);144if ((st->st_string_pool = malloc(sizehint)) == NULL) {145free(st);146return (NULL);147}148149for (n = 0; n < nbuckets; n++)150SLIST_INIT(&st->st_buckets[n]);151152st->st_len = 0;153st->st_nbuckets = nbuckets;154st->st_string_pool_size = sizehint;155*st->st_string_pool = '\0';156ELFTC_STRING_TABLE_UPDATE_LENGTH(st, 1);157158return (st);159}160161void162elftc_string_table_destroy(Elftc_String_Table *st)163{164int n;165struct _Elftc_String_Table_Entry *s, *t;166167for (n = 0; n < st->st_nbuckets; n++)168SLIST_FOREACH_SAFE(s, &st->st_buckets[n], ste_next, t)169free(s);170free(st->st_string_pool);171free(st);172}173174Elftc_String_Table *175elftc_string_table_from_section(Elf_Scn *scn, size_t sizehint)176{177Elf_Data *d;178GElf_Shdr sh;179const char *s, *end;180Elftc_String_Table *st;181size_t len;182183/* Verify the type of the section passed in. */184if (gelf_getshdr(scn, &sh) == NULL ||185sh.sh_type != SHT_STRTAB) {186errno = EINVAL;187return (NULL);188}189190if ((d = elf_getdata(scn, NULL)) == NULL ||191d->d_size == 0) {192errno = EINVAL;193return (NULL);194}195196if ((st = elftc_string_table_create(sizehint)) == NULL)197return (NULL);198199s = d->d_buf;200201/*202* Verify that the first byte of the data buffer is '\0'.203*/204if (*s != '\0') {205errno = EINVAL;206goto fail;207}208209end = s + d->d_size;210211/*212* Skip the first '\0' and insert the strings in the buffer,213* in order.214*/215for (s += 1; s < end; s += len) {216if (elftc_string_table_insert(st, s) == 0)217goto fail;218219len = strlen(s) + 1; /* Include space for the trailing NUL. */220}221222return (st);223224fail:225if (st)226(void) elftc_string_table_destroy(st);227228return (NULL);229}230231const char *232elftc_string_table_image(Elftc_String_Table *st, size_t *size)233{234char *r, *s, *end;235struct _Elftc_String_Table_Entry *ste;236struct _Elftc_String_Table_Bucket *head;237size_t copied, offset, length, newsize;238int hashindex;239240/*241* For the common case of a string table has not seen242* a string deletion, we can just export the current243* pool.244*/245if ((st->st_len & ELFTC_STRING_TABLE_COMPACTION_FLAG) == 0) {246if (size)247*size = ELFTC_STRING_TABLE_LENGTH(st);248return (st->st_string_pool);249}250251/*252* Otherwise, compact the string table in-place.253*/254assert(*st->st_string_pool == '\0');255256newsize = 1;257end = st->st_string_pool + ELFTC_STRING_TABLE_LENGTH(st);258259for (r = s = st->st_string_pool + 1;260s < end;261s += length, r += copied) {262263copied = 0;264length = strlen(s) + 1;265266ste = elftc_string_table_find_hash_entry(st, s,267&hashindex);268head = &st->st_buckets[hashindex];269270assert(ste != NULL);271272/* Ignore deleted strings. */273if (ste->ste_idx < 0) {274SLIST_REMOVE(head, ste, _Elftc_String_Table_Entry,275ste_next);276free(ste);277continue;278}279280/* Move 'live' strings up. */281offset = newsize;282newsize += length;283copied = length;284285if (r == s) /* Nothing removed yet. */286continue;287288memmove(r, s, copied);289290/* Update the index for this entry. */291ste->ste_idx = offset;292}293294ELFTC_STRING_TABLE_CLEAR_COMPACTION_FLAG(st);295ELFTC_STRING_TABLE_UPDATE_LENGTH(st, newsize);296297if (size)298*size = newsize;299300return (st->st_string_pool);301}302303size_t304elftc_string_table_insert(Elftc_String_Table *st, const char *string)305{306struct _Elftc_String_Table_Entry *ste;307ssize_t idx;308int hashindex;309310hashindex = 0;311312ste = elftc_string_table_find_hash_entry(st, string, &hashindex);313314assert(hashindex >= 0 && hashindex < st->st_nbuckets);315316if (ste == NULL) {317if ((ste = malloc(sizeof(*ste))) == NULL)318return (0);319if ((ste->ste_idx = elftc_string_table_add_to_pool(st,320string)) == 0) {321free(ste);322return (0);323}324325SLIST_INSERT_HEAD(&st->st_buckets[hashindex], ste, ste_next);326}327328idx = ste->ste_idx;329if (idx < 0) /* Undelete. */330ste->ste_idx = idx = -idx;331332return (idx);333}334335size_t336elftc_string_table_lookup(Elftc_String_Table *st, const char *string)337{338struct _Elftc_String_Table_Entry *ste;339ssize_t idx;340int hashindex;341342ste = elftc_string_table_find_hash_entry(st, string, &hashindex);343344assert(hashindex >= 0 && hashindex < st->st_nbuckets);345346if (ste == NULL || (idx = ste->ste_idx) < 0)347return (0);348349return (idx);350}351352int353elftc_string_table_remove(Elftc_String_Table *st, const char *string)354{355struct _Elftc_String_Table_Entry *ste;356ssize_t idx;357358ste = elftc_string_table_find_hash_entry(st, string, NULL);359360if (ste == NULL || (idx = ste->ste_idx) < 0)361return (ELFTC_FAILURE);362363assert(idx > 0 && (size_t)idx < ELFTC_STRING_TABLE_LENGTH(st));364365ste->ste_idx = -idx;366367ELFTC_STRING_TABLE_SET_COMPACTION_FLAG(st);368369return (ELFTC_SUCCESS);370}371372const char *373elftc_string_table_to_string(Elftc_String_Table *st, size_t offset)374{375const char *s;376377s = st->st_string_pool + offset;378379/*380* Check for:381* - An offset value within pool bounds.382* - A non-NUL byte at the specified offset.383* - The end of the prior string at offset - 1.384*/385if (offset == 0 || offset >= ELFTC_STRING_TABLE_LENGTH(st) ||386*s == '\0' || *(s - 1) != '\0') {387errno = EINVAL;388return (NULL);389}390391return (s);392}393394395