Roland McGrath | d17fac7 | 2005-08-23 08:20:21 +0000 | [diff] [blame] | 1 | /* Recover relocatibility for addresses computed from debug information. |
Petr Machata | e187314 | 2012-08-01 21:37:52 +0200 | [diff] [blame] | 2 | Copyright (C) 2005-2009, 2012 Red Hat, Inc. |
Mark Wielaard | de2ed97 | 2012-06-05 17:15:16 +0200 | [diff] [blame] | 3 | This file is part of elfutils. |
Roland McGrath | d17fac7 | 2005-08-23 08:20:21 +0000 | [diff] [blame] | 4 | |
Mark Wielaard | de2ed97 | 2012-06-05 17:15:16 +0200 | [diff] [blame] | 5 | This file is free software; you can redistribute it and/or modify |
| 6 | it under the terms of either |
Roland McGrath | d17fac7 | 2005-08-23 08:20:21 +0000 | [diff] [blame] | 7 | |
Mark Wielaard | de2ed97 | 2012-06-05 17:15:16 +0200 | [diff] [blame] | 8 | * the GNU Lesser General Public License as published by the Free |
| 9 | Software Foundation; either version 3 of the License, or (at |
| 10 | your option) any later version |
| 11 | |
| 12 | or |
| 13 | |
| 14 | * the GNU General Public License as published by the Free |
| 15 | Software Foundation; either version 2 of the License, or (at |
| 16 | your option) any later version |
| 17 | |
| 18 | or both in parallel, as here. |
| 19 | |
| 20 | elfutils is distributed in the hope that it will be useful, but |
Ulrich Drepper | 361df7d | 2006-04-04 21:38:57 +0000 | [diff] [blame] | 21 | WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 23 | General Public License for more details. |
| 24 | |
Mark Wielaard | de2ed97 | 2012-06-05 17:15:16 +0200 | [diff] [blame] | 25 | You should have received copies of the GNU General Public License and |
| 26 | the GNU Lesser General Public License along with this program. If |
| 27 | not, see <http://www.gnu.org/licenses/>. */ |
Roland McGrath | d17fac7 | 2005-08-23 08:20:21 +0000 | [diff] [blame] | 28 | |
| 29 | #include "libdwflP.h" |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 30 | #include <fcntl.h> |
Roland McGrath | d17fac7 | 2005-08-23 08:20:21 +0000 | [diff] [blame] | 31 | #include <unistd.h> |
| 32 | |
| 33 | /* Since dwfl_report_elf lays out the sections already, this will only be |
| 34 | called when the section headers of the debuginfo file are being |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 35 | consulted instead, or for the section placed at 0. With binutils |
| 36 | strip-to-debug, the symbol table is in the debuginfo file and relocation |
| 37 | looks there. */ |
Roland McGrath | d17fac7 | 2005-08-23 08:20:21 +0000 | [diff] [blame] | 38 | int |
| 39 | dwfl_offline_section_address (Dwfl_Module *mod, |
| 40 | void **userdata __attribute__ ((unused)), |
| 41 | const char *modname __attribute__ ((unused)), |
| 42 | Dwarf_Addr base __attribute__ ((unused)), |
| 43 | const char *secname __attribute__ ((unused)), |
| 44 | Elf32_Word shndx, |
| 45 | const GElf_Shdr *shdr __attribute__ ((unused)), |
| 46 | Dwarf_Addr *addr) |
| 47 | { |
Roland McGrath | 9aa8ef7 | 2007-05-18 08:59:43 +0000 | [diff] [blame] | 48 | assert (mod->e_type == ET_REL); |
Roland McGrath | d17fac7 | 2005-08-23 08:20:21 +0000 | [diff] [blame] | 49 | assert (shdr->sh_addr == 0); |
| 50 | assert (shdr->sh_flags & SHF_ALLOC); |
Roland McGrath | d17fac7 | 2005-08-23 08:20:21 +0000 | [diff] [blame] | 51 | |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 52 | if (mod->debug.elf == NULL) |
| 53 | /* We are only here because sh_addr is zero even though layout is complete. |
| 54 | The first section in the first file under -e is placed at 0. */ |
| 55 | return 0; |
| 56 | |
Roland McGrath | 9aa8ef7 | 2007-05-18 08:59:43 +0000 | [diff] [blame] | 57 | /* The section numbers might not match between the two files. |
| 58 | The best we can rely on is the order of SHF_ALLOC sections. */ |
| 59 | |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 60 | Elf_Scn *ourscn = elf_getscn (mod->debug.elf, shndx); |
Roland McGrath | 9aa8ef7 | 2007-05-18 08:59:43 +0000 | [diff] [blame] | 61 | Elf_Scn *scn = NULL; |
| 62 | uint_fast32_t skip_alloc = 0; |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 63 | while ((scn = elf_nextscn (mod->debug.elf, scn)) != ourscn) |
Roland McGrath | 9aa8ef7 | 2007-05-18 08:59:43 +0000 | [diff] [blame] | 64 | { |
| 65 | assert (scn != NULL); |
| 66 | GElf_Shdr shdr_mem; |
| 67 | GElf_Shdr *sh = gelf_getshdr (scn, &shdr_mem); |
| 68 | if (unlikely (sh == NULL)) |
| 69 | return -1; |
| 70 | if (sh->sh_flags & SHF_ALLOC) |
| 71 | ++skip_alloc; |
| 72 | } |
| 73 | |
| 74 | scn = NULL; |
| 75 | while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL) |
| 76 | { |
| 77 | GElf_Shdr shdr_mem; |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 78 | GElf_Shdr *main_shdr = gelf_getshdr (scn, &shdr_mem); |
Roland McGrath | 9aa8ef7 | 2007-05-18 08:59:43 +0000 | [diff] [blame] | 79 | if (unlikely (main_shdr == NULL)) |
| 80 | return -1; |
| 81 | if ((main_shdr->sh_flags & SHF_ALLOC) && skip_alloc-- == 0) |
| 82 | { |
| 83 | assert (main_shdr->sh_flags == shdr->sh_flags); |
| 84 | *addr = main_shdr->sh_addr; |
| 85 | return 0; |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | /* This should never happen. */ |
| 90 | return -1; |
Roland McGrath | d17fac7 | 2005-08-23 08:20:21 +0000 | [diff] [blame] | 91 | } |
| 92 | INTDEF (dwfl_offline_section_address) |
| 93 | |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 94 | /* Forward declarations. */ |
| 95 | static Dwfl_Module *process_elf (Dwfl *dwfl, const char *name, |
| 96 | const char *file_name, int fd, Elf *elf); |
| 97 | static Dwfl_Module *process_archive (Dwfl *dwfl, const char *name, |
| 98 | const char *file_name, int fd, Elf *elf, |
| 99 | int (*predicate) (const char *module, |
| 100 | const char *file)); |
Roland McGrath | d17fac7 | 2005-08-23 08:20:21 +0000 | [diff] [blame] | 101 | |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 102 | /* Report one module for an ELF file, or many for an archive. |
| 103 | Always consumes ELF and FD. */ |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 104 | static Dwfl_Module * |
| 105 | process_file (Dwfl *dwfl, const char *name, const char *file_name, int fd, |
| 106 | Elf *elf, int (*predicate) (const char *module, |
| 107 | const char *file)) |
| 108 | { |
| 109 | switch (elf_kind (elf)) |
| 110 | { |
| 111 | default: |
| 112 | case ELF_K_NONE: |
| 113 | __libdwfl_seterrno (elf == NULL ? DWFL_E_LIBELF : DWFL_E_BADELF); |
| 114 | return NULL; |
| 115 | |
| 116 | case ELF_K_ELF: |
| 117 | return process_elf (dwfl, name, file_name, fd, elf); |
| 118 | |
| 119 | case ELF_K_AR: |
| 120 | return process_archive (dwfl, name, file_name, fd, elf, predicate); |
| 121 | } |
| 122 | } |
| 123 | |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 124 | /* Report the open ELF file as a module. Always consumes ELF and FD. */ |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 125 | static Dwfl_Module * |
| 126 | process_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd, |
| 127 | Elf *elf) |
| 128 | { |
| 129 | Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name, fd, elf, |
Roland McGrath | 5453abf | 2009-02-10 17:33:49 -0800 | [diff] [blame] | 130 | dwfl->offline_next_address, false); |
Roland McGrath | d17fac7 | 2005-08-23 08:20:21 +0000 | [diff] [blame] | 131 | if (mod != NULL) |
| 132 | { |
| 133 | /* If this is an ET_EXEC file with fixed addresses, the address range |
| 134 | it consumed may or may not intersect with the arbitrary range we |
| 135 | will use for relocatable modules. Make sure we always use a free |
Roland McGrath | 687d7e9 | 2006-02-27 04:53:00 +0000 | [diff] [blame] | 136 | range for the offline allocations. If this module did use |
| 137 | offline_next_address, it may have rounded it up for the module's |
| 138 | alignment requirements. */ |
| 139 | if ((dwfl->offline_next_address >= mod->low_addr |
| 140 | || mod->low_addr - dwfl->offline_next_address < OFFLINE_REDZONE) |
Roland McGrath | d17fac7 | 2005-08-23 08:20:21 +0000 | [diff] [blame] | 141 | && dwfl->offline_next_address < mod->high_addr + OFFLINE_REDZONE) |
| 142 | dwfl->offline_next_address = mod->high_addr + OFFLINE_REDZONE; |
| 143 | |
| 144 | /* Don't keep the file descriptor around. */ |
| 145 | if (mod->main.fd != -1 && elf_cntl (mod->main.elf, ELF_C_FDREAD) == 0) |
| 146 | { |
| 147 | close (mod->main.fd); |
| 148 | mod->main.fd = -1; |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | return mod; |
| 153 | } |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 154 | |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 155 | /* Always consumes MEMBER. Returns elf_next result on success. |
| 156 | For errors returns ELF_C_NULL with *MOD set to null. */ |
| 157 | static Elf_Cmd |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 158 | process_archive_member (Dwfl *dwfl, const char *name, const char *file_name, |
| 159 | int (*predicate) (const char *module, const char *file), |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 160 | int fd, Elf *member, Dwfl_Module **mod) |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 161 | { |
| 162 | const Elf_Arhdr *h = elf_getarhdr (member); |
| 163 | if (unlikely (h == NULL)) |
| 164 | { |
| 165 | __libdwfl_seterrno (DWFL_E_LIBELF); |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 166 | fail: |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 167 | elf_end (member); |
| 168 | *mod = NULL; |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 169 | return ELF_C_NULL; |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 170 | } |
| 171 | |
Petr Machata | e187314 | 2012-08-01 21:37:52 +0200 | [diff] [blame] | 172 | if (!strcmp (h->ar_name, "/") || !strcmp (h->ar_name, "//") |
| 173 | || !strcmp (h->ar_name, "/SYM64/")) |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 174 | { |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 175 | skip:; |
| 176 | /* Skip this and go to the next. */ |
| 177 | Elf_Cmd result = elf_next (member); |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 178 | elf_end (member); |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 179 | return result; |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 180 | } |
| 181 | |
| 182 | char *member_name; |
| 183 | if (unlikely (asprintf (&member_name, "%s(%s)", file_name, h->ar_name) < 0)) |
| 184 | { |
| 185 | nomem: |
| 186 | __libdwfl_seterrno (DWFL_E_NOMEM); |
| 187 | elf_end (member); |
| 188 | *mod = NULL; |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 189 | return ELF_C_NULL; |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 190 | } |
| 191 | |
| 192 | char *module_name = NULL; |
| 193 | if (name == NULL || name[0] == '\0') |
| 194 | name = h->ar_name; |
| 195 | else if (unlikely (asprintf (&module_name, "%s:%s", name, h->ar_name) < 0)) |
| 196 | { |
| 197 | free (member_name); |
| 198 | goto nomem; |
| 199 | } |
| 200 | else |
| 201 | name = module_name; |
| 202 | |
| 203 | if (predicate != NULL) |
| 204 | { |
| 205 | /* Let the predicate decide whether to use this one. */ |
| 206 | int want = (*predicate) (name, member_name); |
| 207 | if (want <= 0) |
| 208 | { |
| 209 | free (member_name); |
| 210 | free (module_name); |
| 211 | if (unlikely (want < 0)) |
| 212 | { |
| 213 | __libdwfl_seterrno (DWFL_E_CB); |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 214 | goto fail; |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 215 | } |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 216 | goto skip; |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 217 | } |
| 218 | } |
| 219 | |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 220 | /* We let __libdwfl_report_elf cache the fd in mod->main.fd, |
| 221 | though it's the same fd for all the members. |
| 222 | On module teardown we will close it only on the last Elf reference. */ |
| 223 | *mod = process_file (dwfl, name, member_name, fd, member, predicate); |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 224 | free (member_name); |
| 225 | free (module_name); |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 226 | |
| 227 | if (*mod == NULL) /* process_file called elf_end. */ |
| 228 | return ELF_C_NULL; |
| 229 | |
| 230 | /* Advance the archive-reading offset for the next iteration. */ |
| 231 | return elf_next (member); |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 232 | } |
| 233 | |
| 234 | /* Report each member of the archive as its own module. */ |
| 235 | static Dwfl_Module * |
| 236 | process_archive (Dwfl *dwfl, const char *name, const char *file_name, int fd, |
| 237 | Elf *archive, |
| 238 | int (*predicate) (const char *module, const char *file)) |
| 239 | |
| 240 | { |
| 241 | Dwfl_Module *mod = NULL; |
Roland McGrath | b28a894 | 2008-12-11 02:09:28 -0800 | [diff] [blame] | 242 | Elf *member = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, archive); |
| 243 | if (unlikely (member == NULL)) /* Empty archive. */ |
| 244 | { |
| 245 | __libdwfl_seterrno (DWFL_E_BADELF); |
| 246 | return NULL; |
| 247 | } |
| 248 | |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 249 | while (process_archive_member (dwfl, name, file_name, predicate, |
Roland McGrath | b28a894 | 2008-12-11 02:09:28 -0800 | [diff] [blame] | 250 | fd, member, &mod) != ELF_C_NULL) |
| 251 | member = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, archive); |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 252 | |
| 253 | /* We can drop the archive Elf handle even if we're still using members |
| 254 | in live modules. When the last module's elf_end on a member returns |
| 255 | zero, that module will close FD. If no modules survived the predicate, |
| 256 | we are all done with the file right here. */ |
Roland McGrath | b28a894 | 2008-12-11 02:09:28 -0800 | [diff] [blame] | 257 | if (mod != NULL /* If no modules, caller will clean up. */ |
| 258 | && elf_end (archive) == 0) |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 259 | close (fd); |
Roland McGrath | e4c22ea | 2007-10-23 13:07:39 +0000 | [diff] [blame] | 260 | |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 261 | return mod; |
| 262 | } |
| 263 | |
| 264 | Dwfl_Module * |
| 265 | internal_function |
| 266 | __libdwfl_report_offline (Dwfl *dwfl, const char *name, |
| 267 | const char *file_name, int fd, bool closefd, |
| 268 | int (*predicate) (const char *module, |
| 269 | const char *file)) |
| 270 | { |
Roland McGrath | bca4315 | 2009-01-05 23:59:32 -0800 | [diff] [blame] | 271 | Elf *elf; |
| 272 | Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, true); |
| 273 | if (error != DWFL_E_NOERROR) |
| 274 | { |
| 275 | __libdwfl_seterrno (error); |
| 276 | return NULL; |
| 277 | } |
Ulrich Drepper | b597dfa | 2007-10-16 05:21:27 +0000 | [diff] [blame] | 278 | Dwfl_Module *mod = process_file (dwfl, name, file_name, fd, elf, predicate); |
| 279 | if (mod == NULL) |
| 280 | { |
| 281 | elf_end (elf); |
| 282 | if (closefd) |
| 283 | close (fd); |
| 284 | } |
| 285 | return mod; |
| 286 | } |
| 287 | |
| 288 | Dwfl_Module * |
| 289 | dwfl_report_offline (Dwfl *dwfl, const char *name, |
| 290 | const char *file_name, int fd) |
| 291 | { |
| 292 | if (dwfl == NULL) |
| 293 | return NULL; |
| 294 | |
| 295 | bool closefd = false; |
| 296 | if (fd < 0) |
| 297 | { |
| 298 | closefd = true; |
| 299 | fd = open64 (file_name, O_RDONLY); |
| 300 | if (fd < 0) |
| 301 | { |
| 302 | __libdwfl_seterrno (DWFL_E_ERRNO); |
| 303 | return NULL; |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | return __libdwfl_report_offline (dwfl, name, file_name, fd, closefd, NULL); |
| 308 | } |
Roland McGrath | d17fac7 | 2005-08-23 08:20:21 +0000 | [diff] [blame] | 309 | INTDEF (dwfl_report_offline) |