]>
Commit | Line | Data |
---|---|---|
32d0add0 | 1 | /* Copyright 2013-2015 Free Software Foundation, Inc. |
9772d074 UW |
2 | |
3 | This file is part of GDB. | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 3 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
17 | ||
18 | #include <errno.h> | |
19 | #include <stdarg.h> | |
20 | #include <stdint.h> | |
21 | #include <stdio.h> | |
22 | #include <stdlib.h> | |
23 | ||
24 | typedef struct | |
25 | { | |
26 | unsigned char e_ident[16]; | |
27 | uint16_t e_type; | |
28 | uint16_t e_machine; | |
29 | uint32_t e_version; | |
30 | uint32_t e_entry; | |
31 | uint32_t e_phoff; | |
32 | uint32_t e_shoff; | |
33 | uint32_t e_flags; | |
34 | uint16_t e_ehsize; | |
35 | uint16_t e_phentsize; | |
36 | uint16_t e_phnum; | |
37 | uint16_t e_shentsize; | |
38 | uint16_t e_shnum; | |
39 | uint16_t e_shstrndx; | |
40 | } Elf32_Ehdr; | |
41 | ||
42 | typedef struct | |
43 | { | |
44 | unsigned char e_ident[16]; | |
45 | uint16_t e_type; | |
46 | uint16_t e_machine; | |
47 | uint32_t e_version; | |
48 | uint64_t e_entry; | |
49 | uint64_t e_phoff; | |
50 | uint64_t e_shoff; | |
51 | uint32_t e_flags; | |
52 | uint16_t e_ehsize; | |
53 | uint16_t e_phentsize; | |
54 | uint16_t e_phnum; | |
55 | uint16_t e_shentsize; | |
56 | uint16_t e_shnum; | |
57 | uint16_t e_shstrndx; | |
58 | } Elf64_Ehdr; | |
59 | ||
60 | typedef struct | |
61 | { | |
62 | uint32_t p_type; | |
63 | uint32_t p_offset; | |
64 | uint32_t p_vaddr; | |
65 | uint32_t p_paddr; | |
66 | uint32_t p_filesz; | |
67 | uint32_t p_memsz; | |
68 | uint32_t p_flags; | |
69 | uint32_t p_align; | |
70 | } Elf32_Phdr; | |
71 | ||
72 | typedef struct | |
73 | { | |
74 | uint32_t p_type; | |
75 | uint32_t p_flags; | |
76 | uint64_t p_offset; | |
77 | uint64_t p_vaddr; | |
78 | uint64_t p_paddr; | |
79 | uint64_t p_filesz; | |
80 | uint64_t p_memsz; | |
81 | uint64_t p_align; | |
82 | } Elf64_Phdr; | |
83 | ||
84 | struct elfbuf | |
85 | { | |
86 | const char *path; | |
87 | unsigned char *buf; | |
88 | size_t len; | |
89 | enum { ELFCLASS32 = 1, | |
90 | ELFCLASS64 = 2 } ei_class; | |
91 | }; | |
92 | ||
93 | #define ELFBUF_EHDR_LEN(elf) \ | |
94 | ((elf)->ei_class == ELFCLASS32 ? sizeof (Elf32_Ehdr) : \ | |
95 | sizeof (Elf64_Ehdr)) | |
96 | ||
97 | #define ELFBUF_EHDR(elf, memb) \ | |
98 | ((elf)->ei_class == ELFCLASS32 ? \ | |
99 | ((Elf32_Ehdr *) (elf)->buf)->memb \ | |
100 | : ((Elf64_Ehdr *) (elf)->buf)->memb) | |
101 | ||
102 | #define ELFBUF_PHDR_LEN(elf) \ | |
103 | ((elf)->ei_class == ELFCLASS32 ? sizeof (Elf32_Phdr) : \ | |
104 | sizeof (Elf64_Phdr)) | |
105 | ||
106 | #define ELFBUF_PHDR(elf, idx, memb) \ | |
107 | ((elf)->ei_class == ELFCLASS32 ? \ | |
108 | ((Elf32_Phdr *) &(elf)->buf[((Elf32_Ehdr *)(elf)->buf) \ | |
109 | ->e_phoff])[idx].memb \ | |
110 | : ((Elf64_Phdr *) &(elf)->buf[((Elf64_Ehdr *)(elf)->buf) \ | |
111 | ->e_phoff])[idx].memb) | |
112 | ||
113 | static void | |
114 | exit_with_msg(const char *fmt, ...) | |
115 | { | |
116 | va_list ap; | |
117 | ||
118 | fflush (stdout); | |
119 | va_start (ap, fmt); | |
120 | vfprintf (stderr, fmt, ap); | |
121 | va_end (ap); | |
122 | ||
123 | if (errno) | |
124 | { | |
125 | fputs (": ", stderr); | |
126 | perror (NULL); | |
127 | } | |
128 | else | |
129 | fputc ('\n', stderr); | |
130 | exit (1); | |
131 | } | |
132 | ||
133 | static void | |
134 | read_file (unsigned char **buf_ptr, size_t *len_ptr, FILE *fp) | |
135 | { | |
136 | size_t len = 0; | |
137 | size_t size = 1024; | |
138 | size_t chunk; | |
139 | unsigned char *buf = malloc (size); | |
140 | ||
141 | while ((chunk = fread (buf + len, 1, size - len, fp)) == size - len) | |
142 | { | |
143 | len = size; | |
144 | size *= 2; | |
145 | buf = realloc (buf, size); | |
146 | } | |
147 | len += chunk; | |
148 | *buf_ptr = buf; | |
149 | *len_ptr = len; | |
150 | } | |
151 | ||
152 | static void | |
153 | write_file (unsigned char *buf, size_t len, FILE *fp) | |
154 | { | |
155 | fwrite (buf, 1, len, fp); | |
156 | } | |
157 | ||
158 | static void | |
159 | elfbuf_init_from_file (struct elfbuf *elf, const char *path) | |
160 | { | |
161 | FILE *fp = fopen (path, "rb"); | |
162 | unsigned char *buf; | |
163 | size_t len; | |
164 | ||
165 | if (fp == NULL) | |
166 | exit_with_msg ("%s", path); | |
167 | ||
168 | read_file (&buf, &len, fp); | |
169 | fclose (fp); | |
170 | ||
171 | /* Validate ELF identification. */ | |
172 | if (len < 16 | |
173 | || buf[0] != 0x7f || buf[1] != 0x45 || buf[2] != 0x4c || buf[3] != 0x46 | |
174 | || buf[4] < 1 || buf[4] > 2 || buf[5] < 1 || buf[5] > 2) | |
175 | exit_with_msg ("%s: unsupported or invalid ELF file", path); | |
176 | ||
177 | elf->path = path; | |
178 | elf->buf = buf; | |
179 | elf->len = len; | |
180 | elf->ei_class = buf[4]; | |
181 | ||
182 | if (ELFBUF_EHDR_LEN (elf) > len | |
183 | || ELFBUF_EHDR (elf, e_phoff) > len | |
184 | || ELFBUF_EHDR (elf, e_phnum) > ((len - ELFBUF_EHDR (elf, e_phoff)) | |
185 | / ELFBUF_PHDR_LEN (elf)) ) | |
186 | exit_with_msg ("%s: unexpected end of data", path); | |
187 | ||
188 | if (ELFBUF_EHDR (elf, e_phentsize) != ELFBUF_PHDR_LEN (elf)) | |
189 | exit_with_msg ("%s: inconsistent ELF header", path); | |
190 | } | |
191 | ||
192 | static void | |
193 | elfbuf_write_to_file (struct elfbuf *elf, const char *path) | |
194 | { | |
195 | FILE *fp = fopen (path, "wb"); | |
196 | ||
197 | if (fp == NULL) | |
198 | exit_with_msg ("%s", path); | |
199 | ||
200 | write_file (elf->buf, elf->len, fp); | |
201 | fclose (fp); | |
202 | } | |
203 | ||
204 | /* In the auxv note starting at OFFSET with size LEN, mask the hwcap | |
205 | field using the HWCAP_MASK. */ | |
206 | ||
207 | static void | |
208 | elfbuf_handle_auxv (struct elfbuf *elf, size_t offset, size_t len, | |
209 | unsigned long hwcap_mask) | |
210 | { | |
211 | size_t i; | |
212 | uint32_t *auxv32 = (uint32_t *) (elf->buf + offset); | |
213 | uint64_t *auxv64 = (uint64_t *) auxv32; | |
214 | size_t entry_size = elf->ei_class == ELFCLASS32 ? | |
215 | sizeof (auxv32[0]) : sizeof (auxv64[0]); | |
216 | ||
217 | for (i = 0; i < len / entry_size; i++) | |
218 | { | |
219 | uint64_t auxv_type = elf->ei_class == ELFCLASS32 ? | |
220 | auxv32[2 * i] : auxv64[2 * i]; | |
221 | ||
222 | if (auxv_type == 0) | |
223 | break; | |
224 | if (auxv_type != 16) | |
225 | continue; | |
226 | ||
227 | if (elf->ei_class == ELFCLASS32) | |
228 | auxv32[2 * i + 1] &= (uint32_t) hwcap_mask; | |
229 | else | |
230 | auxv64[2 * i + 1] &= (uint64_t) hwcap_mask; | |
231 | } | |
232 | } | |
233 | ||
234 | /* In the note segment starting at OFFSET with size LEN, make notes | |
235 | with type NOTE_TYPE unrecognizable by GDB. Also, mask the hwcap | |
236 | field of any auxv notes using the HWCAP_MASK. */ | |
237 | ||
238 | static void | |
239 | elfbuf_handle_note_segment (struct elfbuf *elf, size_t offset, size_t len, | |
240 | unsigned note_type, unsigned long hwcap_mask) | |
241 | { | |
242 | size_t pos = 0; | |
243 | ||
244 | while (pos + 12 < len) | |
245 | { | |
246 | uint32_t *note = (uint32_t *) (elf->buf + offset + pos); | |
247 | size_t desc_pos = pos + 12 + ((note[0] + 3) & ~3); | |
248 | size_t next_pos = desc_pos + ((note[1] + 3) & ~3); | |
249 | ||
250 | if (desc_pos > len || next_pos > len) | |
251 | exit_with_msg ("%s: corrupt notes data", elf->path); | |
252 | ||
253 | if (note[2] == note_type) | |
254 | note[2] |= 0xff000000; | |
255 | else if (note[2] == 6 && hwcap_mask != 0) | |
256 | elfbuf_handle_auxv (elf, offset + desc_pos, note[1], | |
257 | hwcap_mask); | |
258 | pos = next_pos; | |
259 | } | |
260 | } | |
261 | ||
262 | static void | |
263 | elfbuf_handle_core_notes (struct elfbuf *elf, unsigned note_type, | |
264 | unsigned long hwcap_mask) | |
265 | { | |
266 | unsigned ph_idx; | |
267 | ||
268 | if (ELFBUF_EHDR (elf, e_type) != 4) | |
269 | exit_with_msg ("%s: not a core file", elf->path); | |
270 | ||
271 | /* Iterate over program headers. */ | |
272 | for (ph_idx = 0; ph_idx != ELFBUF_EHDR (elf, e_phnum); ph_idx++) | |
273 | { | |
274 | size_t offset = ELFBUF_PHDR (elf, ph_idx, p_offset); | |
275 | size_t filesz = ELFBUF_PHDR (elf, ph_idx, p_filesz); | |
276 | ||
277 | if (offset > elf->len || filesz > elf->len - offset) | |
278 | exit_with_msg ("%s: unexpected end of data", elf->path); | |
279 | ||
280 | /* Deal with NOTE segments only. */ | |
281 | if (ELFBUF_PHDR (elf, ph_idx, p_type) != 4) | |
282 | continue; | |
283 | elfbuf_handle_note_segment (elf, offset, filesz, note_type, | |
284 | hwcap_mask); | |
285 | } | |
286 | } | |
287 | ||
288 | int | |
289 | main (int argc, char *argv[]) | |
290 | { | |
291 | unsigned note_type; | |
292 | unsigned long hwcap_mask = 0; | |
293 | struct elfbuf elf; | |
294 | ||
295 | if (argc < 4) | |
296 | { | |
297 | abort (); | |
298 | } | |
299 | ||
300 | if (sscanf (argv[3], "%u", ¬e_type) != 1) | |
301 | exit_with_msg ("%s: bad command line arguments\n", argv[0]); | |
302 | ||
303 | if (argc >= 5) | |
304 | { | |
305 | if (sscanf (argv[4], "%lu", &hwcap_mask) != 1) | |
306 | exit_with_msg ("%s: bad command line arguments\n", argv[0]); | |
307 | } | |
308 | ||
309 | elfbuf_init_from_file (&elf, argv[1]); | |
310 | elfbuf_handle_core_notes (&elf, note_type, hwcap_mask); | |
311 | elfbuf_write_to_file (&elf, argv[2]); | |
312 | ||
313 | return 0; | |
314 | } |