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 */
42 int
_libelf_ar_get_number(char * s,size_t sz,int base,size_t * ret)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 */
79 char *
_libelf_ar_get_string(const char * buf,size_t bufsize,int rawname)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 */
128 char *
_libelf_ar_get_name(char * buf,size_t bufsize,Elf * e)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 */
181 Elf *
_libelf_ar_open(Elf * e)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