1 /* Test program for libdwfl symbol resolving 2 Copyright (C) 2013 Red Hat, Inc. 3 This file is part of elfutils. 4 5 This file 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; either version 3 of the License, or 8 (at your option) any later version. 9 10 elfutils is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 17 18 #include <config.h> 19 #include <assert.h> 20 #include <inttypes.h> 21 #include ELFUTILS_HEADER(dwfl) 22 #include <elf.h> 23 #include <dwarf.h> 24 #include <argp.h> 25 #include <stdio.h> 26 #include <stdio_ext.h> 27 #include <stdlib.h> 28 #include <string.h> 29 30 static const char * 31 gelf_type (GElf_Sym *sym) 32 { 33 switch (GELF_ST_TYPE (sym->st_info)) 34 { 35 case STT_NOTYPE: 36 return "NOTYPE"; 37 case STT_OBJECT: 38 return "OBJECT"; 39 case STT_FUNC: 40 return "FUNC"; 41 case STT_SECTION: 42 return "SECTION"; 43 case STT_FILE: 44 return "FILE"; 45 case STT_COMMON: 46 return "COMMON"; 47 case STT_TLS: 48 return "TLS"; 49 default: 50 return "UNKNOWN"; 51 } 52 } 53 54 static const char * 55 gelf_bind (GElf_Sym *sym) 56 { 57 switch (GELF_ST_BIND (sym->st_info)) 58 { 59 case STB_LOCAL: 60 return "LOCAL"; 61 case STB_GLOBAL: 62 return "GLOBAL"; 63 case STB_WEAK: 64 return "WEAK"; 65 default: 66 return "UNKNOWN"; 67 } 68 } 69 70 static int 71 gelf_bind_order (GElf_Sym *sym) 72 { 73 switch (GELF_ST_BIND (sym->st_info)) 74 { 75 case STB_LOCAL: 76 return 1; 77 case STB_WEAK: 78 return 2; 79 case STB_GLOBAL: 80 return 3; 81 default: 82 return 0; 83 } 84 } 85 86 static const char * 87 elf_section_name (Elf *elf, GElf_Word shndx) 88 { 89 GElf_Ehdr ehdr; 90 GElf_Shdr shdr; 91 Elf_Scn *scn = elf_getscn (elf, shndx); 92 gelf_getshdr (scn, &shdr); 93 gelf_getehdr (elf, &ehdr); 94 return elf_strptr (elf, ehdr.e_shstrndx, shdr.sh_name); 95 } 96 97 bool 98 addr_in_section (Elf *elf, GElf_Word shndx, GElf_Addr addr) 99 { 100 GElf_Shdr shdr; 101 Elf_Scn *scn = elf_getscn (elf, shndx); 102 gelf_getshdr (scn, &shdr); 103 return addr >= shdr.sh_addr && addr < shdr.sh_addr + shdr.sh_size; 104 } 105 106 static int 107 list_syms (struct Dwfl_Module *mod, 108 void **user __attribute__ ((unused)), const char *mod_name, 109 Dwarf_Addr low_addr __attribute__ ((unused)), 110 void *arg __attribute__ ((unused))) 111 { 112 int syms = dwfl_module_getsymtab (mod); 113 if (syms < 0) 114 { 115 printf ("%s: %s\n", mod_name, dwfl_errmsg (-1)); 116 return DWARF_CB_OK; 117 } 118 119 for (int ndx = 0; ndx < syms; ndx++) 120 { 121 GElf_Sym sym; 122 GElf_Word shndxp; 123 Elf *elf; 124 Dwarf_Addr bias; 125 const char *name = dwfl_module_getsym (mod, ndx, &sym, &shndxp); 126 127 printf("%4d: %s\t%s\t%s (%" PRIu64 ") %#" PRIx64, 128 ndx, gelf_type (&sym), gelf_bind (&sym), name, 129 sym.st_size, sym.st_value); 130 131 /* The info variant doesn't adjust st_value but returns the (possible) 132 adjusted value separately. */ 133 GElf_Addr value; 134 GElf_Sym isym; 135 name = dwfl_module_getsym_info (mod, ndx, &isym, &value, &shndxp, 136 &elf, &bias); 137 138 GElf_Ehdr ehdr; 139 gelf_getehdr (elf, &ehdr); 140 141 // getsym st_values might or might not be adjusted depending on section. 142 // For ET_REL the adjustment is section relative. 143 assert (sym.st_value == isym.st_value 144 || sym.st_value == isym.st_value + bias 145 || ehdr.e_type == ET_REL); 146 147 /* And the reverse, which works for function symbols at least. 148 Note this only works because the st.value is adjusted by 149 dwfl_module_getsym (). */ 150 if (GELF_ST_TYPE (sym.st_info) == STT_FUNC && shndxp != SHN_UNDEF) 151 { 152 /* Make sure the adjusted value really falls in the elf section. */ 153 assert (addr_in_section (elf, shndxp, sym.st_value - bias)); 154 155 GElf_Addr addr = value; 156 GElf_Sym asym; 157 GElf_Word ashndxp; 158 Elf *aelf; 159 Dwarf_Addr abias; 160 GElf_Off off; 161 const char *aname = dwfl_module_addrinfo (mod, addr, &off, &asym, 162 &ashndxp, &aelf, &abias); 163 164 /* Make sure the adjusted value really falls in the elf section. */ 165 assert (addr_in_section (aelf, ashndxp, asym.st_value) 166 || ehdr.e_type == ET_REL); 167 168 /* Either they are the same symbol (name), the binding of 169 asym is "stronger" (or equal) to sym or asym is more specific 170 (has a lower address) than sym. */ 171 assert ((strcmp (name, aname) == 0 172 || gelf_bind_order (&asym) >= gelf_bind_order (&sym)) 173 && value <= sym.st_value); 174 175 addr = sym.st_value; 176 int res = dwfl_module_relocate_address (mod, &addr); 177 assert (res != -1); 178 if (shndxp < SHN_LORESERVE) 179 printf(", rel: %#" PRIx64 " (%s)", addr, 180 elf_section_name (elf, shndxp)); 181 else 182 printf(", rel: %#" PRIx64 "", addr); 183 184 /* Print the section of the actual value if different from sym. */ 185 if (value != isym.st_value + bias && ehdr.e_type != ET_REL) 186 { 187 GElf_Addr ebias; 188 addr = value; 189 Elf_Scn *scn = dwfl_module_address_section (mod, &addr, &ebias); 190 GElf_Shdr shdr_mem; 191 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 192 Elf *melf = dwfl_module_getelf (mod, &ebias); 193 gelf_getehdr (melf, &ehdr); 194 const char *sname = elf_strptr (melf, ehdr.e_shstrndx, 195 shdr->sh_name); 196 printf (" [%#" PRIx64 ", rel: %#" PRIx64 " (%s)]", 197 value, addr, sname); 198 } 199 200 } 201 printf ("\n"); 202 } 203 204 return DWARF_CB_OK; 205 } 206 207 int 208 main (int argc, char *argv[]) 209 { 210 int remaining; 211 Dwfl *dwfl; 212 error_t res; 213 214 res = argp_parse (dwfl_standard_argp (), argc, argv, 0, &remaining, &dwfl); 215 assert (res == 0 && dwfl != NULL); 216 217 ptrdiff_t off = 0; 218 do 219 off = dwfl_getmodules (dwfl, list_syms, NULL, off); 220 while (off > 0); 221 222 dwfl_end (dwfl); 223 224 return off; 225 } 226