Path: blob/main/contrib/elftoolchain/elfcopy/symbols.c
39481 views
/*-1* Copyright (c) 2007-2013 Kai Wang2* 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 disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/param.h>27#include <assert.h>28#include <err.h>29#include <fnmatch.h>30#include <stdio.h>31#include <stdlib.h>32#include <string.h>3334#include "elfcopy.h"3536ELFTC_VCSID("$Id: symbols.c 3520 2017-04-17 01:47:52Z kaiwang27 $");3738/* Backwards compatibility for systems with older ELF definitions. */39#ifndef STB_GNU_UNIQUE40#define STB_GNU_UNIQUE 1041#endif424344/* Symbol table buffer structure. */45struct symbuf {46Elf32_Sym *l32; /* 32bit local symbol */47Elf32_Sym *g32; /* 32bit global symbol */48Elf64_Sym *l64; /* 64bit local symbol */49Elf64_Sym *g64; /* 64bit global symbol */50size_t ngs, nls; /* number of each kind */51size_t gcap, lcap; /* buffer capacities. */52};5354struct sthash {55LIST_ENTRY(sthash) sh_next;56size_t sh_off;57};58typedef LIST_HEAD(,sthash) hash_head;59#define STHASHSIZE 655366061struct strimpl {62char *buf; /* string table */63size_t sz; /* entries */64size_t cap; /* buffer capacity */65hash_head hash[STHASHSIZE];66};676869/* String table buffer structure. */70struct strbuf {71struct strimpl l; /* local symbols */72struct strimpl g; /* global symbols */73};7475static int is_debug_symbol(unsigned char st_info);76static int is_global_symbol(unsigned char st_info);77static int is_local_symbol(unsigned char st_info);78static int is_local_label(const char *name);79static int is_needed_symbol(struct elfcopy *ecp, int i, GElf_Sym *s);80static int is_remove_symbol(struct elfcopy *ecp, size_t sc, int i,81GElf_Sym *s, const char *name);82static int is_weak_symbol(unsigned char st_info);83static int lookup_exact_string(hash_head *hash, const char *buf,84const char *s);85static int generate_symbols(struct elfcopy *ecp);86static void mark_reloc_symbols(struct elfcopy *ecp, size_t sc);87static void mark_section_group_symbols(struct elfcopy *ecp, size_t sc);88uint32_t str_hash(const char *s);8990/* Convenient bit vector operation macros. */91#define BIT_SET(v, n) (v[(n)>>3] |= 1U << ((n) & 7))92#define BIT_CLR(v, n) (v[(n)>>3] &= ~(1U << ((n) & 7)))93#define BIT_ISSET(v, n) (v[(n)>>3] & (1U << ((n) & 7)))9495static int96is_debug_symbol(unsigned char st_info)97{9899if (GELF_ST_TYPE(st_info) == STT_SECTION ||100GELF_ST_TYPE(st_info) == STT_FILE)101return (1);102103return (0);104}105106static int107is_global_symbol(unsigned char st_info)108{109110if (GELF_ST_BIND(st_info) == STB_GLOBAL ||111GELF_ST_BIND(st_info) == STB_GNU_UNIQUE)112return (1);113114return (0);115}116117static int118is_weak_symbol(unsigned char st_info)119{120121if (GELF_ST_BIND(st_info) == STB_WEAK)122return (1);123124return (0);125}126127static int128is_local_symbol(unsigned char st_info)129{130131if (GELF_ST_BIND(st_info) == STB_LOCAL)132return (1);133134return (0);135}136137static int138is_hidden_symbol(unsigned char st_other)139{140141if (GELF_ST_VISIBILITY(st_other) == STV_HIDDEN ||142GELF_ST_VISIBILITY(st_other) == STV_INTERNAL)143return (1);144145return (0);146}147148static int149is_local_label(const char *name)150{151152/* Compiler generated local symbols that start with .L */153if (name[0] == '.' && name[1] == 'L')154return (1);155156return (0);157}158159/*160* Symbols related to relocation are needed.161*/162static int163is_needed_symbol(struct elfcopy *ecp, int i, GElf_Sym *s)164{165166/* If symbol involves relocation, it is needed. */167if (BIT_ISSET(ecp->v_rel, i))168return (1);169170/* Symbols referred by COMDAT sections are needed. */171if (BIT_ISSET(ecp->v_grp, i))172return (1);173174/*175* For relocatable files (.o files), global and weak symbols176* are needed.177*/178if (ecp->flags & RELOCATABLE) {179if (is_global_symbol(s->st_info) || is_weak_symbol(s->st_info))180return (1);181}182183return (0);184}185186static int187is_remove_symbol(struct elfcopy *ecp, size_t sc, int i, GElf_Sym *s,188const char *name)189{190GElf_Sym sym0 = {1910, /* st_name */1920, /* st_value */1930, /* st_size */1940, /* st_info */1950, /* st_other */196SHN_UNDEF, /* st_shndx */197};198199/*200* Keep the first symbol if it is the special reserved symbol.201* XXX Should we generate one if it's missing?202*/203if (i == 0 && !memcmp(s, &sym0, sizeof(GElf_Sym)))204return (0);205206/* Remove the symbol if the section it refers to was removed. */207if (s->st_shndx != SHN_UNDEF && s->st_shndx < SHN_LORESERVE &&208ecp->secndx[s->st_shndx] == 0)209return (1);210211/* Keep the symbol if specified by command line option -K. */212if (lookup_symop_list(ecp, name, SYMOP_KEEP) != NULL)213return (0);214215if (ecp->strip == STRIP_ALL)216return (1);217218/* Mark symbols used in relocation. */219if (ecp->v_rel == NULL)220mark_reloc_symbols(ecp, sc);221222/* Mark symbols used in section groups. */223if (ecp->v_grp == NULL)224mark_section_group_symbols(ecp, sc);225226/*227* Strip the symbol if specified by command line option -N,228* unless it's used in relocation.229*/230if (lookup_symop_list(ecp, name, SYMOP_STRIP) != NULL) {231if (BIT_ISSET(ecp->v_rel, i)) {232warnx("not stripping symbol `%s' because it is named"233" in a relocation", name);234return (0);235}236return (1);237}238239if (is_needed_symbol(ecp, i, s))240return (0);241242if (ecp->strip == STRIP_UNNEEDED)243return (1);244245if ((ecp->flags & DISCARD_LOCAL) && is_local_symbol(s->st_info) &&246!is_debug_symbol(s->st_info))247return (1);248249if ((ecp->flags & DISCARD_LLABEL) && is_local_symbol(s->st_info) &&250!is_debug_symbol(s->st_info) && is_local_label(name))251return (1);252253if (ecp->strip == STRIP_DEBUG && is_debug_symbol(s->st_info))254return (1);255256return (0);257}258259/*260* Mark symbols referred by relocation entries.261*/262static void263mark_reloc_symbols(struct elfcopy *ecp, size_t sc)264{265const char *name;266Elf_Data *d;267Elf_Scn *s;268GElf_Rel r;269GElf_Rela ra;270GElf_Shdr sh;271size_t n, indx;272int elferr, i, len;273274ecp->v_rel = calloc((sc + 7) / 8, 1);275if (ecp->v_rel == NULL)276err(EXIT_FAILURE, "calloc failed");277278if (elf_getshstrndx(ecp->ein, &indx) == 0)279errx(EXIT_FAILURE, "elf_getshstrndx failed: %s",280elf_errmsg(-1));281282s = NULL;283while ((s = elf_nextscn(ecp->ein, s)) != NULL) {284if (gelf_getshdr(s, &sh) != &sh)285errx(EXIT_FAILURE, "elf_getshdr failed: %s",286elf_errmsg(-1));287288if (sh.sh_type != SHT_REL && sh.sh_type != SHT_RELA)289continue;290291/*292* Skip if this reloc section won't appear in the293* output object.294*/295if ((name = elf_strptr(ecp->ein, indx, sh.sh_name)) == NULL)296errx(EXIT_FAILURE, "elf_strptr failed: %s",297elf_errmsg(-1));298if (is_remove_section(ecp, name) ||299is_remove_reloc_sec(ecp, sh.sh_info))300continue;301302/* Skip if it's not for .symtab */303if (sh.sh_link != elf_ndxscn(ecp->symtab->is))304continue;305306d = NULL;307n = 0;308while (n < sh.sh_size && (d = elf_getdata(s, d)) != NULL) {309len = d->d_size / sh.sh_entsize;310for (i = 0; i < len; i++) {311if (sh.sh_type == SHT_REL) {312if (gelf_getrel(d, i, &r) != &r)313errx(EXIT_FAILURE,314"elf_getrel failed: %s",315elf_errmsg(-1));316n = GELF_R_SYM(r.r_info);317} else {318if (gelf_getrela(d, i, &ra) != &ra)319errx(EXIT_FAILURE,320"elf_getrela failed: %s",321elf_errmsg(-1));322n = GELF_R_SYM(ra.r_info);323}324if (n > 0 && n < sc)325BIT_SET(ecp->v_rel, n);326else if (n != 0)327warnx("invalid symbox index");328}329}330elferr = elf_errno();331if (elferr != 0)332errx(EXIT_FAILURE, "elf_getdata failed: %s",333elf_errmsg(elferr));334}335elferr = elf_errno();336if (elferr != 0)337errx(EXIT_FAILURE, "elf_nextscn failed: %s",338elf_errmsg(elferr));339}340341static void342mark_section_group_symbols(struct elfcopy *ecp, size_t sc)343{344const char *name;345Elf_Scn *s;346GElf_Shdr sh;347size_t indx;348int elferr;349350ecp->v_grp = calloc((sc + 7) / 8, 1);351if (ecp->v_grp == NULL)352err(EXIT_FAILURE, "calloc failed");353354if (elf_getshstrndx(ecp->ein, &indx) == 0)355errx(EXIT_FAILURE, "elf_getshstrndx failed: %s",356elf_errmsg(-1));357358s = NULL;359while ((s = elf_nextscn(ecp->ein, s)) != NULL) {360if (gelf_getshdr(s, &sh) != &sh)361errx(EXIT_FAILURE, "elf_getshdr failed: %s",362elf_errmsg(-1));363364if (sh.sh_type != SHT_GROUP)365continue;366367if ((name = elf_strptr(ecp->ein, indx, sh.sh_name)) == NULL)368errx(EXIT_FAILURE, "elf_strptr failed: %s",369elf_errmsg(-1));370if (is_remove_section(ecp, name))371continue;372373if (sh.sh_info > 0 && sh.sh_info < sc)374BIT_SET(ecp->v_grp, sh.sh_info);375else if (sh.sh_info != 0)376warnx("invalid symbox index");377}378elferr = elf_errno();379if (elferr != 0)380errx(EXIT_FAILURE, "elf_nextscn failed: %s",381elf_errmsg(elferr));382}383384static int385generate_symbols(struct elfcopy *ecp)386{387struct section *s;388struct symop *sp;389struct symbuf *sy_buf;390struct strbuf *st_buf;391const char *name;392char *newname;393unsigned char *gsym;394GElf_Shdr ish;395GElf_Sym sym;396Elf_Data* id;397Elf_Scn *is;398size_t ishstrndx, namelen, ndx, sc, symndx;399int ec, elferr, i;400401if (elf_getshstrndx(ecp->ein, &ishstrndx) == 0)402errx(EXIT_FAILURE, "elf_getshstrndx failed: %s",403elf_errmsg(-1));404if ((ec = gelf_getclass(ecp->eout)) == ELFCLASSNONE)405errx(EXIT_FAILURE, "gelf_getclass failed: %s",406elf_errmsg(-1));407408/* Create buffers for .symtab and .strtab. */409if ((sy_buf = calloc(1, sizeof(*sy_buf))) == NULL)410err(EXIT_FAILURE, "calloc failed");411if ((st_buf = calloc(1, sizeof(*st_buf))) == NULL)412err(EXIT_FAILURE, "calloc failed");413sy_buf->gcap = sy_buf->lcap = 64;414st_buf->g.cap = 256;415st_buf->l.cap = 64;416st_buf->l.sz = 1; /* '\0' at start. */417st_buf->g.sz = 0;418419ecp->symtab->sz = 0;420ecp->strtab->sz = 0;421ecp->symtab->buf = sy_buf;422ecp->strtab->buf = st_buf;423424gsym = NULL;425426/*427* Create bit vector v_secsym, which is used to mark sections428* that already have corresponding STT_SECTION symbols.429*/430ecp->v_secsym = calloc((ecp->nos + 7) / 8, 1);431if (ecp->v_secsym == NULL)432err(EXIT_FAILURE, "calloc failed");433434/* Locate .strtab of input object. */435symndx = 0;436name = NULL;437is = NULL;438while ((is = elf_nextscn(ecp->ein, is)) != NULL) {439if (gelf_getshdr(is, &ish) != &ish)440errx(EXIT_FAILURE, "elf_getshdr failed: %s",441elf_errmsg(-1));442if ((name = elf_strptr(ecp->ein, ishstrndx, ish.sh_name)) ==443NULL)444errx(EXIT_FAILURE, "elf_strptr failed: %s",445elf_errmsg(-1));446if (strcmp(name, ".strtab") == 0) {447symndx = elf_ndxscn(is);448break;449}450}451elferr = elf_errno();452if (elferr != 0)453errx(EXIT_FAILURE, "elf_nextscn failed: %s",454elf_errmsg(elferr));455456/* Symbol table should exist if this function is called. */457if (symndx == 0) {458warnx("can't find .strtab section");459goto clean;460}461462/* Locate .symtab of input object. */463is = NULL;464while ((is = elf_nextscn(ecp->ein, is)) != NULL) {465if (gelf_getshdr(is, &ish) != &ish)466errx(EXIT_FAILURE, "elf_getshdr failed: %s",467elf_errmsg(-1));468if ((name = elf_strptr(ecp->ein, ishstrndx, ish.sh_name)) ==469NULL)470errx(EXIT_FAILURE, "elf_strptr failed: %s",471elf_errmsg(-1));472if (strcmp(name, ".symtab") == 0)473break;474}475elferr = elf_errno();476if (elferr != 0)477errx(EXIT_FAILURE, "elf_nextscn failed: %s",478elf_errmsg(elferr));479if (is == NULL)480errx(EXIT_FAILURE, "can't find .strtab section");481482/*483* Create bit vector gsym to mark global symbols, and symndx484* to keep track of symbol index changes from input object to485* output object, it is used by update_reloc() later to update486* relocation information.487*/488sc = ish.sh_size / ish.sh_entsize;489if (sc > 0) {490ecp->symndx = calloc(sc, sizeof(*ecp->symndx));491if (ecp->symndx == NULL)492err(EXIT_FAILURE, "calloc failed");493gsym = calloc((sc + 7) / 8, sizeof(*gsym));494if (gsym == NULL)495err(EXIT_FAILURE, "calloc failed");496if ((id = elf_getdata(is, NULL)) == NULL) {497elferr = elf_errno();498if (elferr != 0)499errx(EXIT_FAILURE, "elf_getdata failed: %s",500elf_errmsg(elferr));501goto clean;502}503} else504return (0);505506/* Copy/Filter each symbol. */507for (i = 0; (size_t)i < sc; i++) {508if (gelf_getsym(id, i, &sym) != &sym)509errx(EXIT_FAILURE, "gelf_getsym failed: %s",510elf_errmsg(-1));511if ((name = elf_strptr(ecp->ein, symndx, sym.st_name)) == NULL)512errx(EXIT_FAILURE, "elf_strptr failed: %s",513elf_errmsg(-1));514515/* Symbol filtering. */516if (is_remove_symbol(ecp, sc, i, &sym, name) != 0)517continue;518519/* Check if we need to change the binding of this symbol. */520if (is_global_symbol(sym.st_info) ||521is_weak_symbol(sym.st_info)) {522/*523* XXX Binutils objcopy does not weaken certain524* symbols.525*/526if (ecp->flags & WEAKEN_ALL ||527lookup_symop_list(ecp, name, SYMOP_WEAKEN) != NULL)528sym.st_info = GELF_ST_INFO(STB_WEAK,529GELF_ST_TYPE(sym.st_info));530/* Do not localize undefined symbols. */531if (sym.st_shndx != SHN_UNDEF &&532lookup_symop_list(ecp, name, SYMOP_LOCALIZE) !=533NULL)534sym.st_info = GELF_ST_INFO(STB_LOCAL,535GELF_ST_TYPE(sym.st_info));536if (ecp->flags & KEEP_GLOBAL &&537sym.st_shndx != SHN_UNDEF &&538lookup_symop_list(ecp, name, SYMOP_KEEPG) == NULL)539sym.st_info = GELF_ST_INFO(STB_LOCAL,540GELF_ST_TYPE(sym.st_info));541if (ecp->flags & LOCALIZE_HIDDEN &&542sym.st_shndx != SHN_UNDEF &&543is_hidden_symbol(sym.st_other))544sym.st_info = GELF_ST_INFO(STB_LOCAL,545GELF_ST_TYPE(sym.st_info));546} else {547/* STB_LOCAL binding. */548if (lookup_symop_list(ecp, name, SYMOP_GLOBALIZE) !=549NULL)550sym.st_info = GELF_ST_INFO(STB_GLOBAL,551GELF_ST_TYPE(sym.st_info));552/* XXX We should globalize weak symbol? */553}554555/* Check if we need to rename this symbol. */556if ((sp = lookup_symop_list(ecp, name, SYMOP_REDEF)) != NULL)557name = sp->newname;558559/* Check if we need to prefix the symbols. */560newname = NULL;561if (ecp->prefix_sym != NULL && name != NULL && *name != '\0') {562namelen = strlen(name) + strlen(ecp->prefix_sym) + 1;563if ((newname = malloc(namelen)) == NULL)564err(EXIT_FAILURE, "malloc failed");565snprintf(newname, namelen, "%s%s", ecp->prefix_sym,566name);567name = newname;568}569570/* Copy symbol, mark global/weak symbol and add to index map. */571if (is_global_symbol(sym.st_info) ||572is_weak_symbol(sym.st_info)) {573BIT_SET(gsym, i);574ecp->symndx[i] = sy_buf->ngs;575} else576ecp->symndx[i] = sy_buf->nls;577add_to_symtab(ecp, name, sym.st_value, sym.st_size,578sym.st_shndx, sym.st_info, sym.st_other, 0);579580if (newname != NULL)581free(newname);582583/*584* If the symbol is a STT_SECTION symbol, mark the section585* it points to.586*/587if (GELF_ST_TYPE(sym.st_info) == STT_SECTION &&588sym.st_shndx < SHN_LORESERVE) {589assert(ecp->secndx[sym.st_shndx] < (uint64_t)ecp->nos);590BIT_SET(ecp->v_secsym, ecp->secndx[sym.st_shndx]);591}592}593594/*595* Give up if there is no real symbols inside the table.596* XXX The logic here needs to be improved. We need to597* check if that only local symbol is the reserved symbol.598*/599if (sy_buf->nls <= 1 && sy_buf->ngs == 0)600goto clean;601602/*603* Create STT_SECTION symbols for sections that do not already604* got one. However, we do not create STT_SECTION symbol for605* .symtab, .strtab, .shstrtab and reloc sec of relocatables.606*/607TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {608if (s->pseudo)609continue;610if (strcmp(s->name, ".symtab") == 0 ||611strcmp(s->name, ".strtab") == 0 ||612strcmp(s->name, ".shstrtab") == 0)613continue;614if ((ecp->flags & RELOCATABLE) != 0 &&615((s->type == SHT_REL) || (s->type == SHT_RELA)))616continue;617618if ((ndx = elf_ndxscn(s->os)) == SHN_UNDEF)619errx(EXIT_FAILURE, "elf_ndxscn failed: %s",620elf_errmsg(-1));621622if (!BIT_ISSET(ecp->v_secsym, ndx)) {623sym.st_name = 0;624sym.st_value = s->vma;625sym.st_size = 0;626sym.st_info = GELF_ST_INFO(STB_LOCAL, STT_SECTION);627sym.st_other = STV_DEFAULT;628/*629* Don't let add_to_symtab() touch sym.st_shndx.630* In this case, we know the index already.631*/632add_to_symtab(ecp, NULL, sym.st_value, sym.st_size,633ndx, sym.st_info, sym.st_other, 1);634}635}636637/*638* Update st_name and index map for global/weak symbols. Note that639* global/weak symbols are put after local symbols.640*/641if (gsym != NULL) {642for(i = 0; (size_t) i < sc; i++) {643if (!BIT_ISSET(gsym, i))644continue;645646/* Update st_name. */647if (ec == ELFCLASS32)648sy_buf->g32[ecp->symndx[i]].st_name +=649st_buf->l.sz;650else651sy_buf->g64[ecp->symndx[i]].st_name +=652st_buf->l.sz;653654/* Update index map. */655ecp->symndx[i] += sy_buf->nls;656}657free(gsym);658}659660return (1);661662clean:663free(gsym);664free_symtab(ecp);665666return (0);667}668669void670create_symtab(struct elfcopy *ecp)671{672struct section *s, *sy, *st;673size_t maxndx, ndx;674675sy = ecp->symtab;676st = ecp->strtab;677678assert(sy != NULL && st != NULL);679680/*681* Set section index map for .symtab and .strtab. We need to set682* these map because otherwise symbols which refer to .symtab and683* .strtab will be removed by symbol filtering unconditionally.684* And we have to figure out scn index this way (instead of calling685* elf_ndxscn) because we can not create Elf_Scn before we're certain686* that .symtab and .strtab will exist in the output object.687*/688maxndx = 0;689TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {690if (s->os == NULL)691continue;692if ((ndx = elf_ndxscn(s->os)) == SHN_UNDEF)693errx(EXIT_FAILURE, "elf_ndxscn failed: %s",694elf_errmsg(-1));695if (ndx > maxndx)696maxndx = ndx;697}698ecp->secndx[elf_ndxscn(sy->is)] = maxndx + 1;699ecp->secndx[elf_ndxscn(st->is)] = maxndx + 2;700701/*702* Generate symbols for output object if SYMTAB_INTACT is not set.703* If there is no symbol in the input object or all the symbols are704* stripped, then free all the resouces allotted for symbol table,705* and clear SYMTAB_EXIST flag.706*/707if (((ecp->flags & SYMTAB_INTACT) == 0) && !generate_symbols(ecp)) {708TAILQ_REMOVE(&ecp->v_sec, ecp->symtab, sec_list);709TAILQ_REMOVE(&ecp->v_sec, ecp->strtab, sec_list);710free(ecp->symtab->buf);711free(ecp->symtab);712free(ecp->strtab->buf);713free(ecp->strtab);714ecp->symtab = NULL;715ecp->strtab = NULL;716ecp->flags &= ~SYMTAB_EXIST;717return;718}719720/* Create output Elf_Scn for .symtab and .strtab. */721if ((sy->os = elf_newscn(ecp->eout)) == NULL ||722(st->os = elf_newscn(ecp->eout)) == NULL)723errx(EXIT_FAILURE, "elf_newscn failed: %s",724elf_errmsg(-1));725/* Update secndx anyway. */726ecp->secndx[elf_ndxscn(sy->is)] = elf_ndxscn(sy->os);727ecp->secndx[elf_ndxscn(st->is)] = elf_ndxscn(st->os);728729/*730* Copy .symtab and .strtab section headers from input to output731* object to start with, these will be overridden later if need.732*/733copy_shdr(ecp, sy, ".symtab", 1, 0);734copy_shdr(ecp, st, ".strtab", 1, 0);735736/* Copy verbatim if symbol table is intact. */737if (ecp->flags & SYMTAB_INTACT) {738copy_data(sy);739copy_data(st);740return;741}742743create_symtab_data(ecp);744}745746void747free_symtab(struct elfcopy *ecp)748{749struct symbuf *sy_buf;750struct strbuf *st_buf;751struct sthash *sh, *shtmp;752int i;753754if (ecp->symtab != NULL && ecp->symtab->buf != NULL) {755sy_buf = ecp->symtab->buf;756if (sy_buf->l32 != NULL)757free(sy_buf->l32);758if (sy_buf->g32 != NULL)759free(sy_buf->g32);760if (sy_buf->l64 != NULL)761free(sy_buf->l64);762if (sy_buf->g64 != NULL)763free(sy_buf->g64);764}765766if (ecp->strtab != NULL && ecp->strtab->buf != NULL) {767st_buf = ecp->strtab->buf;768if (st_buf->l.buf != NULL)769free(st_buf->l.buf);770if (st_buf->g.buf != NULL)771free(st_buf->g.buf);772for (i = 0; i < STHASHSIZE; i++) {773LIST_FOREACH_SAFE(sh, &st_buf->l.hash[i], sh_next,774shtmp) {775LIST_REMOVE(sh, sh_next);776free(sh);777}778LIST_FOREACH_SAFE(sh, &st_buf->g.hash[i], sh_next,779shtmp) {780LIST_REMOVE(sh, sh_next);781free(sh);782}783}784}785786if (ecp->symndx != NULL) {787free(ecp->symndx);788ecp->symndx = NULL;789}790if (ecp->v_rel != NULL) {791free(ecp->v_rel);792ecp->v_rel = NULL;793}794if (ecp->v_grp != NULL) {795free(ecp->v_grp);796ecp->v_grp = NULL;797}798if (ecp->v_secsym != NULL) {799free(ecp->v_secsym);800ecp->v_secsym = NULL;801}802}803804void805create_external_symtab(struct elfcopy *ecp)806{807struct section *s;808struct symbuf *sy_buf;809struct strbuf *st_buf;810GElf_Shdr sh;811size_t ndx;812813if (ecp->oec == ELFCLASS32)814ecp->symtab = create_external_section(ecp, ".symtab", NULL,815NULL, 0, 0, SHT_SYMTAB, ELF_T_SYM, 0, 4, 0, 0);816else817ecp->symtab = create_external_section(ecp, ".symtab", NULL,818NULL, 0, 0, SHT_SYMTAB, ELF_T_SYM, 0, 8, 0, 0);819820ecp->strtab = create_external_section(ecp, ".strtab", NULL, NULL, 0, 0,821SHT_STRTAB, ELF_T_BYTE, 0, 1, 0, 0);822823/* Let sh_link field of .symtab section point to .strtab section. */824if (gelf_getshdr(ecp->symtab->os, &sh) == NULL)825errx(EXIT_FAILURE, "gelf_getshdr() failed: %s",826elf_errmsg(-1));827sh.sh_link = elf_ndxscn(ecp->strtab->os);828if (!gelf_update_shdr(ecp->symtab->os, &sh))829errx(EXIT_FAILURE, "gelf_update_shdr() failed: %s",830elf_errmsg(-1));831832/* Create buffers for .symtab and .strtab. */833if ((sy_buf = calloc(1, sizeof(*sy_buf))) == NULL)834err(EXIT_FAILURE, "calloc failed");835if ((st_buf = calloc(1, sizeof(*st_buf))) == NULL)836err(EXIT_FAILURE, "calloc failed");837sy_buf->gcap = sy_buf->lcap = 64;838st_buf->g.cap = 256;839st_buf->l.cap = 64;840st_buf->l.sz = 1; /* '\0' at start. */841st_buf->g.sz = 0;842843ecp->symtab->sz = 0;844ecp->strtab->sz = 0;845ecp->symtab->buf = sy_buf;846ecp->strtab->buf = st_buf;847848/* Always create the special symbol at the symtab beginning. */849add_to_symtab(ecp, NULL, 0, 0, SHN_UNDEF,850ELF32_ST_INFO(STB_LOCAL, STT_NOTYPE), 0, 1);851852/* Create STT_SECTION symbols. */853TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {854if (s->pseudo)855continue;856if (strcmp(s->name, ".symtab") == 0 ||857strcmp(s->name, ".strtab") == 0 ||858strcmp(s->name, ".shstrtab") == 0)859continue;860(void) elf_errno();861if ((ndx = elf_ndxscn(s->os)) == SHN_UNDEF) {862warnx("elf_ndxscn failed: %s",863elf_errmsg(-1));864continue;865}866add_to_symtab(ecp, NULL, 0, 0, ndx,867GELF_ST_INFO(STB_LOCAL, STT_SECTION), 0, 1);868}869}870871void872add_to_symtab(struct elfcopy *ecp, const char *name, uint64_t st_value,873uint64_t st_size, uint16_t st_shndx, unsigned char st_info,874unsigned char st_other, int ndx_known)875{876struct symbuf *sy_buf;877struct strbuf *st_buf;878struct sthash *sh;879uint32_t hash;880int pos;881882/*883* Convenient macro for copying global/local 32/64 bit symbols884* from input object to the buffer created for output object.885* It handles buffer growing, st_name calculating and st_shndx886* updating for symbols with non-special section index.887*/888#define _ST_NAME_EMPTY_l 0889#define _ST_NAME_EMPTY_g -1890#define _ADDSYM(B, SZ) do { \891if (sy_buf->B##SZ == NULL) { \892sy_buf->B##SZ = malloc(sy_buf->B##cap * \893sizeof(Elf##SZ##_Sym)); \894if (sy_buf->B##SZ == NULL) \895err(EXIT_FAILURE, "malloc failed"); \896} else if (sy_buf->n##B##s >= sy_buf->B##cap) { \897sy_buf->B##cap *= 2; \898sy_buf->B##SZ = realloc(sy_buf->B##SZ, sy_buf->B##cap * \899sizeof(Elf##SZ##_Sym)); \900if (sy_buf->B##SZ == NULL) \901err(EXIT_FAILURE, "realloc failed"); \902} \903sy_buf->B##SZ[sy_buf->n##B##s].st_info = st_info; \904sy_buf->B##SZ[sy_buf->n##B##s].st_other = st_other; \905sy_buf->B##SZ[sy_buf->n##B##s].st_value = st_value; \906sy_buf->B##SZ[sy_buf->n##B##s].st_size = st_size; \907if (ndx_known) \908sy_buf->B##SZ[sy_buf->n##B##s].st_shndx = st_shndx; \909else if (st_shndx == SHN_UNDEF || st_shndx >= SHN_LORESERVE) \910sy_buf->B##SZ[sy_buf->n##B##s].st_shndx = st_shndx; \911else \912sy_buf->B##SZ[sy_buf->n##B##s].st_shndx = \913ecp->secndx[st_shndx]; \914if (st_buf->B.buf == NULL) { \915st_buf->B.buf = calloc(st_buf->B.cap, \916sizeof(*st_buf->B.buf)); \917if (st_buf->B.buf == NULL) \918err(EXIT_FAILURE, "malloc failed"); \919} \920if (name != NULL && *name != '\0') { \921pos = lookup_exact_string(st_buf->B.hash, st_buf->B.buf,\922name); \923if (pos != -1) \924sy_buf->B##SZ[sy_buf->n##B##s].st_name = pos; \925else { \926sy_buf->B##SZ[sy_buf->n##B##s].st_name = \927st_buf->B.sz; \928while (st_buf->B.sz + strlen(name) >= \929st_buf->B.cap - 1) { \930st_buf->B.cap *= 2; \931st_buf->B.buf = realloc(st_buf->B.buf, \932st_buf->B.cap); \933if (st_buf->B.buf == NULL) \934err(EXIT_FAILURE, \935"realloc failed"); \936} \937if ((sh = malloc(sizeof(*sh))) == NULL) \938err(EXIT_FAILURE, "malloc failed"); \939sh->sh_off = st_buf->B.sz; \940hash = str_hash(name); \941LIST_INSERT_HEAD(&st_buf->B.hash[hash], sh, \942sh_next); \943strncpy(&st_buf->B.buf[st_buf->B.sz], name, \944strlen(name)); \945st_buf->B.buf[st_buf->B.sz + strlen(name)] = '\0'; \946st_buf->B.sz += strlen(name) + 1; \947} \948} else \949sy_buf->B##SZ[sy_buf->n##B##s].st_name = \950(Elf##SZ##_Word)_ST_NAME_EMPTY_##B; \951sy_buf->n##B##s++; \952} while (0)953954sy_buf = ecp->symtab->buf;955st_buf = ecp->strtab->buf;956957if (ecp->oec == ELFCLASS32) {958if (is_local_symbol(st_info))959_ADDSYM(l, 32);960else961_ADDSYM(g, 32);962} else {963if (is_local_symbol(st_info))964_ADDSYM(l, 64);965else966_ADDSYM(g, 64);967}968969/* Update section size. */970ecp->symtab->sz = (sy_buf->nls + sy_buf->ngs) *971(ecp->oec == ELFCLASS32 ? sizeof(Elf32_Sym) : sizeof(Elf64_Sym));972ecp->strtab->sz = st_buf->l.sz + st_buf->g.sz;973974#undef _ADDSYM975#undef _ST_NAME_EMPTY_l976#undef _ST_NAME_EMPTY_g977}978979void980finalize_external_symtab(struct elfcopy *ecp)981{982struct symbuf *sy_buf;983struct strbuf *st_buf;984int i;985986/*987* Update st_name for global/weak symbols. (global/weak symbols988* are put after local symbols)989*/990sy_buf = ecp->symtab->buf;991st_buf = ecp->strtab->buf;992for (i = 0; (size_t) i < sy_buf->ngs; i++) {993if (ecp->oec == ELFCLASS32) {994if (sy_buf->g32[i].st_name == (Elf32_Word)-1)995sy_buf->g32[i].st_name = 0;996else997sy_buf->g32[i].st_name += st_buf->l.sz;998} else {999if (sy_buf->g64[i].st_name == (Elf64_Word)-1)1000sy_buf->g64[i].st_name = 0;1001else1002sy_buf->g64[i].st_name += st_buf->l.sz;1003}1004}1005}10061007void1008create_symtab_data(struct elfcopy *ecp)1009{1010struct section *sy, *st;1011struct symbuf *sy_buf;1012struct strbuf *st_buf;1013Elf_Data *gsydata, *lsydata, *gstdata, *lstdata;1014GElf_Shdr shy, sht;10151016sy = ecp->symtab;1017st = ecp->strtab;10181019if (gelf_getshdr(sy->os, ­) == NULL)1020errx(EXIT_FAILURE, "gelf_getshdr() failed: %s",1021elf_errmsg(-1));1022if (gelf_getshdr(st->os, &sht) == NULL)1023errx(EXIT_FAILURE, "gelf_getshdr() failed: %s",1024elf_errmsg(-1));10251026/*1027* Create two Elf_Data for .symtab section of output object, one1028* for local symbols and another for global symbols. Note that1029* local symbols appear first in the .symtab.1030*/1031sy_buf = sy->buf;1032if (sy_buf->nls > 0) {1033if ((lsydata = elf_newdata(sy->os)) == NULL)1034errx(EXIT_FAILURE, "elf_newdata() failed: %s.",1035elf_errmsg(-1));1036if (ecp->oec == ELFCLASS32) {1037lsydata->d_align = 4;1038lsydata->d_off = 0;1039lsydata->d_buf = sy_buf->l32;1040lsydata->d_size = sy_buf->nls *1041sizeof(Elf32_Sym);1042lsydata->d_type = ELF_T_SYM;1043lsydata->d_version = EV_CURRENT;1044} else {1045lsydata->d_align = 8;1046lsydata->d_off = 0;1047lsydata->d_buf = sy_buf->l64;1048lsydata->d_size = sy_buf->nls *1049sizeof(Elf64_Sym);1050lsydata->d_type = ELF_T_SYM;1051lsydata->d_version = EV_CURRENT;1052}1053}1054if (sy_buf->ngs > 0) {1055if ((gsydata = elf_newdata(sy->os)) == NULL)1056errx(EXIT_FAILURE, "elf_newdata() failed: %s.",1057elf_errmsg(-1));1058if (ecp->oec == ELFCLASS32) {1059gsydata->d_align = 4;1060gsydata->d_off = sy_buf->nls *1061sizeof(Elf32_Sym);1062gsydata->d_buf = sy_buf->g32;1063gsydata->d_size = sy_buf->ngs *1064sizeof(Elf32_Sym);1065gsydata->d_type = ELF_T_SYM;1066gsydata->d_version = EV_CURRENT;1067} else {1068gsydata->d_align = 8;1069gsydata->d_off = sy_buf->nls *1070sizeof(Elf64_Sym);1071gsydata->d_buf = sy_buf->g64;1072gsydata->d_size = sy_buf->ngs *1073sizeof(Elf64_Sym);1074gsydata->d_type = ELF_T_SYM;1075gsydata->d_version = EV_CURRENT;1076}1077}10781079/*1080* Create two Elf_Data for .strtab, one for local symbol name1081* and another for globals. Same as .symtab, local symbol names1082* appear first.1083*/1084st_buf = st->buf;1085if ((lstdata = elf_newdata(st->os)) == NULL)1086errx(EXIT_FAILURE, "elf_newdata() failed: %s.",1087elf_errmsg(-1));1088lstdata->d_align = 1;1089lstdata->d_off = 0;1090lstdata->d_buf = st_buf->l.buf;1091lstdata->d_size = st_buf->l.sz;1092lstdata->d_type = ELF_T_BYTE;1093lstdata->d_version = EV_CURRENT;10941095if (st_buf->g.sz > 0) {1096if ((gstdata = elf_newdata(st->os)) == NULL)1097errx(EXIT_FAILURE, "elf_newdata() failed: %s.",1098elf_errmsg(-1));1099gstdata->d_align = 1;1100gstdata->d_off = lstdata->d_size;1101gstdata->d_buf = st_buf->g.buf;1102gstdata->d_size = st_buf->g.sz;1103gstdata->d_type = ELF_T_BYTE;1104gstdata->d_version = EV_CURRENT;1105}11061107shy.sh_addr = 0;1108shy.sh_addralign = (ecp->oec == ELFCLASS32 ? 4 : 8);1109shy.sh_size = sy->sz;1110shy.sh_type = SHT_SYMTAB;1111shy.sh_flags = 0;1112shy.sh_entsize = gelf_fsize(ecp->eout, ELF_T_SYM, 1,1113EV_CURRENT);1114/*1115* According to SYSV abi, here sh_info is one greater than1116* the symbol table index of the last local symbol(binding1117* STB_LOCAL).1118*/1119shy.sh_info = sy_buf->nls;11201121sht.sh_addr = 0;1122sht.sh_addralign = 1;1123sht.sh_size = st->sz;1124sht.sh_type = SHT_STRTAB;1125sht.sh_flags = 0;1126sht.sh_entsize = 0;1127sht.sh_info = 0;1128sht.sh_link = 0;11291130if (!gelf_update_shdr(sy->os, ­))1131errx(EXIT_FAILURE, "gelf_update_shdr() failed: %s",1132elf_errmsg(-1));1133if (!gelf_update_shdr(st->os, &sht))1134errx(EXIT_FAILURE, "gelf_update_shdr() failed: %s",1135elf_errmsg(-1));1136}11371138void1139add_to_symop_list(struct elfcopy *ecp, const char *name, const char *newname,1140unsigned int op)1141{1142struct symop *s;11431144assert (name != NULL);1145STAILQ_FOREACH(s, &ecp->v_symop, symop_list)1146if (!strcmp(name, s->name))1147goto found;11481149if ((s = calloc(1, sizeof(*s))) == NULL)1150errx(EXIT_FAILURE, "not enough memory");1151STAILQ_INSERT_TAIL(&ecp->v_symop, s, symop_list);1152s->name = name;1153found:1154if (op == SYMOP_REDEF)1155s->newname = newname;1156s->op |= op;1157}11581159struct symop *1160lookup_symop_list(struct elfcopy *ecp, const char *name, unsigned int op)1161{1162struct symop *s, *ret;1163const char *pattern;11641165STAILQ_FOREACH(s, &ecp->v_symop, symop_list) {1166if ((s->op & op) == 0)1167continue;1168if (name == NULL || !strcmp(name, s->name))1169return (s);1170if ((ecp->flags & WILDCARD) == 0)1171continue;11721173/* Handle wildcards. */1174pattern = s->name;1175if (pattern[0] == '!') {1176/* Negative match. */1177pattern++;1178ret = NULL;1179} else {1180/* Regular wildcard match. */1181ret = s;1182}1183if (!fnmatch(pattern, name, 0))1184return (ret);1185}11861187return (NULL);1188}11891190static int1191lookup_exact_string(hash_head *buckets, const char *buf, const char *s)1192{1193struct sthash *sh;1194uint32_t hash;11951196hash = str_hash(s);1197LIST_FOREACH(sh, &buckets[hash], sh_next)1198if (strcmp(buf + sh->sh_off, s) == 0)1199return sh->sh_off;1200return (-1);1201}12021203uint32_t1204str_hash(const char *s)1205{1206uint32_t hash;12071208for (hash = 2166136261UL; *s; s++)1209hash = (hash ^ *s) * 16777619;12101211return (hash & (STHASHSIZE - 1));1212}121312141215