1/*-
2 * Copyright (c) 2009 Kai Wang
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include "_libdwarf.h"
28
29ELFTC_VCSID("$Id: libdwarf_elf_init.c 3475 2016-05-18 18:11:26Z emaste $");
30
31static const char *debug_name[] = {
32	".debug_abbrev",
33	".debug_aranges",
34	".debug_frame",
35	".debug_info",
36	".debug_types",
37	".debug_line",
38	".debug_pubnames",
39	".eh_frame",
40	".debug_macinfo",
41	".debug_str",
42	".debug_loc",
43	".debug_pubtypes",
44	".debug_ranges",
45	".debug_static_func",
46	".debug_static_vars",
47	".debug_typenames",
48	".debug_weaknames",
49	NULL
50};
51
52static void
53_dwarf_elf_apply_rel_reloc(Dwarf_Debug dbg, void *buf, uint64_t bufsize,
54    Elf_Data *rel_data, Elf_Data *symtab_data, int endian)
55{
56	Dwarf_Unsigned type;
57	GElf_Rel rel;
58	GElf_Sym sym;
59	size_t symndx;
60	uint64_t offset;
61	uint64_t addend;
62	int size, j;
63
64	j = 0;
65	while (gelf_getrel(rel_data, j++, &rel) != NULL) {
66		symndx = GELF_R_SYM(rel.r_info);
67		type = GELF_R_TYPE(rel.r_info);
68
69		if (gelf_getsym(symtab_data, symndx, &sym) == NULL)
70			continue;
71
72		size = _dwarf_get_reloc_size(dbg, type);
73		if (size == 0)
74			continue; /* Unknown or non-absolute relocation. */
75
76		offset = rel.r_offset;
77		if (offset + size >= bufsize)
78			continue;
79
80		if (endian == ELFDATA2MSB)
81			addend = _dwarf_read_msb(buf, &offset, size);
82		else
83			addend = _dwarf_read_lsb(buf, &offset, size);
84
85		offset = rel.r_offset;
86		if (endian == ELFDATA2MSB)
87			_dwarf_write_msb(buf, &offset, sym.st_value + addend,
88			    size);
89		else
90			_dwarf_write_lsb(buf, &offset, sym.st_value + addend,
91			    size);
92	}
93}
94
95static void
96_dwarf_elf_apply_rela_reloc(Dwarf_Debug dbg, void *buf, uint64_t bufsize,
97    Elf_Data *rel_data, Elf_Data *symtab_data, int endian)
98{
99	Dwarf_Unsigned type;
100	GElf_Rela rela;
101	GElf_Sym sym;
102	size_t symndx;
103	uint64_t offset;
104	int size, j;
105
106	j = 0;
107	while (gelf_getrela(rel_data, j++, &rela) != NULL) {
108		symndx = GELF_R_SYM(rela.r_info);
109		type = GELF_R_TYPE(rela.r_info);
110
111		if (gelf_getsym(symtab_data, symndx, &sym) == NULL)
112			continue;
113
114		offset = rela.r_offset;
115		size = _dwarf_get_reloc_size(dbg, type);
116		if (size == 0)
117			continue; /* Unknown or non-absolute relocation. */
118		if (offset + size >= bufsize)
119			continue;
120
121		if (endian == ELFDATA2MSB)
122			_dwarf_write_msb(buf, &offset,
123			    sym.st_value + rela.r_addend, size);
124		else
125			_dwarf_write_lsb(buf, &offset,
126			    sym.st_value + rela.r_addend, size);
127	}
128}
129
130static int
131_dwarf_elf_relocate(Dwarf_Debug dbg, Elf *elf, Dwarf_Elf_Data *ed, size_t shndx,
132    size_t symtab, Elf_Data *symtab_data, Dwarf_Error *error)
133{
134	GElf_Ehdr eh;
135	GElf_Shdr sh;
136	Elf_Scn *scn;
137	Elf_Data *rel;
138	int elferr;
139
140	if (symtab == 0 || symtab_data == NULL)
141		return (DW_DLE_NONE);
142
143	if (gelf_getehdr(elf, &eh) == NULL) {
144		DWARF_SET_ELF_ERROR(dbg, error);
145		return (DW_DLE_ELF);
146	}
147
148	scn = NULL;
149	(void) elf_errno();
150	while ((scn = elf_nextscn(elf, scn)) != NULL) {
151		if (gelf_getshdr(scn, &sh) == NULL) {
152			DWARF_SET_ELF_ERROR(dbg, error);
153			return (DW_DLE_ELF);
154		}
155
156		if ((sh.sh_type != SHT_REL && sh.sh_type != SHT_RELA) ||
157		     sh.sh_size == 0)
158			continue;
159
160		if (sh.sh_info == shndx && sh.sh_link == symtab) {
161			if ((rel = elf_getdata(scn, NULL)) == NULL) {
162				elferr = elf_errno();
163				if (elferr != 0) {
164					_DWARF_SET_ERROR(NULL, error,
165					    DW_DLE_ELF, elferr);
166					return (DW_DLE_ELF);
167				} else
168					return (DW_DLE_NONE);
169			}
170
171			ed->ed_alloc = malloc(ed->ed_data->d_size);
172			if (ed->ed_alloc == NULL) {
173				DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY);
174				return (DW_DLE_MEMORY);
175			}
176			memcpy(ed->ed_alloc, ed->ed_data->d_buf,
177			    ed->ed_data->d_size);
178			if (sh.sh_type == SHT_REL)
179				_dwarf_elf_apply_rel_reloc(dbg,
180				    ed->ed_alloc, ed->ed_data->d_size,
181				    rel, symtab_data, eh.e_ident[EI_DATA]);
182			else
183				_dwarf_elf_apply_rela_reloc(dbg,
184				    ed->ed_alloc, ed->ed_data->d_size,
185				    rel, symtab_data, eh.e_ident[EI_DATA]);
186
187			return (DW_DLE_NONE);
188		}
189	}
190	elferr = elf_errno();
191	if (elferr != 0) {
192		DWARF_SET_ELF_ERROR(dbg, error);
193		return (DW_DLE_ELF);
194	}
195
196	return (DW_DLE_NONE);
197}
198
199int
200_dwarf_elf_init(Dwarf_Debug dbg, Elf *elf, Dwarf_Error *error)
201{
202	Dwarf_Obj_Access_Interface *iface;
203	Dwarf_Elf_Object *e;
204	const char *name;
205	GElf_Shdr sh;
206	Elf_Scn *scn;
207	Elf_Data *symtab_data;
208	size_t symtab_ndx;
209	int elferr, i, j, n, ret;
210
211	ret = DW_DLE_NONE;
212
213	if ((iface = calloc(1, sizeof(*iface))) == NULL) {
214		DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY);
215		return (DW_DLE_MEMORY);
216	}
217
218	if ((e = calloc(1, sizeof(*e))) == NULL) {
219		free(iface);
220		DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY);
221		return (DW_DLE_MEMORY);
222	}
223
224	e->eo_elf = elf;
225	e->eo_methods.get_section_info = _dwarf_elf_get_section_info;
226	e->eo_methods.get_byte_order = _dwarf_elf_get_byte_order;
227	e->eo_methods.get_length_size = _dwarf_elf_get_length_size;
228	e->eo_methods.get_pointer_size = _dwarf_elf_get_pointer_size;
229	e->eo_methods.get_section_count = _dwarf_elf_get_section_count;
230	e->eo_methods.load_section = _dwarf_elf_load_section;
231
232	iface->object = e;
233	iface->methods = &e->eo_methods;
234
235	dbg->dbg_iface = iface;
236
237	if (gelf_getehdr(elf, &e->eo_ehdr) == NULL) {
238		DWARF_SET_ELF_ERROR(dbg, error);
239		ret = DW_DLE_ELF;
240		goto fail_cleanup;
241	}
242
243	dbg->dbg_machine = e->eo_ehdr.e_machine;
244
245	if (!elf_getshstrndx(elf, &e->eo_strndx)) {
246		DWARF_SET_ELF_ERROR(dbg, error);
247		ret = DW_DLE_ELF;
248		goto fail_cleanup;
249	}
250
251	n = 0;
252	symtab_ndx = 0;
253	symtab_data = NULL;
254	scn = NULL;
255	(void) elf_errno();
256	while ((scn = elf_nextscn(elf, scn)) != NULL) {
257		if (gelf_getshdr(scn, &sh) == NULL) {
258			DWARF_SET_ELF_ERROR(dbg, error);
259			ret = DW_DLE_ELF;
260			goto fail_cleanup;
261		}
262
263		if ((name = elf_strptr(elf, e->eo_strndx, sh.sh_name)) ==
264		    NULL) {
265			DWARF_SET_ELF_ERROR(dbg, error);
266			ret = DW_DLE_ELF;
267			goto fail_cleanup;
268		}
269
270		if (!strcmp(name, ".symtab")) {
271			symtab_ndx = elf_ndxscn(scn);
272			if ((symtab_data = elf_getdata(scn, NULL)) == NULL) {
273				elferr = elf_errno();
274				if (elferr != 0) {
275					_DWARF_SET_ERROR(NULL, error,
276					    DW_DLE_ELF, elferr);
277					ret = DW_DLE_ELF;
278					goto fail_cleanup;
279				}
280			}
281			continue;
282		}
283
284		for (i = 0; debug_name[i] != NULL; i++) {
285			if (!strcmp(name, debug_name[i]))
286				n++;
287		}
288	}
289	elferr = elf_errno();
290	if (elferr != 0) {
291		DWARF_SET_ELF_ERROR(dbg, error);
292		return (DW_DLE_ELF);
293	}
294
295	e->eo_seccnt = n;
296
297	if (n == 0)
298		return (DW_DLE_NONE);
299
300	if ((e->eo_data = calloc(n, sizeof(Dwarf_Elf_Data))) == NULL ||
301	    (e->eo_shdr = calloc(n, sizeof(GElf_Shdr))) == NULL) {
302		DWARF_SET_ERROR(NULL, error, DW_DLE_MEMORY);
303		ret = DW_DLE_MEMORY;
304		goto fail_cleanup;
305	}
306
307	scn = NULL;
308	j = 0;
309	while ((scn = elf_nextscn(elf, scn)) != NULL && j < n) {
310		if (gelf_getshdr(scn, &sh) == NULL) {
311			DWARF_SET_ELF_ERROR(dbg, error);
312			ret = DW_DLE_ELF;
313			goto fail_cleanup;
314		}
315
316		memcpy(&e->eo_shdr[j], &sh, sizeof(sh));
317
318		if ((name = elf_strptr(elf, e->eo_strndx, sh.sh_name)) ==
319		    NULL) {
320			DWARF_SET_ELF_ERROR(dbg, error);
321			ret = DW_DLE_ELF;
322			goto fail_cleanup;
323		}
324
325		for (i = 0; debug_name[i] != NULL; i++) {
326			if (strcmp(name, debug_name[i]))
327				continue;
328
329			(void) elf_errno();
330			if ((e->eo_data[j].ed_data = elf_getdata(scn, NULL)) ==
331			    NULL) {
332				elferr = elf_errno();
333				if (elferr != 0) {
334					_DWARF_SET_ERROR(dbg, error,
335					    DW_DLE_ELF, elferr);
336					ret = DW_DLE_ELF;
337					goto fail_cleanup;
338				}
339			}
340
341			if (_libdwarf.applyreloc) {
342				if (_dwarf_elf_relocate(dbg, elf,
343				    &e->eo_data[j], elf_ndxscn(scn), symtab_ndx,
344				    symtab_data, error) != DW_DLE_NONE)
345					goto fail_cleanup;
346			}
347
348			j++;
349		}
350	}
351
352	assert(j == n);
353
354	return (DW_DLE_NONE);
355
356fail_cleanup:
357
358	_dwarf_elf_deinit(dbg);
359
360	return (ret);
361}
362
363void
364_dwarf_elf_deinit(Dwarf_Debug dbg)
365{
366	Dwarf_Obj_Access_Interface *iface;
367	Dwarf_Elf_Object *e;
368	int i;
369
370	iface = dbg->dbg_iface;
371	assert(iface != NULL);
372
373	e = iface->object;
374	assert(e != NULL);
375
376	if (e->eo_data) {
377		for (i = 0; (Dwarf_Unsigned) i < e->eo_seccnt; i++) {
378			if (e->eo_data[i].ed_alloc)
379				free(e->eo_data[i].ed_alloc);
380		}
381		free(e->eo_data);
382	}
383	if (e->eo_shdr)
384		free(e->eo_shdr);
385
386	free(e);
387	free(iface);
388
389	dbg->dbg_iface = NULL;
390}
391