1210348Skaiw/*-
2210348Skaiw * Copyright (c) 2006,2009 Joseph Koshy
3210348Skaiw * All rights reserved.
4210348Skaiw *
5210348Skaiw * Redistribution and use in source and binary forms, with or without
6210348Skaiw * modification, are permitted provided that the following conditions
7210348Skaiw * are met:
8210348Skaiw * 1. Redistributions of source code must retain the above copyright
9210348Skaiw *    notice, this list of conditions and the following disclaimer.
10210348Skaiw * 2. Redistributions in binary form must reproduce the above copyright
11210348Skaiw *    notice, this list of conditions and the following disclaimer in the
12210348Skaiw *    documentation and/or other materials provided with the distribution.
13210348Skaiw *
14210348Skaiw * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS `AS IS' AND
15210348Skaiw * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16210348Skaiw * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17210348Skaiw * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18210348Skaiw * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19210348Skaiw * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20210348Skaiw * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21210348Skaiw * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22210348Skaiw * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23210348Skaiw * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24210348Skaiw * SUCH DAMAGE.
25210348Skaiw */
26210348Skaiw
27210348Skaiw#include <sys/cdefs.h>
28210348Skaiw__FBSDID("$FreeBSD$");
29210348Skaiw
30210348Skaiw#include <ar.h>
31210348Skaiw#include <assert.h>
32210348Skaiw#include <libelf.h>
33210348Skaiw#include <stdlib.h>
34210348Skaiw#include <string.h>
35210348Skaiw
36210348Skaiw#include "_libelf.h"
37210348Skaiw
38210348Skaiw/*
39210348Skaiw * Convert a string bounded by `start' and `start+sz' (exclusive) to a
40210348Skaiw * number in the specified base.
41210348Skaiw */
42210348Skaiwint
43210348Skaiw_libelf_ar_get_number(char *s, size_t sz, int base, size_t *ret)
44210348Skaiw{
45210348Skaiw	int c, v;
46210348Skaiw	size_t r;
47210348Skaiw	char *e;
48210348Skaiw
49210348Skaiw	assert(base <= 10);
50210348Skaiw
51210348Skaiw	e = s + sz;
52210348Skaiw
53210348Skaiw	/* skip leading blanks */
54210348Skaiw	for (;s < e && (c = *s) == ' '; s++)
55210348Skaiw		;
56210348Skaiw
57210348Skaiw	r = 0L;
58210348Skaiw	for (;s < e; s++) {
59210348Skaiw		if ((c = *s) == ' ')
60210348Skaiw			break;
61210348Skaiw		if (c < '0' || c > '9')
62210348Skaiw			return (0);
63210348Skaiw		v = c - '0';
64210348Skaiw		if (v >= base)		/* Illegal digit. */
65210348Skaiw			break;
66210348Skaiw		r *= base;
67210348Skaiw		r += v;
68210348Skaiw	}
69210348Skaiw
70210348Skaiw	*ret = r;
71210348Skaiw
72210348Skaiw	return (1);
73210348Skaiw}
74210348Skaiw
75210348Skaiw/*
76210348Skaiw * Retrieve a string from a name field.  If `rawname' is set, leave
77210348Skaiw * ar(1) control characters in.
78210348Skaiw */
79210348Skaiwchar *
80210348Skaiw_libelf_ar_get_string(const char *buf, size_t bufsize, int rawname)
81210348Skaiw{
82210348Skaiw	const char *q;
83210348Skaiw	char *r;
84210348Skaiw	size_t sz;
85210348Skaiw
86210348Skaiw	if (rawname)
87210348Skaiw		sz = bufsize + 1;
88210348Skaiw	else {
89210348Skaiw		/* Skip back over trailing blanks. */
90210348Skaiw		for (q = buf + bufsize - 1; q >= buf && *q == ' '; --q)
91210348Skaiw			;
92210348Skaiw
93210348Skaiw		if (q < buf) {
94210348Skaiw			/*
95210348Skaiw			 * If the input buffer only had blanks in it,
96210348Skaiw			 * return a zero-length string.
97210348Skaiw			 */
98210348Skaiw			buf = "";
99210348Skaiw			sz = 1;
100210348Skaiw		} else {
101210348Skaiw			/*
102210348Skaiw			 * Remove the trailing '/' character, but only
103210348Skaiw			 * if the name isn't one of the special names
104210348Skaiw			 * "/" and "//".
105210348Skaiw			 */
106210348Skaiw			if (q > buf + 1 ||
107210348Skaiw			    (q == (buf + 1) && *buf != '/'))
108210348Skaiw				q--;
109210348Skaiw
110210348Skaiw			sz = q - buf + 2; /* Space for a trailing NUL. */
111210348Skaiw		}
112210348Skaiw	}
113210348Skaiw
114210348Skaiw	if ((r = malloc(sz)) == NULL) {
115210348Skaiw		LIBELF_SET_ERROR(RESOURCE, 0);
116210348Skaiw		return (NULL);
117210348Skaiw	}
118210348Skaiw
119210348Skaiw	(void) strncpy(r, buf, sz);
120210348Skaiw	r[sz - 1] = '\0';
121210348Skaiw
122210348Skaiw	return (r);
123210348Skaiw}
124210348Skaiw
125210348Skaiw/*
126210348Skaiw * Retrieve the full name of the archive member.
127210348Skaiw */
128210348Skaiwchar *
129210348Skaiw_libelf_ar_get_name(char *buf, size_t bufsize, Elf *e)
130210348Skaiw{
131210348Skaiw	char c, *q, *r, *s;
132210348Skaiw	size_t len;
133210348Skaiw	size_t offset;
134210348Skaiw
135210348Skaiw	assert(e->e_kind == ELF_K_AR);
136210348Skaiw
137210348Skaiw	if (buf[0] == '/' && (c = buf[1]) >= '0' && c <= '9') {
138210348Skaiw		/*
139210348Skaiw		 * The value in field ar_name is a decimal offset into
140210348Skaiw		 * the archive string table where the actual name
141210348Skaiw		 * resides.
142210348Skaiw		 */
143210348Skaiw		if (_libelf_ar_get_number(buf + 1, bufsize - 1, 10,
144210348Skaiw		    &offset) == 0) {
145210348Skaiw			LIBELF_SET_ERROR(ARCHIVE, 0);
146210348Skaiw			return (NULL);
147210348Skaiw		}
148210348Skaiw
149210348Skaiw		if (offset > e->e_u.e_ar.e_rawstrtabsz) {
150210348Skaiw			LIBELF_SET_ERROR(ARCHIVE, 0);
151210348Skaiw			return (NULL);
152210348Skaiw		}
153210348Skaiw
154210348Skaiw		s = q = e->e_u.e_ar.e_rawstrtab + offset;
155210348Skaiw		r = e->e_u.e_ar.e_rawstrtab + e->e_u.e_ar.e_rawstrtabsz;
156210348Skaiw
157210348Skaiw		for (s = q; s < r && *s != '/'; s++)
158210348Skaiw			;
159210348Skaiw		len = s - q + 1; /* space for the trailing NUL */
160210348Skaiw
161210348Skaiw		if ((s = malloc(len)) == NULL) {
162210348Skaiw			LIBELF_SET_ERROR(RESOURCE, 0);
163210348Skaiw			return (NULL);
164210348Skaiw		}
165210348Skaiw
166210348Skaiw		(void) strncpy(s, q, len);
167210348Skaiw		s[len - 1] = '\0';
168210348Skaiw
169210348Skaiw		return (s);
170210348Skaiw	}
171210348Skaiw
172210348Skaiw	/*
173210348Skaiw	 * Normal 'name'
174210348Skaiw	 */
175210348Skaiw	return (_libelf_ar_get_string(buf, bufsize, 0));
176210348Skaiw}
177210348Skaiw
178210348Skaiw/*
179210348Skaiw * Open an 'ar' archive.
180210348Skaiw */
181210348SkaiwElf *
182210348Skaiw_libelf_ar_open(Elf *e)
183210348Skaiw{
184210348Skaiw	int i;
185210348Skaiw	char *s, *end;
186210348Skaiw	size_t sz;
187210348Skaiw	struct ar_hdr arh;
188210348Skaiw
189210348Skaiw	e->e_kind = ELF_K_AR;
190210348Skaiw	e->e_u.e_ar.e_nchildren = 0;
191210348Skaiw	e->e_u.e_ar.e_next = (off_t) -1;
192210348Skaiw
193210348Skaiw	/*
194210348Skaiw	 * Look for special members.
195210348Skaiw	 */
196210348Skaiw
197210348Skaiw	s = e->e_rawfile + SARMAG;
198210348Skaiw	end = e->e_rawfile + e->e_rawsize;
199210348Skaiw
200210348Skaiw	assert(e->e_rawsize > 0);
201210348Skaiw
202210348Skaiw	/*
203210348Skaiw	 * Look for magic names "/ " and "// " in the first two entries
204210348Skaiw	 * of the archive.
205210348Skaiw	 */
206210348Skaiw	for (i = 0; i < 2; i++) {
207210348Skaiw
208210348Skaiw		if (s + sizeof(arh) > end) {
209210348Skaiw			LIBELF_SET_ERROR(ARCHIVE, 0);
210210348Skaiw			return (NULL);
211210348Skaiw		}
212210348Skaiw
213210348Skaiw		(void) memcpy(&arh, s, sizeof(arh));
214210348Skaiw
215210348Skaiw		if (arh.ar_fmag[0] != '`' || arh.ar_fmag[1] != '\n') {
216210348Skaiw			LIBELF_SET_ERROR(ARCHIVE, 0);
217210348Skaiw			return (NULL);
218210348Skaiw		}
219210348Skaiw
220210348Skaiw		if (arh.ar_name[0] != '/')	/* not a special symbol */
221210348Skaiw			break;
222210348Skaiw
223210348Skaiw		if (_libelf_ar_get_number(arh.ar_size, sizeof(arh.ar_size),
224210348Skaiw			10, &sz) == 0) {
225210348Skaiw			LIBELF_SET_ERROR(ARCHIVE, 0);
226210348Skaiw			return (NULL);
227210348Skaiw		}
228210348Skaiw
229210348Skaiw		assert(sz > 0);
230210348Skaiw
231210348Skaiw		s += sizeof(arh);
232210348Skaiw
233210348Skaiw		if (arh.ar_name[1] == ' ') {	/* "/ " => symbol table */
234210348Skaiw
235210348Skaiw			e->e_u.e_ar.e_rawsymtab = s;
236210348Skaiw			e->e_u.e_ar.e_rawsymtabsz = sz;
237210348Skaiw
238210348Skaiw		} else if (arh.ar_name[1] == '/' && arh.ar_name[2] == ' ') {
239210348Skaiw
240210348Skaiw			/* "// " => string table for long file names */
241210348Skaiw			e->e_u.e_ar.e_rawstrtab = s;
242210348Skaiw			e->e_u.e_ar.e_rawstrtabsz = sz;
243210348Skaiw		}
244210348Skaiw
245210348Skaiw		sz = LIBELF_ADJUST_AR_SIZE(sz);
246210348Skaiw
247210348Skaiw		s += sz;
248210348Skaiw	}
249210348Skaiw
250210348Skaiw	e->e_u.e_ar.e_next = (off_t) (s - e->e_rawfile);
251210348Skaiw
252210348Skaiw	return (e);
253210348Skaiw}
254