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.
5 This file is free software; you can redistribute it and/or modify
6 it under the terms of either
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
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
18 or both in parallel, as here.
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.
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/>. */
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)
43 __libdwfl_report_elf (Dwfl *dwfl, const char *name, const char *file_name,
44 int fd, Elf *elf, GElf_Addr base, bool sanity)
46 GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
50 __libdwfl_seterrno (DWFL_E_LIBELF);
55 GElf_Addr address_sync = 0;
56 GElf_Addr start = 0, end = 0, bias = 0;
60 /* For a relocatable object, we do an arbitrary section layout.
61 By updating the section header in place, we leave the layout
62 information to be found by relocation. */
64 start = end = base = (base + REL_MIN_ALIGN - 1) & -REL_MIN_ALIGN;
68 while ((scn = elf_nextscn (elf, scn)) != NULL)
71 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
72 if (unlikely (shdr == NULL))
75 if (shdr->sh_flags & SHF_ALLOC)
77 const GElf_Xword align = shdr->sh_addralign ?: 1;
78 const GElf_Addr next = (end + align - 1) & -align;
79 if (shdr->sh_addr == 0
80 /* Once we've started doing layout we have to do it all,
81 unless we just layed out the first section at 0 when
82 it already was at 0. */
83 || (bias == 0 && end > start && end != next))
87 /* This is the first section assigned a location.
88 Use its aligned address as the module's base. */
89 start = base = shdr->sh_addr;
90 else if (unlikely (base & (align - 1)))
92 /* If BASE has less than the maximum alignment of
93 any section, we eat more than the optimal amount
94 of padding and so make the module's apparent
95 size come out larger than it would when placed
96 at zero. So reset the layout with a better base. */
98 start = end = base = (base + align - 1) & -align;
99 Elf_Scn *prev_scn = NULL;
102 prev_scn = elf_nextscn (elf, prev_scn);
103 GElf_Shdr prev_shdr_mem;
104 GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn,
106 if (unlikely (prev_shdr == NULL))
108 if (prev_shdr->sh_flags & SHF_ALLOC)
110 const GElf_Xword prev_align
111 = prev_shdr->sh_addralign ?: 1;
114 = (end + prev_align - 1) & -prev_align;
115 end = prev_shdr->sh_addr + prev_shdr->sh_size;
117 if (unlikely (! gelf_update_shdr (prev_scn,
122 while (prev_scn != scn);
126 end = shdr->sh_addr + shdr->sh_size;
127 if (likely (shdr->sh_addr != 0)
128 && unlikely (! gelf_update_shdr (scn, shdr)))
133 /* The address is already assigned. Just track it. */
134 if (first || end < shdr->sh_addr + shdr->sh_size)
135 end = shdr->sh_addr + shdr->sh_size;
136 if (first || bias > shdr->sh_addr)
137 /* This is the lowest address in the module. */
138 bias = shdr->sh_addr;
140 if ((shdr->sh_addr - bias + base) & (align - 1))
141 /* This section winds up misaligned using BASE.
142 Adjust BASE upwards to make it congruent to
143 the lowest section address in the file modulo ALIGN. */
144 base = (((base + align - 1) & -align)
145 + (bias & (align - 1)));
154 /* The section headers had nonzero sh_addr values. The layout
155 was already done. We've just collected the total span.
156 Now just compute the bias from the requested base. */
158 end = end - bias + start;
163 /* Everything else has to have program headers. */
167 /* An assigned base address is meaningless for these. */
173 if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
175 for (size_t i = 0; i < phnum; ++i)
177 GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
178 if (unlikely (ph == NULL))
180 if (ph->p_type == PT_LOAD)
182 vaddr = ph->p_vaddr & -ph->p_align;
183 address_sync = ph->p_vaddr + ph->p_memsz;
184 if ((base & (ph->p_align - 1)) != 0)
185 base = (base + ph->p_align - 1) & -ph->p_align;
186 start = base + (ph->p_vaddr & -ph->p_align);
190 bias = start - vaddr;
192 for (size_t i = phnum; i-- > 0;)
194 GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
195 if (unlikely (ph == NULL))
197 if (ph->p_type == PT_LOAD
198 && ph->p_vaddr + ph->p_memsz > 0)
200 end = base + (ph->p_vaddr + ph->p_memsz);
205 if (end == 0 && sanity)
207 __libdwfl_seterrno (DWFL_E_NO_PHDR);
213 Dwfl_Module *m = INTUSE(dwfl_report_module) (dwfl, name, start, end);
216 if (m->main.name == NULL)
218 m->main.name = strdup (file_name);
221 else if ((fd >= 0 && m->main.fd != fd)
222 || strcmp (m->main.name, file_name))
226 __libdwfl_seterrno (DWFL_E_OVERLAP);
230 /* Preinstall the open ELF handle for the module. */
231 if (m->main.elf == NULL)
234 m->main.vaddr = vaddr;
235 m->main.address_sync = address_sync;
237 m->e_type = ehdr->e_type;
242 if (m->main_bias != bias
243 || m->main.vaddr != vaddr || m->main.address_sync != address_sync)
251 dwfl_report_elf (Dwfl *dwfl, const char *name,
252 const char *file_name, int fd, GElf_Addr base)
254 bool closefd = false;
258 fd = open64 (file_name, O_RDONLY);
261 __libdwfl_seterrno (DWFL_E_ERRNO);
267 Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, false);
268 if (error != DWFL_E_NOERROR)
270 __libdwfl_seterrno (error);
274 Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name,
275 fd, elf, base, true);
285 INTDEF (dwfl_report_elf)