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