dwfl_report_elf.c revision d17fac7e89666b47811581b10b5ca0d253a3a82d
1/* Report a module to libdwfl based on ELF program headers.
2   Copyright (C) 2005 Red Hat, Inc.
3
4   This program is Open Source software; you can redistribute it and/or
5   modify it under the terms of the Open Software License version 1.0 as
6   published by the Open Source Initiative.
7
8   You should have received a copy of the Open Software License along
9   with this program; if not, you may obtain a copy of the Open Software
10   License version 1.0 from http://www.opensource.org/licenses/osl.php or
11   by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
12   3001 King Ranch Road, Ukiah, CA 95482.   */
13
14#include "libdwflP.h"
15#include <fcntl.h>
16#include <unistd.h>
17
18
19Dwfl_Module *
20dwfl_report_elf (Dwfl *dwfl, const char *name,
21		 const char *file_name, int fd, GElf_Addr base)
22{
23  bool closefd = false;
24
25  if (fd < 0)
26    {
27      fd = open64 (file_name, O_RDONLY);
28      if (fd < 0)
29	{
30	  __libdwfl_seterrno (DWFL_E_ERRNO);
31	  return NULL;
32	}
33      closefd = true;
34    }
35
36  Elf *elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
37
38  GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
39  if (ehdr == NULL)
40    {
41    elf_error:
42      __libdwfl_seterrno (DWFL_E_LIBELF);
43      if (closefd)
44	close (fd);
45      return NULL;
46    }
47
48  GElf_Addr start = 0, end = 0, bias = 0;
49  switch (ehdr->e_type)
50    {
51    case ET_REL:
52      /* For a relocatable object, we do an arbitrary section layout.
53	 By updating the section header in place, we leave the layout
54	 information to be found by relocation.  */
55
56      start = end = base;
57
58      Elf_Scn *scn = NULL;
59      while ((scn = elf_nextscn (elf, scn)) != NULL)
60	{
61	  GElf_Shdr shdr_mem;
62	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
63	  if (shdr == NULL)
64	    goto elf_error;
65
66	  if (shdr->sh_flags & SHF_ALLOC)
67	    {
68	      const GElf_Xword align = shdr->sh_addralign ?: 1;
69	      shdr->sh_addr = (end + align - 1) & -align;
70	      if (end == base)
71		/* This is the first section assigned a location.
72		   Use its aligned address as the module's base.  */
73		start = shdr->sh_addr;
74	      end = shdr->sh_addr + shdr->sh_size;
75	      if (! gelf_update_shdr (scn, shdr))
76		goto elf_error;
77	    }
78	}
79
80      if (end == start)
81	{
82	  __libdwfl_seterrno (DWFL_E_BADELF);
83	  if (closefd)
84	    close (fd);
85	  return NULL;
86	}
87      break;
88
89      /* Everything else has to have program headers.  */
90
91    case ET_EXEC:
92    case ET_CORE:
93      /* An assigned base address is meaningless for these.  */
94      base = 0;
95
96    case ET_DYN:
97    default:
98      for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i)
99	{
100	  GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
101	  if (ph == NULL)
102	    goto elf_error;
103	  if (ph->p_type == PT_LOAD)
104	    {
105	      if ((base & (ph->p_align - 1)) != 0)
106		base = (base + ph->p_align - 1) & -ph->p_align;
107	      start = base + (ph->p_vaddr & -ph->p_align);
108	      break;
109	    }
110	}
111      bias = base;
112
113      for (uint_fast16_t i = ehdr->e_phnum; i-- > 0;)
114	{
115	  GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
116	  if (ph == NULL)
117	    goto elf_error;
118	  if (ph->p_type == PT_LOAD)
119	    {
120	      end = base + (ph->p_vaddr + ph->p_memsz);
121	      break;
122	    }
123	}
124
125      if (end == 0)
126	{
127	  __libdwfl_seterrno (DWFL_E_NO_PHDR);
128	  if (closefd)
129	    close (fd);
130	  return NULL;
131	}
132      break;
133    }
134
135  Dwfl_Module *m = INTUSE(dwfl_report_module) (dwfl, name, start, end);
136  if (m != NULL)
137    {
138      if (m->main.name == NULL)
139	{
140	  m->main.name = strdup (file_name);
141	  m->main.fd = fd;
142	}
143      else if ((fd >= 0 && m->main.fd != fd)
144	       || strcmp (m->main.name, file_name))
145	{
146	  elf_end (elf);
147	overlap:
148	  if (closefd)
149	    close (fd);
150	  m->gc = true;
151	  __libdwfl_seterrno (DWFL_E_OVERLAP);
152	  m = NULL;
153	}
154
155      /* Preinstall the open ELF handle for the module.  */
156      if (m->main.elf == NULL)
157	{
158	  m->main.elf = elf;
159	  m->main.bias = bias;
160	  m->e_type = ehdr->e_type;
161	}
162      else
163	{
164	  elf_end (elf);
165	  if (m->main.bias != base)
166	    goto overlap;
167	}
168    }
169  return m;
170}
171INTDEF (dwfl_report_elf)
172