1 /* Recover relocatibility for addresses computed from debug information. 2 Copyright (C) 2005-2010 Red Hat, Inc. 3 This file is part of Red Hat elfutils. 4 5 Red Hat elfutils is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by the 7 Free Software Foundation; version 2 of the License. 8 9 Red Hat elfutils is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 General Public License for more details. 13 14 You should have received a copy of the GNU General Public License along 15 with Red Hat elfutils; if not, write to the Free Software Foundation, 16 Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. 17 18 In addition, as a special exception, Red Hat, Inc. gives You the 19 additional right to link the code of Red Hat elfutils with code licensed 20 under any Open Source Initiative certified open source license 21 (http://www.opensource.org/licenses/index.php) which requires the 22 distribution of source code with any binary distribution and to 23 distribute linked combinations of the two. Non-GPL Code permitted under 24 this exception must only link to the code of Red Hat elfutils through 25 those well defined interfaces identified in the file named EXCEPTION 26 found in the source code files (the "Approved Interfaces"). The files 27 of Non-GPL Code may instantiate templates or use macros or inline 28 functions from the Approved Interfaces without causing the resulting 29 work to be covered by the GNU General Public License. Only Red Hat, 30 Inc. may make changes or additions to the list of Approved Interfaces. 31 Red Hat's grant of this exception is conditioned upon your not adding 32 any new exceptions. If you wish to add a new Approved Interface or 33 exception, please contact Red Hat. You must obey the GNU General Public 34 License in all respects for all of the Red Hat elfutils code and other 35 code used in conjunction with Red Hat elfutils except the Non-GPL Code 36 covered by this exception. If you modify this file, you may extend this 37 exception to your version of the file, but you are not obligated to do 38 so. If you do not wish to provide this exception without modification, 39 you must delete this exception statement from your version and license 40 this file solely under the GPL without exception. 41 42 Red Hat elfutils is an included package of the Open Invention Network. 43 An included package of the Open Invention Network is a package for which 44 Open Invention Network licensees cross-license their patents. No patent 45 license is granted, either expressly or impliedly, by designation as an 46 included package. Should you wish to participate in the Open Invention 47 Network licensing program, please visit www.openinventionnetwork.com 48 <http://www.openinventionnetwork.com>. */ 49 50 #include "libdwflP.h" 51 52 struct dwfl_relocation 53 { 54 size_t count; 55 struct 56 { 57 Elf_Scn *scn; 58 Elf_Scn *relocs; 59 const char *name; 60 GElf_Addr start, end; 61 } refs[0]; 62 }; 63 64 65 struct secref 66 { 67 struct secref *next; 68 Elf_Scn *scn; 69 Elf_Scn *relocs; 70 const char *name; 71 GElf_Addr start, end; 72 }; 73 74 static int 75 compare_secrefs (const void *a, const void *b) 76 { 77 struct secref *const *p1 = a; 78 struct secref *const *p2 = b; 79 80 /* No signed difference calculation is correct here, since the 81 terms are unsigned and could be more than INT64_MAX apart. */ 82 if ((*p1)->start < (*p2)->start) 83 return -1; 84 if ((*p1)->start > (*p2)->start) 85 return 1; 86 87 return 0; 88 } 89 90 static int 91 cache_sections (Dwfl_Module *mod) 92 { 93 if (likely (mod->reloc_info != NULL)) 94 return mod->reloc_info->count; 95 96 struct secref *refs = NULL; 97 size_t nrefs = 0; 98 99 size_t shstrndx; 100 if (unlikely (elf_getshdrstrndx (mod->main.elf, &shstrndx) < 0)) 101 { 102 elf_error: 103 __libdwfl_seterrno (DWFL_E_LIBELF); 104 return -1; 105 } 106 107 bool check_reloc_sections = false; 108 Elf_Scn *scn = NULL; 109 while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL) 110 { 111 GElf_Shdr shdr_mem; 112 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 113 if (shdr == NULL) 114 goto elf_error; 115 116 if ((shdr->sh_flags & SHF_ALLOC) && shdr->sh_addr == 0 117 && mod->e_type == ET_REL) 118 { 119 /* This section might not yet have been looked at. */ 120 if (__libdwfl_relocate_value (mod, mod->main.elf, &shstrndx, 121 elf_ndxscn (scn), 122 &shdr->sh_addr) != DWFL_E_NOERROR) 123 continue; 124 shdr = gelf_getshdr (scn, &shdr_mem); 125 if (unlikely (shdr == NULL)) 126 goto elf_error; 127 } 128 129 if (shdr->sh_flags & SHF_ALLOC) 130 { 131 const char *name = elf_strptr (mod->main.elf, shstrndx, 132 shdr->sh_name); 133 if (unlikely (name == NULL)) 134 goto elf_error; 135 136 struct secref *newref = alloca (sizeof *newref); 137 newref->scn = scn; 138 newref->relocs = NULL; 139 newref->name = name; 140 newref->start = dwfl_adjusted_address (mod, shdr->sh_addr); 141 newref->end = newref->start + shdr->sh_size; 142 newref->next = refs; 143 refs = newref; 144 ++nrefs; 145 } 146 147 if (mod->e_type == ET_REL 148 && shdr->sh_size != 0 149 && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) 150 && mod->dwfl->callbacks->section_address != NULL) 151 { 152 if (shdr->sh_info < elf_ndxscn (scn)) 153 { 154 /* We've already looked at the section these relocs apply to. */ 155 Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info); 156 if (likely (tscn != NULL)) 157 for (struct secref *sec = refs; sec != NULL; sec = sec->next) 158 if (sec->scn == tscn) 159 { 160 sec->relocs = scn; 161 break; 162 } 163 } 164 else 165 /* We'll have to do a second pass. */ 166 check_reloc_sections = true; 167 } 168 } 169 170 mod->reloc_info = malloc (offsetof (struct dwfl_relocation, refs[nrefs])); 171 if (mod->reloc_info == NULL) 172 { 173 __libdwfl_seterrno (DWFL_E_NOMEM); 174 return -1; 175 } 176 177 struct secref **sortrefs = alloca (nrefs * sizeof sortrefs[0]); 178 for (size_t i = nrefs; i-- > 0; refs = refs->next) 179 sortrefs[i] = refs; 180 assert (refs == NULL); 181 182 qsort (sortrefs, nrefs, sizeof sortrefs[0], &compare_secrefs); 183 184 mod->reloc_info->count = nrefs; 185 for (size_t i = 0; i < nrefs; ++i) 186 { 187 mod->reloc_info->refs[i].name = sortrefs[i]->name; 188 mod->reloc_info->refs[i].scn = sortrefs[i]->scn; 189 mod->reloc_info->refs[i].relocs = sortrefs[i]->relocs; 190 mod->reloc_info->refs[i].start = sortrefs[i]->start; 191 mod->reloc_info->refs[i].end = sortrefs[i]->end; 192 } 193 194 if (unlikely (check_reloc_sections)) 195 { 196 /* There was a reloc section that preceded its target section. 197 So we have to scan again now that we have cached all the 198 possible target sections we care about. */ 199 200 scn = NULL; 201 while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL) 202 { 203 GElf_Shdr shdr_mem; 204 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 205 if (shdr == NULL) 206 goto elf_error; 207 208 if (shdr->sh_size != 0 209 && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)) 210 { 211 Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info); 212 if (likely (tscn != NULL)) 213 for (size_t i = 0; i < nrefs; ++i) 214 if (mod->reloc_info->refs[i].scn == tscn) 215 { 216 mod->reloc_info->refs[i].relocs = scn; 217 break; 218 } 219 } 220 } 221 } 222 223 return nrefs; 224 } 225 226 227 int 228 dwfl_module_relocations (Dwfl_Module *mod) 229 { 230 if (mod == NULL) 231 return -1; 232 233 switch (mod->e_type) 234 { 235 case ET_REL: 236 return cache_sections (mod); 237 238 case ET_DYN: 239 return 1; 240 241 case ET_EXEC: 242 assert (mod->main.vaddr == mod->low_addr); 243 break; 244 } 245 246 return 0; 247 } 248 249 const char * 250 dwfl_module_relocation_info (Dwfl_Module *mod, unsigned int idx, 251 Elf32_Word *shndxp) 252 { 253 if (mod == NULL) 254 return NULL; 255 256 switch (mod->e_type) 257 { 258 case ET_REL: 259 break; 260 261 case ET_DYN: 262 if (idx != 0) 263 return NULL; 264 if (shndxp) 265 *shndxp = SHN_ABS; 266 return ""; 267 268 default: 269 return NULL; 270 } 271 272 if (cache_sections (mod) < 0) 273 return NULL; 274 275 struct dwfl_relocation *sections = mod->reloc_info; 276 277 if (idx >= sections->count) 278 return NULL; 279 280 if (shndxp) 281 *shndxp = elf_ndxscn (sections->refs[idx].scn); 282 283 return sections->refs[idx].name; 284 } 285 286 /* Check that MOD is valid and make sure its relocation has been done. */ 287 static bool 288 check_module (Dwfl_Module *mod) 289 { 290 if (INTUSE(dwfl_module_getsymtab) (mod) < 0) 291 { 292 Dwfl_Error error = dwfl_errno (); 293 if (error != DWFL_E_NO_SYMTAB) 294 { 295 __libdwfl_seterrno (error); 296 return true; 297 } 298 } 299 300 if (mod->dw == NULL) 301 { 302 Dwarf_Addr bias; 303 if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL) 304 { 305 Dwfl_Error error = dwfl_errno (); 306 if (error != DWFL_E_NO_DWARF) 307 { 308 __libdwfl_seterrno (error); 309 return true; 310 } 311 } 312 } 313 314 return false; 315 } 316 317 /* Find the index in MOD->reloc_info.refs containing *ADDR. */ 318 static int 319 find_section (Dwfl_Module *mod, Dwarf_Addr *addr) 320 { 321 if (cache_sections (mod) < 0) 322 return -1; 323 324 struct dwfl_relocation *sections = mod->reloc_info; 325 326 /* The sections are sorted by address, so we can use binary search. */ 327 size_t l = 0, u = sections->count; 328 while (l < u) 329 { 330 size_t idx = (l + u) / 2; 331 if (*addr < sections->refs[idx].start) 332 u = idx; 333 else if (*addr > sections->refs[idx].end) 334 l = idx + 1; 335 else 336 { 337 /* Consider the limit of a section to be inside it, unless it's 338 inside the next one. A section limit address can appear in 339 line records. */ 340 if (*addr == sections->refs[idx].end 341 && idx < sections->count 342 && *addr == sections->refs[idx + 1].start) 343 ++idx; 344 345 *addr -= sections->refs[idx].start; 346 return idx; 347 } 348 } 349 350 __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_NO_MATCH)); 351 return -1; 352 } 353 354 int 355 dwfl_module_relocate_address (Dwfl_Module *mod, Dwarf_Addr *addr) 356 { 357 if (unlikely (check_module (mod))) 358 return -1; 359 360 switch (mod->e_type) 361 { 362 case ET_REL: 363 return find_section (mod, addr); 364 365 case ET_DYN: 366 /* All relative to first and only relocation base: module start. */ 367 *addr -= mod->low_addr; 368 break; 369 370 default: 371 /* Already absolute, dwfl_module_relocations returned zero. We 372 shouldn't really have been called, but it's a harmless no-op. */ 373 break; 374 } 375 376 return 0; 377 } 378 INTDEF (dwfl_module_relocate_address) 379 380 Elf_Scn * 381 dwfl_module_address_section (Dwfl_Module *mod, Dwarf_Addr *address, 382 Dwarf_Addr *bias) 383 { 384 if (check_module (mod)) 385 return NULL; 386 387 int idx = find_section (mod, address); 388 if (idx < 0) 389 return NULL; 390 391 if (mod->reloc_info->refs[idx].relocs != NULL) 392 { 393 assert (mod->e_type == ET_REL); 394 395 Elf_Scn *tscn = mod->reloc_info->refs[idx].scn; 396 Elf_Scn *relocscn = mod->reloc_info->refs[idx].relocs; 397 Dwfl_Error result = __libdwfl_relocate_section (mod, mod->main.elf, 398 relocscn, tscn, true); 399 if (likely (result == DWFL_E_NOERROR)) 400 mod->reloc_info->refs[idx].relocs = NULL; 401 else 402 { 403 __libdwfl_seterrno (result); 404 return NULL; 405 } 406 } 407 408 *bias = dwfl_adjusted_address (mod, 0); 409 return mod->reloc_info->refs[idx].scn; 410 } 411 INTDEF (dwfl_module_address_section) 412