1/*
2 * This file is part of ltrace.
3 * Copyright (C) 2013 Petr Machata, Red Hat Inc.
4 * Copyright (C) 2010 Zach Welch, CodeSourcery
5 * Copyright (C) 2004,2008,2009 Juan Cespedes
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 */
22
23#include <gelf.h>
24#include <stdio.h>
25#include <string.h>
26
27#include "proc.h"
28#include "library.h"
29#include "ltrace-elf.h"
30
31static int
32get_hardfp(uint64_t abi_vfp_args)
33{
34	if (abi_vfp_args == 2)
35		fprintf(stderr,
36			"Tag_ABI_VFP_args value 2 (tool chain-specific "
37			"conventions) not supported.\n");
38	return abi_vfp_args == 1;
39}
40
41int
42arch_elf_init(struct ltelf *lte, struct library *lib)
43{
44	GElf_Addr jmprel_addr;
45	Elf_Scn *jmprel_sec;
46	GElf_Shdr jmprel_shdr;
47	if (elf_load_dynamic_entry(lte, DT_JMPREL, &jmprel_addr) < 0
48	    || elf_get_section_covering(lte, jmprel_addr,
49					&jmprel_sec, &jmprel_shdr) < 0
50	    || jmprel_sec == NULL)
51		return -1;
52
53	lte->arch.jmprel_data = elf_loaddata(jmprel_sec, &jmprel_shdr);
54	if (lte->arch.jmprel_data == NULL)
55		return -1;
56
57	/* Nothing in this section is strictly critical.  It's not
58	 * that much of a deal if we fail to guess right whether the
59	 * ABI is softfp or hardfp.  */
60	unsigned hardfp = 0;
61
62	Elf_Scn *scn;
63	Elf_Data *data;
64	GElf_Shdr shdr;
65	if (elf_get_section_type(lte, SHT_ARM_ATTRIBUTES, &scn, &shdr) < 0
66	    || (scn != NULL && (data = elf_loaddata(scn, &shdr)) == NULL)) {
67		fprintf(stderr,
68			"Error when obtaining ARM attribute section: %s\n",
69			elf_errmsg(-1));
70		goto done;
71
72	} else if (scn != NULL && data != NULL) {
73		GElf_Xword offset = 0;
74		uint8_t version;
75		if (elf_read_next_u8(data, &offset, &version) < 0) {
76			goto done;
77		} else if (version != 'A') {
78			fprintf(stderr, "Unsupported ARM attribute section "
79				"version %d ('%c').\n", version, version);
80			goto done;
81		}
82
83		do {
84			const char signature[] = "aeabi";
85			/* N.B. LEN is including the length field
86			 * itself.  */
87			uint32_t sec_len;
88			if (elf_read_u32(data, offset, &sec_len) < 0
89			    || !elf_can_read_next(data, offset, sec_len)) {
90				goto done;
91			}
92			const GElf_Xword next_offset = offset + sec_len;
93			offset += 4;
94
95			if (sec_len < 4 + sizeof signature
96			    || strcmp(signature, data->d_buf + offset) != 0)
97				goto skip;
98			offset += sizeof signature;
99
100			const GElf_Xword offset0 = offset;
101			uint64_t tag;
102			uint32_t sub_len;
103			if (elf_read_next_uleb128(data, &offset, &tag) < 0
104			    || elf_read_next_u32(data, &offset, &sub_len) < 0
105			    || !elf_can_read_next(data, offset0, sub_len))
106				goto done;
107
108			if (tag != 1)
109				/* IHI0045D_ABI_addenda: "section and
110				 * symbol attributes are deprecated
111				 * [...] consumers are permitted to
112				 * ignore them."  */
113				goto skip;
114
115			while (offset < offset0 + sub_len) {
116				if (elf_read_next_uleb128(data,
117							  &offset, &tag) < 0)
118					goto done;
119
120				switch (tag) {
121					uint64_t v;
122				case 6: /* Tag_CPU_arch */
123				case 7: /* Tag_CPU_arch_profile */
124				case 8: /* Tag_ARM_ISA_use */
125				case 9: /* Tag_THUMB_ISA_use */
126				case 10: /* Tag_FP_arch */
127				case 11: /* Tag_WMMX_arch */
128				case 12: /* Tag_Advanced_SIMD_arch */
129				case 13: /* Tag_PCS_config */
130				case 14: /* Tag_ABI_PCS_R9_use */
131				case 15: /* Tag_ABI_PCS_RW_data */
132				case 16: /* Tag_ABI_PCS_RO_data */
133				case 17: /* Tag_ABI_PCS_GOT_use */
134				case 18: /* Tag_ABI_PCS_wchar_t */
135				case 19: /* Tag_ABI_FP_rounding */
136				case 20: /* Tag_ABI_FP_denormal */
137				case 21: /* Tag_ABI_FP_exceptions */
138				case 22: /* Tag_ABI_FP_user_exceptions */
139				case 23: /* Tag_ABI_FP_number_model */
140				case 24: /* Tag_ABI_align_needed */
141				case 25: /* Tag_ABI_align_preserved */
142				case 26: /* Tag_ABI_enum_size */
143				case 27: /* Tag_ABI_HardFP_use */
144				case 28: /* Tag_ABI_VFP_args */
145				case 29: /* Tag_ABI_WMMX_args */
146				case 30: /* Tag_ABI_optimization_goals */
147				case 31: /* Tag_ABI_FP_optimization_goals */
148				case 32: /* Tag_compatibility */
149				case 34: /* Tag_CPU_unaligned_access */
150				case 36: /* Tag_FP_HP_extension */
151				case 38: /* Tag_ABI_FP_16bit_format */
152				case 42: /* Tag_MPextension_use */
153				case 70: /* Tag_MPextension_use as well */
154				case 44: /* Tag_DIV_use */
155				case 64: /* Tag_nodefaults */
156				case 66: /* Tag_T2EE_use */
157				case 68: /* Tag_Virtualization_use */
158				uleb128:
159					if (elf_read_next_uleb128
160						(data, &offset, &v) < 0)
161						goto done;
162					if (tag == 28)
163						hardfp = get_hardfp(v);
164					if (tag != 32)
165						continue;
166
167					/* Tag 32 has two arguments,
168					 * fall through.  */
169
170				case 4:	/* Tag_CPU_raw_name */
171				case 5:	/* Tag_CPU_name */
172				case 65: /* Tag_also_compatible_with */
173				case 67: /* Tag_conformance */
174				ntbs:
175					offset += strlen(data->d_buf
176							 + offset) + 1;
177					continue;
178				}
179
180				/* Handle unknown tags in a generic
181				 * manner, if possible.  */
182				if (tag <= 32) {
183					fprintf(stderr,
184						"Unknown tag %lld "
185						"at offset %#llx "
186						"of ARM attribute section.",
187						tag, offset);
188					goto skip;
189				} else if (tag % 2 == 0) {
190					goto uleb128;
191				} else {
192					goto ntbs;
193				}
194			}
195
196		skip:
197			offset = next_offset;
198
199		} while (elf_can_read_next(data, offset, 1));
200
201	}
202
203done:
204	lib->arch.hardfp = hardfp;
205	return 0;
206}
207
208void
209arch_elf_destroy(struct ltelf *lte)
210{
211}
212
213static int
214arch_plt_entry_has_stub(struct ltelf *lte, size_t off) {
215	char *buf = (char *) lte->arch.jmprel_data->d_buf;
216	uint16_t op = *(uint16_t *) (buf + off);
217	return op == 0x4778;
218}
219
220GElf_Addr
221arch_plt_sym_val(struct ltelf *lte, size_t ndx, GElf_Rela * rela) {
222	size_t start = lte->arch.jmprel_data->d_size + 12;
223	size_t off = start + 20, i;
224	for (i = 0; i < ndx; i++)
225		off += arch_plt_entry_has_stub(lte, off) ? 16 : 12;
226	if (arch_plt_entry_has_stub(lte, off))
227		off += 4;
228	return lte->plt_addr + off - start;
229}
230
231void *
232sym2addr(struct process *proc, struct library_symbol *sym)
233{
234	return sym->enter_addr;
235}
236
237int
238arch_library_init(struct library *lib)
239{
240	return 0;
241}
242
243void
244arch_library_destroy(struct library *lib)
245{
246}
247
248int
249arch_library_clone(struct library *retp, struct library *lib)
250{
251	retp->arch = lib->arch;
252	return 0;
253}
254