1/*-
2 * Copyright (c) 2006,2009 Joseph Koshy
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 <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <ar.h>
31#include <assert.h>
32#include <libelf.h>
33#include <stdlib.h>
34#include <string.h>
35
36#include "_libelf.h"
37
38/*
39 * Convert a string bounded by `start' and `start+sz' (exclusive) to a
40 * number in the specified base.
41 */
42int
43_libelf_ar_get_number(char *s, size_t sz, int base, size_t *ret)
44{
45	int c, v;
46	size_t r;
47	char *e;
48
49	assert(base <= 10);
50
51	e = s + sz;
52
53	/* skip leading blanks */
54	for (;s < e && (c = *s) == ' '; s++)
55		;
56
57	r = 0L;
58	for (;s < e; s++) {
59		if ((c = *s) == ' ')
60			break;
61		if (c < '0' || c > '9')
62			return (0);
63		v = c - '0';
64		if (v >= base)		/* Illegal digit. */
65			break;
66		r *= base;
67		r += v;
68	}
69
70	*ret = r;
71
72	return (1);
73}
74
75/*
76 * Retrieve a string from a name field.  If `rawname' is set, leave
77 * ar(1) control characters in.
78 */
79char *
80_libelf_ar_get_string(const char *buf, size_t bufsize, int rawname)
81{
82	const char *q;
83	char *r;
84	size_t sz;
85
86	if (rawname)
87		sz = bufsize + 1;
88	else {
89		/* Skip back over trailing blanks. */
90		for (q = buf + bufsize - 1; q >= buf && *q == ' '; --q)
91			;
92
93		if (q < buf) {
94			/*
95			 * If the input buffer only had blanks in it,
96			 * return a zero-length string.
97			 */
98			buf = "";
99			sz = 1;
100		} else {
101			/*
102			 * Remove the trailing '/' character, but only
103			 * if the name isn't one of the special names
104			 * "/" and "//".
105			 */
106			if (q > buf + 1 ||
107			    (q == (buf + 1) && *buf != '/'))
108				q--;
109
110			sz = q - buf + 2; /* Space for a trailing NUL. */
111		}
112	}
113
114	if ((r = malloc(sz)) == NULL) {
115		LIBELF_SET_ERROR(RESOURCE, 0);
116		return (NULL);
117	}
118
119	(void) strncpy(r, buf, sz);
120	r[sz - 1] = '\0';
121
122	return (r);
123}
124
125/*
126 * Retrieve the full name of the archive member.
127 */
128char *
129_libelf_ar_get_name(char *buf, size_t bufsize, Elf *e)
130{
131	char c, *q, *r, *s;
132	size_t len;
133	size_t offset;
134
135	assert(e->e_kind == ELF_K_AR);
136
137	if (buf[0] == '/' && (c = buf[1]) >= '0' && c <= '9') {
138		/*
139		 * The value in field ar_name is a decimal offset into
140		 * the archive string table where the actual name
141		 * resides.
142		 */
143		if (_libelf_ar_get_number(buf + 1, bufsize - 1, 10,
144		    &offset) == 0) {
145			LIBELF_SET_ERROR(ARCHIVE, 0);
146			return (NULL);
147		}
148
149		if (offset > e->e_u.e_ar.e_rawstrtabsz) {
150			LIBELF_SET_ERROR(ARCHIVE, 0);
151			return (NULL);
152		}
153
154		s = q = e->e_u.e_ar.e_rawstrtab + offset;
155		r = e->e_u.e_ar.e_rawstrtab + e->e_u.e_ar.e_rawstrtabsz;
156
157		for (s = q; s < r && *s != '/'; s++)
158			;
159		len = s - q + 1; /* space for the trailing NUL */
160
161		if ((s = malloc(len)) == NULL) {
162			LIBELF_SET_ERROR(RESOURCE, 0);
163			return (NULL);
164		}
165
166		(void) strncpy(s, q, len);
167		s[len - 1] = '\0';
168
169		return (s);
170	}
171
172	/*
173	 * Normal 'name'
174	 */
175	return (_libelf_ar_get_string(buf, bufsize, 0));
176}
177
178/*
179 * Open an 'ar' archive.
180 */
181Elf *
182_libelf_ar_open(Elf *e)
183{
184	int i;
185	char *s, *end;
186	size_t sz;
187	struct ar_hdr arh;
188
189	e->e_kind = ELF_K_AR;
190	e->e_u.e_ar.e_nchildren = 0;
191	e->e_u.e_ar.e_next = (off_t) -1;
192
193	/*
194	 * Look for special members.
195	 */
196
197	s = e->e_rawfile + SARMAG;
198	end = e->e_rawfile + e->e_rawsize;
199
200	assert(e->e_rawsize > 0);
201
202	/*
203	 * Look for magic names "/ " and "// " in the first two entries
204	 * of the archive.
205	 */
206	for (i = 0; i < 2; i++) {
207
208		if (s + sizeof(arh) > end) {
209			LIBELF_SET_ERROR(ARCHIVE, 0);
210			return (NULL);
211		}
212
213		(void) memcpy(&arh, s, sizeof(arh));
214
215		if (arh.ar_fmag[0] != '`' || arh.ar_fmag[1] != '\n') {
216			LIBELF_SET_ERROR(ARCHIVE, 0);
217			return (NULL);
218		}
219
220		if (arh.ar_name[0] != '/')	/* not a special symbol */
221			break;
222
223		if (_libelf_ar_get_number(arh.ar_size, sizeof(arh.ar_size),
224			10, &sz) == 0) {
225			LIBELF_SET_ERROR(ARCHIVE, 0);
226			return (NULL);
227		}
228
229		assert(sz > 0);
230
231		s += sizeof(arh);
232
233		if (arh.ar_name[1] == ' ') {	/* "/ " => symbol table */
234
235			e->e_u.e_ar.e_rawsymtab = s;
236			e->e_u.e_ar.e_rawsymtabsz = sz;
237
238		} else if (arh.ar_name[1] == '/' && arh.ar_name[2] == ' ') {
239
240			/* "// " => string table for long file names */
241			e->e_u.e_ar.e_rawstrtab = s;
242			e->e_u.e_ar.e_rawstrtabsz = sz;
243		}
244
245		sz = LIBELF_ADJUST_AR_SIZE(sz);
246
247		s += sz;
248	}
249
250	e->e_u.e_ar.e_next = (off_t) (s - e->e_rawfile);
251
252	return (e);
253}
254