1/* Report a module to libdwfl based on ELF program headers. 2 Copyright (C) 2005-2010 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 either 7 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 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 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/>. */ 28 29#include "libdwflP.h" 30#include <fcntl.h> 31#include <unistd.h> 32 33 34/* We start every ET_REL module at a moderately aligned boundary. 35 This keeps the low addresses easy to read compared to a layout 36 starting at 0 (as when using -e). It also makes it unlikely 37 that a middle section will have a larger alignment and require 38 rejiggering (see below). */ 39#define REL_MIN_ALIGN ((GElf_Xword) 0x100) 40 41bool 42internal_function 43__libdwfl_elf_address_range (Elf *elf, GElf_Addr base, bool add_p_vaddr, 44 bool sanity, GElf_Addr *vaddrp, 45 GElf_Addr *address_syncp, GElf_Addr *startp, 46 GElf_Addr *endp, GElf_Addr *biasp, 47 GElf_Half *e_typep) 48{ 49 GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem); 50 if (ehdr == NULL) 51 { 52 elf_error: 53 __libdwfl_seterrno (DWFL_E_LIBELF); 54 return false; 55 } 56 57 GElf_Addr vaddr = 0; 58 GElf_Addr address_sync = 0; 59 GElf_Addr start = 0, end = 0, bias = 0; 60 switch (ehdr->e_type) 61 { 62 case ET_REL: 63 /* For a relocatable object, we do an arbitrary section layout. 64 By updating the section header in place, we leave the layout 65 information to be found by relocation. */ 66 67 start = end = base = (base + REL_MIN_ALIGN - 1) & -REL_MIN_ALIGN; 68 69 bool first = true; 70 Elf_Scn *scn = NULL; 71 while ((scn = elf_nextscn (elf, scn)) != NULL) 72 { 73 GElf_Shdr shdr_mem; 74 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 75 if (unlikely (shdr == NULL)) 76 goto elf_error; 77 78 if (shdr->sh_flags & SHF_ALLOC) 79 { 80 const GElf_Xword align = shdr->sh_addralign ?: 1; 81 const GElf_Addr next = (end + align - 1) & -align; 82 if (shdr->sh_addr == 0 83 /* Once we've started doing layout we have to do it all, 84 unless we just layed out the first section at 0 when 85 it already was at 0. */ 86 || (bias == 0 && end > start && end != next)) 87 { 88 shdr->sh_addr = next; 89 if (end == base) 90 /* This is the first section assigned a location. 91 Use its aligned address as the module's base. */ 92 start = base = shdr->sh_addr; 93 else if (unlikely (base & (align - 1))) 94 { 95 /* If BASE has less than the maximum alignment of 96 any section, we eat more than the optimal amount 97 of padding and so make the module's apparent 98 size come out larger than it would when placed 99 at zero. So reset the layout with a better base. */ 100 101 start = end = base = (base + align - 1) & -align; 102 Elf_Scn *prev_scn = NULL; 103 do 104 { 105 prev_scn = elf_nextscn (elf, prev_scn); 106 GElf_Shdr prev_shdr_mem; 107 GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn, 108 &prev_shdr_mem); 109 if (unlikely (prev_shdr == NULL)) 110 goto elf_error; 111 if (prev_shdr->sh_flags & SHF_ALLOC) 112 { 113 const GElf_Xword prev_align 114 = prev_shdr->sh_addralign ?: 1; 115 116 prev_shdr->sh_addr 117 = (end + prev_align - 1) & -prev_align; 118 end = prev_shdr->sh_addr + prev_shdr->sh_size; 119 120 if (unlikely (! gelf_update_shdr (prev_scn, 121 prev_shdr))) 122 goto elf_error; 123 } 124 } 125 while (prev_scn != scn); 126 continue; 127 } 128 129 end = shdr->sh_addr + shdr->sh_size; 130 if (likely (shdr->sh_addr != 0) 131 && unlikely (! gelf_update_shdr (scn, shdr))) 132 goto elf_error; 133 } 134 else 135 { 136 /* The address is already assigned. Just track it. */ 137 if (first || end < shdr->sh_addr + shdr->sh_size) 138 end = shdr->sh_addr + shdr->sh_size; 139 if (first || bias > shdr->sh_addr) 140 /* This is the lowest address in the module. */ 141 bias = shdr->sh_addr; 142 143 if ((shdr->sh_addr - bias + base) & (align - 1)) 144 /* This section winds up misaligned using BASE. 145 Adjust BASE upwards to make it congruent to 146 the lowest section address in the file modulo ALIGN. */ 147 base = (((base + align - 1) & -align) 148 + (bias & (align - 1))); 149 } 150 151 first = false; 152 } 153 } 154 155 if (bias != 0) 156 { 157 /* The section headers had nonzero sh_addr values. The layout 158 was already done. We've just collected the total span. 159 Now just compute the bias from the requested base. */ 160 start = base; 161 end = end - bias + start; 162 bias = start - bias; 163 } 164 break; 165 166 /* Everything else has to have program headers. */ 167 168 case ET_EXEC: 169 case ET_CORE: 170 /* An assigned base address is meaningless for these. */ 171 base = 0; 172 add_p_vaddr = true; 173 174 case ET_DYN: 175 default:; 176 size_t phnum; 177 if (unlikely (elf_getphdrnum (elf, &phnum) != 0)) 178 goto elf_error; 179 for (size_t i = 0; i < phnum; ++i) 180 { 181 GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem); 182 if (unlikely (ph == NULL)) 183 goto elf_error; 184 if (ph->p_type == PT_LOAD) 185 { 186 vaddr = ph->p_vaddr & -ph->p_align; 187 address_sync = ph->p_vaddr + ph->p_memsz; 188 break; 189 } 190 } 191 if (add_p_vaddr) 192 { 193 start = base + vaddr; 194 bias = base; 195 } 196 else 197 { 198 start = base; 199 bias = base - vaddr; 200 } 201 202 for (size_t i = phnum; i-- > 0;) 203 { 204 GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem); 205 if (unlikely (ph == NULL)) 206 goto elf_error; 207 if (ph->p_type == PT_LOAD 208 && ph->p_vaddr + ph->p_memsz > 0) 209 { 210 end = bias + (ph->p_vaddr + ph->p_memsz); 211 break; 212 } 213 } 214 215 if (end == 0 && sanity) 216 { 217 __libdwfl_seterrno (DWFL_E_NO_PHDR); 218 return false; 219 } 220 break; 221 } 222 if (vaddrp) 223 *vaddrp = vaddr; 224 if (address_syncp) 225 *address_syncp = address_sync; 226 if (startp) 227 *startp = start; 228 if (endp) 229 *endp = end; 230 if (biasp) 231 *biasp = bias; 232 if (e_typep) 233 *e_typep = ehdr->e_type; 234 return true; 235} 236 237Dwfl_Module * 238internal_function 239__libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name, 240 int fd, Elf *elf, GElf_Addr base, bool add_p_vaddr, 241 bool sanity) 242{ 243 GElf_Addr vaddr, address_sync, start, end, bias; 244 GElf_Half e_type; 245 if (! __libdwfl_elf_address_range (elf, base, add_p_vaddr, sanity, &vaddr, 246 &address_sync, &start, &end, &bias, 247 &e_type)) 248 return NULL; 249 Dwfl_Module *m = INTUSE(dwfl_report_module) (dwfl, name, start, end); 250 if (m != NULL) 251 { 252 if (m->main.name == NULL) 253 { 254 m->main.name = strdup (file_name); 255 m->main.fd = fd; 256 } 257 else if ((fd >= 0 && m->main.fd != fd) 258 || strcmp (m->main.name, file_name)) 259 { 260 overlap: 261 m->gc = true; 262 __libdwfl_seterrno (DWFL_E_OVERLAP); 263 return NULL; 264 } 265 266 /* Preinstall the open ELF handle for the module. */ 267 if (m->main.elf == NULL) 268 { 269 m->main.elf = elf; 270 m->main.vaddr = vaddr; 271 m->main.address_sync = address_sync; 272 m->main_bias = bias; 273 m->e_type = e_type; 274 } 275 else 276 { 277 elf_end (elf); 278 if (m->main_bias != bias 279 || m->main.vaddr != vaddr || m->main.address_sync != address_sync) 280 goto overlap; 281 } 282 } 283 return m; 284} 285 286Dwfl_Module * 287dwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd, 288 GElf_Addr base, bool add_p_vaddr) 289{ 290 bool closefd = false; 291 if (fd < 0) 292 { 293 closefd = true; 294 fd = open (file_name, O_RDONLY); 295 if (fd < 0) 296 { 297 __libdwfl_seterrno (DWFL_E_ERRNO); 298 return NULL; 299 } 300 } 301 302 Elf *elf; 303 Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, false); 304 if (error != DWFL_E_NOERROR) 305 { 306 __libdwfl_seterrno (error); 307 return NULL; 308 } 309 310 Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name, 311 fd, elf, base, add_p_vaddr, true); 312 if (mod == NULL) 313 { 314 elf_end (elf); 315 if (closefd) 316 close (fd); 317 } 318 319 return mod; 320} 321INTDEF (dwfl_report_elf) 322NEW_VERSION (dwfl_report_elf, ELFUTILS_0.156) 323 324#ifdef SYMBOL_VERSIONING 325Dwfl_Module * 326 _compat_without_add_p_vaddr_dwfl_report_elf (Dwfl *dwfl, const char *name, 327 const char *file_name, int fd, 328 GElf_Addr base); 329COMPAT_VERSION_NEWPROTO (dwfl_report_elf, ELFUTILS_0.122, without_add_p_vaddr) 330 331Dwfl_Module * 332_compat_without_add_p_vaddr_dwfl_report_elf (Dwfl *dwfl, const char *name, 333 const char *file_name, int fd, 334 GElf_Addr base) 335{ 336 return dwfl_report_elf (dwfl, name, file_name, fd, base, true); 337} 338#endif 339