1 /* Extract symbol list from binary. 2 Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. 3 Written by Ulrich Drepper <drepper (at) redhat.com>, 1998. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation, version 2. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program; if not, write to the Free Software Foundation, 16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 17 18 #ifdef HAVE_CONFIG_H 19 # include <config.h> 20 #endif 21 22 #include <fcntl.h> 23 #include <gelf.h> 24 #include <libelf.h> 25 #include <nlist.h> 26 #include <unistd.h> 27 28 #include "libelfP.h" 29 30 31 struct hashentry 32 { 33 const char *str; 34 GElf_Sym sym; 35 }; 36 #define TYPE struct hashentry 37 /* XXX Use a better hash function some day. */ 38 #define HASHFCT(str, len) INTUSE(elf_hash) (str) 39 #define COMPARE(p1, p2) strcmp ((p1)->str, (p2)->str) 40 #define CLASS static 41 #define PREFIX nlist_ 42 #define xcalloc(n, m) calloc (n, m) 43 #define next_prime(s) __libelf_next_prime (s) 44 #include <fixedsizehash.h> 45 46 47 int 48 nlist (const char *filename, struct nlist *nl) 49 { 50 int fd; 51 Elf *elf; 52 Elf_Scn *scn = NULL; 53 Elf_Scn *symscn = NULL; 54 GElf_Shdr shdr_mem; 55 GElf_Shdr *shdr = NULL; 56 Elf_Data *data; 57 struct nlist_fshash *table; 58 size_t nsyms; 59 size_t cnt; 60 61 /* Open the file. */ 62 fd = open (filename, O_RDONLY); 63 if (fd == -1) 64 { 65 __libelf_seterrno (ELF_E_NOFILE); 66 goto fail; 67 } 68 69 /* For compatibility reasons (`nlist' existed before ELF and libelf) 70 we don't expect the caller to set the ELF version. Do this here 71 if it hasn't happened yet. */ 72 if (__libelf_version_initialized == 0) 73 INTUSE(elf_version) (EV_CURRENT); 74 75 /* Now get an ELF descriptor. */ 76 elf = INTUSE(elf_begin) (fd, ELF_C_READ_MMAP, NULL); 77 if (elf == NULL) 78 goto fail; 79 80 /* Find a symbol table. We prefer the real symbol table but if it 81 does not exist use the dynamic symbol table. */ 82 while ((scn = INTUSE(elf_nextscn) (elf, scn)) != NULL) 83 { 84 shdr = INTUSE(gelf_getshdr) (scn, &shdr_mem); 85 if (shdr == NULL) 86 goto fail_close; 87 88 /* That is what we are looking for. */ 89 if (shdr->sh_type == SHT_SYMTAB) 90 { 91 symscn = scn; 92 break; 93 } 94 95 /* Better than nothing. Remember this section. */ 96 if (shdr->sh_type == SHT_DYNSYM) 97 symscn = scn; 98 } 99 100 if (symscn == NULL) 101 /* We haven't found anything. Fail. */ 102 goto fail_close; 103 104 /* Re-get the section header in case we found only the dynamic symbol 105 table. */ 106 if (scn == NULL) 107 shdr = INTUSE(gelf_getshdr) (symscn, &shdr_mem); 108 /* SHDR->SH_LINK now contains the index of the string section. */ 109 110 /* Get the data for the symbol section. */ 111 data = INTUSE(elf_getdata) (symscn, NULL); 112 if (data == NULL) 113 goto fail_close; 114 115 /* How many symbols are there? */ 116 nsyms = (shdr->sh_size 117 / INTUSE(gelf_fsize) (elf, ELF_T_SYM, 1, data->d_version)); 118 119 /* Create the hash table. */ 120 table = nlist_fshash_init (nsyms); 121 if (table == NULL) 122 { 123 __libelf_seterrno (ELF_E_NOMEM); 124 goto fail_close; 125 } 126 127 /* Iterate over all the symbols in the section. */ 128 for (cnt = 0; cnt < nsyms; ++cnt) 129 { 130 struct hashentry mem; 131 GElf_Sym *sym; 132 133 /* Get the symbol. */ 134 sym = INTUSE(gelf_getsym) (data, cnt, &mem.sym); 135 if (sym == NULL) 136 goto fail_dealloc; 137 138 /* Get the name of the symbol. */ 139 mem.str = INTUSE(elf_strptr) (elf, shdr->sh_link, sym->st_name); 140 if (mem.str == NULL) 141 goto fail_dealloc; 142 143 /* Don't allow zero-length strings. */ 144 if (mem.str[0] == '\0') 145 continue; 146 147 /* And add it to the hash table. Note that we are using the 148 overwrite version. This will ensure that 149 a) global symbols are preferred over local symbols since 150 they are all located at the end 151 b) if there are multiple local symbols with the same name 152 the last one is used. 153 */ 154 (void) nlist_fshash_overwrite (table, mem.str, 0, &mem); 155 } 156 157 /* Now it is time to look for the symbols the user asked for. 158 XXX What is a `null name/null string'? This is what the 159 standard says terminates the list. Is it a null pointer 160 or a zero-length string? We test for both... */ 161 while (nl->n_name != NULL && nl->n_name[0] != '\0') 162 { 163 struct hashentry search; 164 const struct hashentry *found; 165 166 /* Search for a matching entry in the hash table. */ 167 search.str = nl->n_name; 168 found = nlist_fshash_find (table, nl->n_name, 0, &search); 169 170 if (found != NULL) 171 { 172 /* Found it. */ 173 nl->n_value = found->sym.st_value; 174 nl->n_scnum = found->sym.st_shndx; 175 nl->n_type = GELF_ST_TYPE (found->sym.st_info); 176 /* XXX What shall we fill in the next fields? */ 177 nl->n_sclass = 0; 178 nl->n_numaux = 0; 179 } 180 else 181 { 182 /* Not there. */ 183 nl->n_value = 0; 184 nl->n_scnum = 0; 185 nl->n_type = 0; 186 nl->n_sclass = 0; 187 nl->n_numaux = 0; 188 } 189 190 /* Next search request. */ 191 ++nl; 192 } 193 194 /* Free the resources. */ 195 nlist_fshash_fini (table); 196 197 /* We do not need the ELF descriptor anymore. */ 198 (void) elf_end (elf); 199 200 return 0; 201 202 fail_dealloc: 203 nlist_fshash_fini (table); 204 205 fail_close: 206 /* We do not need the ELF descriptor anymore. */ 207 (void) elf_end (elf); 208 209 fail: 210 /* We have to set all entries to zero. */ 211 while (nl->n_name != NULL && nl->n_name[0] != '\0') 212 { 213 nl->n_value = 0; 214 nl->n_scnum = 0; 215 nl->n_type = 0; 216 nl->n_sclass = 0; 217 nl->n_numaux = 0; 218 219 /* Next entry. */ 220 ++nl; 221 } 222 223 return -1; 224 } 225