|
1 /* |
|
2 * begin.c - implementation of the elf_begin(3) and elf_memory(3) functions. |
|
3 * Copyright (C) 1995 - 2004 Michael Riepe |
|
4 * |
|
5 * This library is free software; you can redistribute it and/or |
|
6 * modify it under the terms of the GNU Library General Public |
|
7 * License as published by the Free Software Foundation; either |
|
8 * version 2 of the License, or (at your option) any later version. |
|
9 * |
|
10 * This library 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 GNU |
|
13 * Library General Public License for more details. |
|
14 * |
|
15 * You should have received a copy of the GNU Library General Public |
|
16 * License along with this library; if not, write to the Free Software |
|
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
18 */ |
|
19 |
|
20 #include <private.h> |
|
21 |
|
22 #ifndef lint |
|
23 static const char rcsid[] = "@(#) $Id: begin.c,v 1.20 2006/08/17 23:59:58 michael Exp $"; |
|
24 #endif /* lint */ |
|
25 |
|
26 #if HAVE_AR_H |
|
27 #include <ar.h> |
|
28 #else /* HAVE_AR_H */ |
|
29 |
|
30 #define ARMAG "!<arch>\n" |
|
31 #define SARMAG 8 |
|
32 |
|
33 struct ar_hdr { |
|
34 char ar_name[16]; |
|
35 char ar_date[12]; |
|
36 char ar_uid[6]; |
|
37 char ar_gid[6]; |
|
38 char ar_mode[8]; |
|
39 char ar_size[10]; |
|
40 char ar_fmag[2]; |
|
41 }; |
|
42 |
|
43 #define ARFMAG "`\n" |
|
44 |
|
45 #endif /* HAVE_AR_H */ |
|
46 |
|
47 static const Elf _elf_init = INIT_ELF; |
|
48 static const char fmag[] = ARFMAG; |
|
49 |
|
50 static unsigned long |
|
51 getnum(const char *str, size_t len, int base, size_t *err) { |
|
52 unsigned long result = 0; |
|
53 |
|
54 while (len && *str == ' ') { |
|
55 str++; len--; |
|
56 } |
|
57 while (len && *str >= '0' && (*str - '0') < base) { |
|
58 result = base * result + *str++ - '0'; len--; |
|
59 } |
|
60 while (len && *str == ' ') { |
|
61 str++; len--; |
|
62 } |
|
63 if (len) { |
|
64 *err = len; |
|
65 } |
|
66 return result; |
|
67 } |
|
68 |
|
69 static void |
|
70 _elf_init_ar(Elf *elf) { |
|
71 struct ar_hdr *hdr; |
|
72 size_t offset; |
|
73 size_t size; |
|
74 size_t err = 0; |
|
75 |
|
76 elf->e_kind = ELF_K_AR; |
|
77 elf->e_idlen = SARMAG; |
|
78 elf->e_off = SARMAG; |
|
79 |
|
80 /* process special members */ |
|
81 offset = SARMAG; |
|
82 while (!elf->e_strtab && offset + sizeof(*hdr) <= elf->e_size) { |
|
83 hdr = (struct ar_hdr*)(elf->e_data + offset); |
|
84 if (memcmp(hdr->ar_fmag, fmag, sizeof(fmag) - 1)) { |
|
85 break; |
|
86 } |
|
87 if (hdr->ar_name[0] != '/') { |
|
88 break; |
|
89 } |
|
90 size = getnum(hdr->ar_size, sizeof(hdr->ar_size), 10, &err); |
|
91 if (err || !size) { |
|
92 break; |
|
93 } |
|
94 offset += sizeof(*hdr); |
|
95 if (offset + size > elf->e_size) { |
|
96 break; |
|
97 } |
|
98 if (hdr->ar_name[1] == '/' && hdr->ar_name[2] == ' ') { |
|
99 elf->e_strtab = elf->e_data + offset; |
|
100 elf->e_strlen = size; |
|
101 break; |
|
102 } |
|
103 if (hdr->ar_name[1] != ' ') { |
|
104 break; |
|
105 } |
|
106 /* |
|
107 * Windows (.lib) archives provide two symbol tables |
|
108 * The first one is the one we want. |
|
109 */ |
|
110 if (!elf->e_symtab) { |
|
111 elf->e_symtab = elf->e_data + offset; |
|
112 elf->e_symlen = size; |
|
113 } |
|
114 offset += size + (size & 1); |
|
115 } |
|
116 } |
|
117 |
|
118 static Elf_Arhdr* |
|
119 _elf_arhdr(Elf *arf) { |
|
120 struct ar_hdr *hdr; |
|
121 Elf_Arhdr *arhdr; |
|
122 size_t namelen; |
|
123 size_t tmp; |
|
124 char *name; |
|
125 size_t err = 0; |
|
126 |
|
127 if (arf->e_off == arf->e_size) { |
|
128 /* no error! */ |
|
129 return NULL; |
|
130 } |
|
131 if (arf->e_off < 0 || arf->e_off > arf->e_size) { |
|
132 seterr(ERROR_OUTSIDE); |
|
133 return NULL; |
|
134 } |
|
135 if (arf->e_off + sizeof(*hdr) > arf->e_size) { |
|
136 seterr(ERROR_TRUNC_ARHDR); |
|
137 return NULL; |
|
138 } |
|
139 elf_assert(arf->e_data != NULL); |
|
140 hdr = (struct ar_hdr*)(arf->e_data + arf->e_off); |
|
141 if (memcmp(hdr->ar_fmag, fmag, sizeof(fmag) - 1)) { |
|
142 seterr(ERROR_ARFMAG); |
|
143 return NULL; |
|
144 } |
|
145 |
|
146 name = hdr->ar_name; |
|
147 for (namelen = sizeof(hdr->ar_name); namelen > 0; namelen--) { |
|
148 if (name[namelen - 1] != ' ') { |
|
149 break; |
|
150 } |
|
151 } |
|
152 if (name[0] == '/') { |
|
153 if (name[1] >= '0' && name[1] <= '9') { |
|
154 if (!arf->e_strtab) { |
|
155 seterr(ERROR_ARSTRTAB); |
|
156 return NULL; |
|
157 } |
|
158 tmp = getnum(&name[1], namelen - 1, 10, &err); |
|
159 if (err) { |
|
160 seterr(ERROR_ARSPECIAL); |
|
161 return NULL; |
|
162 } |
|
163 if (tmp < 0 || tmp >= arf->e_strlen) { |
|
164 seterr(ERROR_ARSTRTAB); |
|
165 return NULL; |
|
166 } |
|
167 for (namelen = tmp; namelen < arf->e_strlen; namelen++) { |
|
168 if (arf->e_strtab[namelen] == '/') { |
|
169 break; |
|
170 } |
|
171 } |
|
172 if (namelen == arf->e_strlen) { |
|
173 seterr(ERROR_ARSTRTAB); |
|
174 return NULL; |
|
175 } |
|
176 name = arf->e_strtab + tmp; |
|
177 namelen -= tmp; |
|
178 } |
|
179 else if (namelen != 1 && !(namelen == 2 && name[1] == '/')) { |
|
180 seterr(ERROR_ARSPECIAL); |
|
181 return NULL; |
|
182 } |
|
183 } |
|
184 else if (namelen > 0 && name[namelen - 1] == '/') { |
|
185 namelen--; |
|
186 } |
|
187 /* XXX some broken software omits the trailing slash |
|
188 else { |
|
189 namelen = 0; |
|
190 } |
|
191 */ |
|
192 |
|
193 if (!(arhdr = (Elf_Arhdr*)malloc(sizeof(*arhdr) + |
|
194 sizeof(hdr->ar_name) + namelen + 2))) { |
|
195 seterr(ERROR_MEM_ARHDR); |
|
196 return NULL; |
|
197 } |
|
198 |
|
199 arhdr->ar_name = NULL; |
|
200 arhdr->ar_rawname = (char*)(arhdr + 1); |
|
201 arhdr->ar_date = getnum(hdr->ar_date, sizeof(hdr->ar_date), 10, &err); |
|
202 arhdr->ar_uid = getnum(hdr->ar_uid, sizeof(hdr->ar_uid), 10, &err); |
|
203 arhdr->ar_gid = getnum(hdr->ar_gid, sizeof(hdr->ar_gid), 10, &err); |
|
204 arhdr->ar_mode = getnum(hdr->ar_mode, sizeof(hdr->ar_mode), 8, &err); |
|
205 arhdr->ar_size = getnum(hdr->ar_size, sizeof(hdr->ar_size), 10, &err); |
|
206 if (err) { |
|
207 free(arhdr); |
|
208 seterr(ERROR_ARHDR); |
|
209 return NULL; |
|
210 } |
|
211 if (arf->e_off + sizeof(struct ar_hdr) + arhdr->ar_size > arf->e_size) { |
|
212 free(arhdr); |
|
213 seterr(ERROR_TRUNC_MEMBER); |
|
214 return NULL; |
|
215 } |
|
216 |
|
217 memcpy(arhdr->ar_rawname, hdr->ar_name, sizeof(hdr->ar_name)); |
|
218 arhdr->ar_rawname[sizeof(hdr->ar_name)] = '\0'; |
|
219 |
|
220 if (namelen) { |
|
221 arhdr->ar_name = arhdr->ar_rawname + sizeof(hdr->ar_name) + 1; |
|
222 memcpy(arhdr->ar_name, name, namelen); |
|
223 arhdr->ar_name[namelen] = '\0'; |
|
224 } |
|
225 |
|
226 return arhdr; |
|
227 } |
|
228 |
|
229 static void |
|
230 _elf_check_type(Elf *elf, size_t size) { |
|
231 elf->e_idlen = size; |
|
232 if (size >= EI_NIDENT && !memcmp(elf->e_data, ELFMAG, SELFMAG)) { |
|
233 elf->e_kind = ELF_K_ELF; |
|
234 elf->e_idlen = EI_NIDENT; |
|
235 elf->e_class = elf->e_data[EI_CLASS]; |
|
236 elf->e_encoding = elf->e_data[EI_DATA]; |
|
237 elf->e_version = elf->e_data[EI_VERSION]; |
|
238 } |
|
239 else if (size >= SARMAG && !memcmp(elf->e_data, ARMAG, SARMAG)) { |
|
240 _elf_init_ar(elf); |
|
241 } |
|
242 } |
|
243 |
|
244 Elf* |
|
245 elf_begin(int fd, Elf_Cmd cmd, Elf *ref) { |
|
246 Elf_Arhdr *arhdr = NULL; |
|
247 size_t size = 0; |
|
248 off_t off; |
|
249 Elf *elf; |
|
250 |
|
251 elf_assert(_elf_init.e_magic == ELF_MAGIC); |
|
252 if (_elf_version == EV_NONE) { |
|
253 seterr(ERROR_VERSION_UNSET); |
|
254 return NULL; |
|
255 } |
|
256 else if (cmd == ELF_C_NULL) { |
|
257 return NULL; |
|
258 } |
|
259 else if (cmd == ELF_C_WRITE) { |
|
260 ref = NULL; |
|
261 } |
|
262 else if (cmd != ELF_C_READ && cmd != ELF_C_RDWR) { |
|
263 seterr(ERROR_INVALID_CMD); |
|
264 return NULL; |
|
265 } |
|
266 else if (ref) { |
|
267 elf_assert(ref->e_magic == ELF_MAGIC); |
|
268 if (!ref->e_readable || (cmd == ELF_C_RDWR && !ref->e_writable)) { |
|
269 seterr(ERROR_CMDMISMATCH); |
|
270 return NULL; |
|
271 } |
|
272 if (ref->e_kind != ELF_K_AR) { |
|
273 ref->e_count++; |
|
274 return ref; |
|
275 } |
|
276 if (cmd == ELF_C_RDWR) { |
|
277 seterr(ERROR_MEMBERWRITE); |
|
278 return NULL; |
|
279 } |
|
280 if (ref->e_memory) { |
|
281 fd = ref->e_fd; |
|
282 } |
|
283 else if (fd != ref->e_fd) { |
|
284 seterr(ERROR_FDMISMATCH); |
|
285 return NULL; |
|
286 } |
|
287 if (!(arhdr = _elf_arhdr(ref))) { |
|
288 return NULL; |
|
289 } |
|
290 size = arhdr->ar_size; |
|
291 } |
|
292 else if ((off = lseek(fd, (off_t)0, SEEK_END)) == (off_t)-1 |
|
293 || (off_t)(size = off) != off) { |
|
294 seterr(ERROR_IO_GETSIZE); |
|
295 return NULL; |
|
296 } |
|
297 |
|
298 if (!(elf = (Elf*)malloc(sizeof(Elf)))) { |
|
299 seterr(ERROR_MEM_ELF); |
|
300 return NULL; |
|
301 } |
|
302 *elf = _elf_init; |
|
303 elf->e_fd = fd; |
|
304 elf->e_parent = ref; |
|
305 elf->e_size = elf->e_dsize = size; |
|
306 |
|
307 if (cmd != ELF_C_READ) { |
|
308 elf->e_writable = 1; |
|
309 } |
|
310 if (cmd != ELF_C_WRITE) { |
|
311 elf->e_readable = 1; |
|
312 } |
|
313 else { |
|
314 return elf; |
|
315 } |
|
316 |
|
317 if (ref) { |
|
318 size_t offset = ref->e_off + sizeof(struct ar_hdr); |
|
319 Elf *xelf; |
|
320 |
|
321 elf_assert(arhdr); |
|
322 elf->e_arhdr = arhdr; |
|
323 elf->e_base = ref->e_base + offset; |
|
324 /* |
|
325 * Share the archive's memory image. To avoid |
|
326 * multiple independent elf descriptors if the |
|
327 * same member is requested twice, scan the list |
|
328 * of open members for duplicates. |
|
329 * |
|
330 * I don't know how SVR4 handles this case. Don't rely on it. |
|
331 */ |
|
332 for (xelf = ref->e_members; xelf; xelf = xelf->e_link) { |
|
333 elf_assert(xelf->e_parent == ref); |
|
334 if (xelf->e_base == elf->e_base) { |
|
335 free(arhdr); |
|
336 free(elf); |
|
337 xelf->e_count++; |
|
338 return xelf; |
|
339 } |
|
340 } |
|
341 if (size == 0) { |
|
342 elf->e_data = NULL; |
|
343 } |
|
344 #if 1 |
|
345 else { |
|
346 /* |
|
347 * Archive members may be misaligned. Freezing them will |
|
348 * cause libelf to allocate buffers for translated data, |
|
349 * which should be properly aligned in all cases. |
|
350 */ |
|
351 elf_assert(!ref->e_cooked); |
|
352 elf->e_data = elf->e_rawdata = ref->e_data + offset; |
|
353 } |
|
354 #else |
|
355 else if (ref->e_data == ref->e_rawdata) { |
|
356 elf_assert(!ref->e_cooked); |
|
357 /* |
|
358 * archive is frozen - freeze member, too |
|
359 */ |
|
360 elf->e_data = elf->e_rawdata = ref->e_data + offset; |
|
361 } |
|
362 else { |
|
363 elf_assert(!ref->e_memory); |
|
364 elf->e_data = ref->e_data + offset; |
|
365 /* |
|
366 * The member's memory image may have been modified if |
|
367 * the member has been processed before. Since we need the |
|
368 * original image, we have to re-read the archive file. |
|
369 * Will fail if the archive's file descriptor is disabled. |
|
370 */ |
|
371 if (!ref->e_cooked) { |
|
372 ref->e_cooked = 1; |
|
373 } |
|
374 else if (!_elf_read(ref, elf->e_data, offset, size)) { |
|
375 free(arhdr); |
|
376 free(elf); |
|
377 return NULL; |
|
378 } |
|
379 } |
|
380 #endif |
|
381 elf->e_next = offset + size + (size & 1); |
|
382 elf->e_disabled = ref->e_disabled; |
|
383 elf->e_memory = ref->e_memory; |
|
384 /* parent/child linking */ |
|
385 elf->e_link = ref->e_members; |
|
386 ref->e_members = elf; |
|
387 ref->e_count++; |
|
388 /* Slowaris compatibility - do not rely on this! */ |
|
389 ref->e_off = elf->e_next; |
|
390 } |
|
391 else if (size) { |
|
392 #if HAVE_MMAP |
|
393 /* |
|
394 * Using mmap on writable files will interfere with elf_update |
|
395 */ |
|
396 if (!elf->e_writable && (elf->e_data = _elf_mmap(elf))) { |
|
397 elf->e_unmap_data = 1; |
|
398 } |
|
399 else |
|
400 #endif /* HAVE_MMAP */ |
|
401 if (!(elf->e_data = _elf_read(elf, NULL, 0, size))) { |
|
402 free(elf); |
|
403 return NULL; |
|
404 } |
|
405 } |
|
406 |
|
407 _elf_check_type(elf, size); |
|
408 return elf; |
|
409 } |
|
410 |
|
411 Elf* |
|
412 elf_memory(char *image, size_t size) { |
|
413 Elf *elf; |
|
414 |
|
415 elf_assert(_elf_init.e_magic == ELF_MAGIC); |
|
416 if (_elf_version == EV_NONE) { |
|
417 seterr(ERROR_VERSION_UNSET); |
|
418 return NULL; |
|
419 } |
|
420 else if (size == 0 || image == NULL) { |
|
421 /* TODO: set error code? */ |
|
422 return NULL; |
|
423 } |
|
424 |
|
425 if (!(elf = (Elf*)malloc(sizeof(Elf)))) { |
|
426 seterr(ERROR_MEM_ELF); |
|
427 return NULL; |
|
428 } |
|
429 *elf = _elf_init; |
|
430 elf->e_size = elf->e_dsize = size; |
|
431 elf->e_data = elf->e_rawdata = image; |
|
432 elf->e_readable = 1; |
|
433 elf->e_disabled = 1; |
|
434 elf->e_memory = 1; |
|
435 |
|
436 _elf_check_type(elf, size); |
|
437 return elf; |
|
438 } |
|
439 |
|
440 #if __LIBELF64 |
|
441 |
|
442 int |
|
443 gelf_getclass(Elf *elf) { |
|
444 if (elf && elf->e_kind == ELF_K_ELF && valid_class(elf->e_class)) { |
|
445 return elf->e_class; |
|
446 } |
|
447 return ELFCLASSNONE; |
|
448 } |
|
449 |
|
450 #endif /* __LIBELF64 */ |