xref: /openbsd/usr.bin/ctfconv/dw.c (revision 283ced89)
1*283ced89Sclaudio /*	$OpenBSD: dw.c,v 1.6 2024/02/21 13:16:14 claudio Exp $ */
20687c322Sjasper 
3192095f7Smpi /*
4192095f7Smpi  * Copyright (c) 2016 Martin Pieuchot
5192095f7Smpi  * Copyright (c) 2014 Matthew Dempsky <matthew@dempsky.org>
6192095f7Smpi  *
7192095f7Smpi  * Permission to use, copy, modify, and distribute this software for any
8192095f7Smpi  * purpose with or without fee is hereby granted, provided that the above
9192095f7Smpi  * copyright notice and this permission notice appear in all copies.
10192095f7Smpi  *
11192095f7Smpi  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12192095f7Smpi  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13192095f7Smpi  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14192095f7Smpi  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15192095f7Smpi  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16192095f7Smpi  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17192095f7Smpi  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18192095f7Smpi  */
19192095f7Smpi 
20192095f7Smpi #include <sys/queue.h>
21192095f7Smpi 
22192095f7Smpi #include <errno.h>
23192095f7Smpi #include <stdint.h>
24*283ced89Sclaudio #include <stdio.h>
25192095f7Smpi #include <stdlib.h>
26192095f7Smpi #include <string.h>
27192095f7Smpi 
28192095f7Smpi #include "dw.h"
29192095f7Smpi #include "dwarf.h"
30192095f7Smpi #include "pool.h"
31192095f7Smpi 
32192095f7Smpi #ifndef NOPOOL
33192095f7Smpi struct pool dcu_pool, die_pool, dav_pool, dab_pool, dat_pool;
34192095f7Smpi #endif /* NOPOOL */
35192095f7Smpi 
36192095f7Smpi #ifndef nitems
37192095f7Smpi #define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
38192095f7Smpi #endif
39192095f7Smpi 
40192095f7Smpi static int	 dw_read_u8(struct dwbuf *, uint8_t *);
41192095f7Smpi static int	 dw_read_u16(struct dwbuf *, uint16_t *);
42192095f7Smpi static int	 dw_read_u32(struct dwbuf *, uint32_t *);
43192095f7Smpi static int	 dw_read_u64(struct dwbuf *, uint64_t *);
44192095f7Smpi 
45192095f7Smpi static int	 dw_read_sleb128(struct dwbuf *, int64_t *);
46192095f7Smpi static int	 dw_read_uleb128(struct dwbuf *, uint64_t *);
47192095f7Smpi 
48192095f7Smpi static int	 dw_read_bytes(struct dwbuf *, void *, size_t);
49192095f7Smpi static int	 dw_read_string(struct dwbuf *, const char **);
50192095f7Smpi static int	 dw_read_buf(struct dwbuf *, struct dwbuf *, size_t);
51192095f7Smpi 
52192095f7Smpi static int	 dw_skip_bytes(struct dwbuf *, size_t);
53192095f7Smpi 
54192095f7Smpi static int	 dw_attr_parse(struct dwbuf *, struct dwattr *, uint8_t,
55192095f7Smpi 		     struct dwaval_queue *);
56192095f7Smpi static void	 dw_attr_purge(struct dwaval_queue *);
57192095f7Smpi static int	 dw_die_parse(struct dwbuf *, size_t, uint8_t,
58192095f7Smpi 		     struct dwabbrev_queue *, struct dwdie_queue *);
59192095f7Smpi static void	 dw_die_purge(struct dwdie_queue *);
60192095f7Smpi 
61192095f7Smpi static int
dw_read_bytes(struct dwbuf * d,void * v,size_t n)62192095f7Smpi dw_read_bytes(struct dwbuf *d, void *v, size_t n)
63192095f7Smpi {
64192095f7Smpi 	if (d->len < n)
65192095f7Smpi 		return -1;
66192095f7Smpi 	memcpy(v, d->buf, n);
67192095f7Smpi 	d->buf += n;
68192095f7Smpi 	d->len -= n;
69192095f7Smpi 	return 0;
70192095f7Smpi }
71192095f7Smpi 
72192095f7Smpi static int
dw_read_u8(struct dwbuf * d,uint8_t * v)73192095f7Smpi dw_read_u8(struct dwbuf *d, uint8_t *v)
74192095f7Smpi {
75192095f7Smpi 	return dw_read_bytes(d, v, sizeof(*v));
76192095f7Smpi }
77192095f7Smpi 
78192095f7Smpi static int
dw_read_u16(struct dwbuf * d,uint16_t * v)79192095f7Smpi dw_read_u16(struct dwbuf *d, uint16_t *v)
80192095f7Smpi {
81192095f7Smpi 	return dw_read_bytes(d, v, sizeof(*v));
82192095f7Smpi }
83192095f7Smpi 
84192095f7Smpi static int
dw_read_u32(struct dwbuf * d,uint32_t * v)85192095f7Smpi dw_read_u32(struct dwbuf *d, uint32_t *v)
86192095f7Smpi {
87192095f7Smpi 	return dw_read_bytes(d, v, sizeof(*v));
88192095f7Smpi }
89192095f7Smpi 
90192095f7Smpi static int
dw_read_u64(struct dwbuf * d,uint64_t * v)91192095f7Smpi dw_read_u64(struct dwbuf *d, uint64_t *v)
92192095f7Smpi {
93192095f7Smpi 	return dw_read_bytes(d, v, sizeof(*v));
94192095f7Smpi }
95192095f7Smpi 
96192095f7Smpi /* Read a DWARF LEB128 (little-endian base-128) value. */
97192095f7Smpi static inline int
dw_read_leb128(struct dwbuf * d,uint64_t * v,int signextend)98192095f7Smpi dw_read_leb128(struct dwbuf *d, uint64_t *v, int signextend)
99192095f7Smpi {
100192095f7Smpi 	unsigned int shift = 0;
101192095f7Smpi 	uint64_t res = 0;
102192095f7Smpi 	uint8_t x;
103192095f7Smpi 
104192095f7Smpi 	while (shift < 64 && !dw_read_u8(d, &x)) {
105192095f7Smpi 		res |= (uint64_t)(x & 0x7f) << shift;
106192095f7Smpi 		shift += 7;
107192095f7Smpi 		if ((x & 0x80) == 0) {
108192095f7Smpi 			if (signextend && shift < 64 && (x & 0x40) != 0)
109192095f7Smpi 				res |= ~(uint64_t)0 << shift;
110192095f7Smpi 			*v = res;
111192095f7Smpi 			return 0;
112192095f7Smpi 		}
113192095f7Smpi 	}
114192095f7Smpi 	return -1;
115192095f7Smpi }
116192095f7Smpi 
117192095f7Smpi static int
dw_read_sleb128(struct dwbuf * d,int64_t * v)118192095f7Smpi dw_read_sleb128(struct dwbuf *d, int64_t *v)
119192095f7Smpi {
120192095f7Smpi 	return dw_read_leb128(d, (uint64_t *)v, 1);
121192095f7Smpi }
122192095f7Smpi 
123192095f7Smpi static int
dw_read_uleb128(struct dwbuf * d,uint64_t * v)124192095f7Smpi dw_read_uleb128(struct dwbuf *d, uint64_t *v)
125192095f7Smpi {
126192095f7Smpi 	return dw_read_leb128(d, v, 0);
127192095f7Smpi }
128192095f7Smpi 
129192095f7Smpi /* Read a NUL terminated string. */
130192095f7Smpi static int
dw_read_string(struct dwbuf * d,const char ** s)131192095f7Smpi dw_read_string(struct dwbuf *d, const char **s)
132192095f7Smpi {
133192095f7Smpi 	const char *end = memchr(d->buf, '\0', d->len);
134192095f7Smpi 	size_t n;
135192095f7Smpi 
136192095f7Smpi 	if (end == NULL)
137192095f7Smpi 		return -1;
138192095f7Smpi 
139192095f7Smpi 	n = end - d->buf + 1;
140192095f7Smpi 	*s = d->buf;
141192095f7Smpi 	d->buf += n;
142192095f7Smpi 	d->len -= n;
143192095f7Smpi 	return 0;
144192095f7Smpi }
145192095f7Smpi 
146192095f7Smpi static int
dw_read_buf(struct dwbuf * d,struct dwbuf * v,size_t n)147192095f7Smpi dw_read_buf(struct dwbuf *d, struct dwbuf *v, size_t n)
148192095f7Smpi {
149192095f7Smpi 	if (d->len < n)
150192095f7Smpi 		return -1;
151192095f7Smpi 	v->buf = d->buf;
152192095f7Smpi 	v->len = n;
153192095f7Smpi 	d->buf += n;
154192095f7Smpi 	d->len -= n;
155192095f7Smpi 	return 0;
156192095f7Smpi }
157192095f7Smpi 
158192095f7Smpi static int
dw_skip_bytes(struct dwbuf * d,size_t n)159192095f7Smpi dw_skip_bytes(struct dwbuf *d, size_t n)
160192095f7Smpi {
161192095f7Smpi 	if (d->len < n)
162192095f7Smpi 		return -1;
163192095f7Smpi 	d->buf += n;
164192095f7Smpi 	d->len -= n;
165192095f7Smpi 	return 0;
166192095f7Smpi }
167192095f7Smpi 
168192095f7Smpi const char *
dw_tag2name(uint64_t tag)169192095f7Smpi dw_tag2name(uint64_t tag)
170192095f7Smpi {
171192095f7Smpi 	static const char *dw_tags[] = { DW_TAG_NAMES };
172192095f7Smpi 
173192095f7Smpi 	if (tag <= nitems(dw_tags))
174192095f7Smpi 		return dw_tags[tag - 1];
175192095f7Smpi 
176192095f7Smpi 	if (tag == DW_TAG_lo_user)
177192095f7Smpi 		return "DW_TAG_lo_user";
178192095f7Smpi 	if (tag == DW_TAG_hi_user)
179192095f7Smpi 		return "DW_TAG_hi_user";
180192095f7Smpi 
181192095f7Smpi 	return NULL;
182192095f7Smpi }
183192095f7Smpi 
184192095f7Smpi const char *
dw_at2name(uint64_t at)185192095f7Smpi dw_at2name(uint64_t at)
186192095f7Smpi {
187192095f7Smpi 	static const char *dw_attrs[] = { DW_AT_NAMES };
188*283ced89Sclaudio 	static char buf[64];
189192095f7Smpi 
190192095f7Smpi 	if (at <= nitems(dw_attrs))
191192095f7Smpi 		return dw_attrs[at - 1];
192192095f7Smpi 
193192095f7Smpi 	if (at == DW_AT_lo_user)
194192095f7Smpi 		return "DW_AT_lo_user";
195192095f7Smpi 	if (at == DW_AT_hi_user)
196192095f7Smpi 		return "DW_AT_hi_user";
197192095f7Smpi 
198*283ced89Sclaudio 	snprintf(buf, sizeof(buf), "#%llu", at);
199*283ced89Sclaudio 	return buf;
200192095f7Smpi }
201192095f7Smpi 
202192095f7Smpi const char *
dw_form2name(uint64_t form)203192095f7Smpi dw_form2name(uint64_t form)
204192095f7Smpi {
205192095f7Smpi 	static const char *dw_forms[] = { DW_FORM_NAMES };
206192095f7Smpi 
207192095f7Smpi 	if (form <= nitems(dw_forms))
208192095f7Smpi 		return dw_forms[form - 1];
209192095f7Smpi 
210192095f7Smpi 	if (form == DW_FORM_GNU_ref_alt)
211192095f7Smpi 		return "DW_FORM_GNU_ref_alt";
212192095f7Smpi 	if (form == DW_FORM_GNU_strp_alt)
213192095f7Smpi 		return "DW_FORM_GNU_strp_alt";
214192095f7Smpi 
215192095f7Smpi 	return NULL;
216192095f7Smpi }
217192095f7Smpi 
218192095f7Smpi const char *
dw_op2name(uint8_t op)219192095f7Smpi dw_op2name(uint8_t op)
220192095f7Smpi {
221192095f7Smpi 	static const char *dw_ops[] = { DW_OP_NAMES };
222192095f7Smpi 
223192095f7Smpi 	if (op <= nitems(dw_ops))
224192095f7Smpi 		return dw_ops[op - 1];
225192095f7Smpi 
226192095f7Smpi 	if (op == DW_OP_lo_user)
227192095f7Smpi 		return "DW_OP_lo_user";
228192095f7Smpi 	if (op == DW_OP_hi_user)
229192095f7Smpi 		return "DW_OP_hi_user";
230192095f7Smpi 
231192095f7Smpi 	return NULL;
232192095f7Smpi }
233192095f7Smpi 
234192095f7Smpi static int
dw_attr_parse(struct dwbuf * dwbuf,struct dwattr * dat,uint8_t psz,struct dwaval_queue * davq)235192095f7Smpi dw_attr_parse(struct dwbuf *dwbuf, struct dwattr *dat, uint8_t psz,
236192095f7Smpi     struct dwaval_queue *davq)
237192095f7Smpi {
238192095f7Smpi 	struct dwaval	*dav;
239192095f7Smpi 	uint64_t	 form = dat->dat_form;
240192095f7Smpi 	int		 error = 0, i = 0;
241192095f7Smpi 
242192095f7Smpi 	while (form == DW_FORM_indirect) {
243192095f7Smpi 		/* XXX loop prevention not strict enough? */
244192095f7Smpi 		if (dw_read_uleb128(dwbuf, &form) || (++i > 3))
245192095f7Smpi 			return ELOOP;
246192095f7Smpi 	}
247192095f7Smpi 
248192095f7Smpi 	dav = pzalloc(&dav_pool, sizeof(*dav));
249192095f7Smpi 	if (dav == NULL)
250192095f7Smpi 		return ENOMEM;
251192095f7Smpi 
252192095f7Smpi 	dav->dav_dat = dat;
253192095f7Smpi 
254192095f7Smpi 	switch (form) {
255192095f7Smpi 	case DW_FORM_addr:
256192095f7Smpi 	case DW_FORM_ref_addr:
257192095f7Smpi 		if (psz == sizeof(uint32_t))
258192095f7Smpi 			error = dw_read_u32(dwbuf, &dav->dav_u32);
259192095f7Smpi 		else
260192095f7Smpi 			error = dw_read_u64(dwbuf, &dav->dav_u64);
261192095f7Smpi 		break;
262192095f7Smpi 	case DW_FORM_block1:
263192095f7Smpi 		error = dw_read_u8(dwbuf, &dav->dav_u8);
264192095f7Smpi 		if (error == 0)
265192095f7Smpi 			error = dw_read_buf(dwbuf, &dav->dav_buf, dav->dav_u8);
266192095f7Smpi 		break;
267192095f7Smpi 	case DW_FORM_block2:
268192095f7Smpi 		error = dw_read_u16(dwbuf, &dav->dav_u16);
269192095f7Smpi 		if (error == 0)
270192095f7Smpi 			error = dw_read_buf(dwbuf, &dav->dav_buf, dav->dav_u16);
271192095f7Smpi 		break;
272192095f7Smpi 	case DW_FORM_block4:
273192095f7Smpi 		error = dw_read_u32(dwbuf, &dav->dav_u32);
274192095f7Smpi 		if (error == 0)
275192095f7Smpi 			error = dw_read_buf(dwbuf, &dav->dav_buf, dav->dav_u32);
276192095f7Smpi 		break;
277192095f7Smpi 	case DW_FORM_block:
278192095f7Smpi 		error = dw_read_uleb128(dwbuf, &dav->dav_u64);
279192095f7Smpi 		if (error == 0)
280192095f7Smpi 			error = dw_read_buf(dwbuf, &dav->dav_buf, dav->dav_u64);
281192095f7Smpi 		break;
282192095f7Smpi 	case DW_FORM_data1:
283192095f7Smpi 	case DW_FORM_flag:
284192095f7Smpi 	case DW_FORM_ref1:
285192095f7Smpi 		error = dw_read_u8(dwbuf, &dav->dav_u8);
286192095f7Smpi 		break;
287192095f7Smpi 	case DW_FORM_data2:
288192095f7Smpi 	case DW_FORM_ref2:
289192095f7Smpi 		error = dw_read_u16(dwbuf, &dav->dav_u16);
290192095f7Smpi 		break;
291192095f7Smpi 	case DW_FORM_data4:
292192095f7Smpi 	case DW_FORM_ref4:
293192095f7Smpi 		error = dw_read_u32(dwbuf, &dav->dav_u32);
294192095f7Smpi 		break;
295192095f7Smpi 	case DW_FORM_data8:
296192095f7Smpi 	case DW_FORM_ref8:
297192095f7Smpi 		error = dw_read_u64(dwbuf, &dav->dav_u64);
298192095f7Smpi 		break;
299192095f7Smpi 	case DW_FORM_ref_udata:
300192095f7Smpi 	case DW_FORM_udata:
301192095f7Smpi 		error = dw_read_uleb128(dwbuf, &dav->dav_u64);
302192095f7Smpi 		break;
303192095f7Smpi 	case DW_FORM_sdata:
304192095f7Smpi 		error = dw_read_sleb128(dwbuf, &dav->dav_s64);
305192095f7Smpi 		break;
306192095f7Smpi 	case DW_FORM_string:
307192095f7Smpi 		error = dw_read_string(dwbuf, &dav->dav_str);
308192095f7Smpi 		break;
309192095f7Smpi 	case DW_FORM_strp:
310192095f7Smpi 		error = dw_read_u32(dwbuf, &dav->dav_u32);
311192095f7Smpi 		break;
312192095f7Smpi 	case DW_FORM_flag_present:
313192095f7Smpi 		dav->dav_u8 = 1;
314192095f7Smpi 		break;
315192095f7Smpi 	default:
316192095f7Smpi 		error = ENOENT;
317192095f7Smpi 		break;
318192095f7Smpi 	}
319192095f7Smpi 
320192095f7Smpi 	if (error) {
321192095f7Smpi 		pfree(&dav_pool, dav);
322192095f7Smpi 		return error;
323192095f7Smpi 	}
324192095f7Smpi 
325192095f7Smpi 	SIMPLEQ_INSERT_TAIL(davq, dav, dav_next);
326192095f7Smpi 	return 0;
327192095f7Smpi }
328192095f7Smpi 
329192095f7Smpi static void
dw_attr_purge(struct dwaval_queue * davq)330192095f7Smpi dw_attr_purge(struct dwaval_queue *davq)
331192095f7Smpi {
332192095f7Smpi 	struct dwaval	*dav;
333192095f7Smpi 
334192095f7Smpi 	while ((dav = SIMPLEQ_FIRST(davq)) != NULL) {
335192095f7Smpi 		SIMPLEQ_REMOVE_HEAD(davq, dav_next);
336192095f7Smpi 		pfree(&dav_pool, dav);
337192095f7Smpi 	}
338192095f7Smpi 
339192095f7Smpi 	SIMPLEQ_INIT(davq);
340192095f7Smpi }
341192095f7Smpi 
342192095f7Smpi static int
dw_die_parse(struct dwbuf * dwbuf,size_t nextoff,uint8_t psz,struct dwabbrev_queue * dabq,struct dwdie_queue * dieq)343192095f7Smpi dw_die_parse(struct dwbuf *dwbuf, size_t nextoff, uint8_t psz,
344192095f7Smpi     struct dwabbrev_queue *dabq, struct dwdie_queue *dieq)
345192095f7Smpi {
346192095f7Smpi 	struct dwdie	*die;
347192095f7Smpi 	struct dwabbrev	*dab;
348192095f7Smpi 	struct dwattr	*dat;
349192095f7Smpi 	uint64_t	 code;
350192095f7Smpi 	size_t		 doff;
351192095f7Smpi 	uint8_t		 lvl = 0;
352192095f7Smpi 	int		 error;
353192095f7Smpi 
354192095f7Smpi 
355192095f7Smpi 	while (dwbuf->len > 0) {
356192095f7Smpi 		doff = nextoff - dwbuf->len;
357192095f7Smpi 		if (dw_read_uleb128(dwbuf, &code))
358192095f7Smpi 			return -1;
359192095f7Smpi 
360192095f7Smpi 		if (code == 0) {
361192095f7Smpi 			lvl--;
362192095f7Smpi 			continue;
363192095f7Smpi 		}
364192095f7Smpi 
365192095f7Smpi 		SIMPLEQ_FOREACH(dab, dabq, dab_next) {
366192095f7Smpi 			if (dab->dab_code == code)
367192095f7Smpi 				break;
368192095f7Smpi 		}
369192095f7Smpi 		if (dab == NULL)
370192095f7Smpi 			return ESRCH;
371192095f7Smpi 
372192095f7Smpi 		die = pmalloc(&die_pool, sizeof(*die));
373192095f7Smpi 		if (die == NULL)
374192095f7Smpi 			return ENOMEM;
375192095f7Smpi 
376192095f7Smpi 		die->die_lvl = lvl;
377192095f7Smpi 		die->die_dab = dab;
378192095f7Smpi 		die->die_offset = doff;
379192095f7Smpi 		SIMPLEQ_INIT(&die->die_avals);
380192095f7Smpi 
381192095f7Smpi 		SIMPLEQ_FOREACH(dat, &dab->dab_attrs, dat_next) {
382192095f7Smpi 			error = dw_attr_parse(dwbuf, dat, psz, &die->die_avals);
383192095f7Smpi 			if (error != 0) {
384192095f7Smpi 				dw_attr_purge(&die->die_avals);
385192095f7Smpi 				return error;
386192095f7Smpi 			}
387192095f7Smpi 		}
388192095f7Smpi 
389192095f7Smpi 		if (dab->dab_children == DW_CHILDREN_yes)
390192095f7Smpi 			lvl++;
391192095f7Smpi 
392192095f7Smpi 		SIMPLEQ_INSERT_TAIL(dieq, die, die_next);
393192095f7Smpi 	}
394192095f7Smpi 
395192095f7Smpi 	return 0;
396192095f7Smpi }
397192095f7Smpi 
398192095f7Smpi static void
dw_die_purge(struct dwdie_queue * dieq)399192095f7Smpi dw_die_purge(struct dwdie_queue *dieq)
400192095f7Smpi {
401192095f7Smpi 	struct dwdie	*die;
402192095f7Smpi 
403192095f7Smpi 	while ((die = SIMPLEQ_FIRST(dieq)) != NULL) {
404192095f7Smpi 		SIMPLEQ_REMOVE_HEAD(dieq, die_next);
405192095f7Smpi 		dw_attr_purge(&die->die_avals);
406192095f7Smpi 		pfree(&die_pool, die);
407192095f7Smpi 	}
408192095f7Smpi 
409192095f7Smpi 	SIMPLEQ_INIT(dieq);
410192095f7Smpi }
411192095f7Smpi 
412192095f7Smpi int
dw_ab_parse(struct dwbuf * abseg,struct dwabbrev_queue * dabq)413192095f7Smpi dw_ab_parse(struct dwbuf *abseg, struct dwabbrev_queue *dabq)
414192095f7Smpi {
415192095f7Smpi 	struct dwabbrev	*dab;
416192095f7Smpi 	uint64_t	 code, tag;
417192095f7Smpi 	uint8_t		 children;
418192095f7Smpi 
419192095f7Smpi 	if (abseg->len == 0)
420192095f7Smpi 		return EINVAL;
421192095f7Smpi 
422192095f7Smpi 	for (;;) {
423192095f7Smpi 		if (dw_read_uleb128(abseg, &code) || (code == 0))
424192095f7Smpi 			break;
425192095f7Smpi 
426192095f7Smpi 		if (dw_read_uleb128(abseg, &tag) ||
427192095f7Smpi 		    dw_read_u8(abseg, &children))
428192095f7Smpi 			return -1;
429192095f7Smpi 
430192095f7Smpi 		dab = pmalloc(&dab_pool, sizeof(*dab));
431192095f7Smpi 		if (dab == NULL)
432192095f7Smpi 			return ENOMEM;
433192095f7Smpi 
434192095f7Smpi 		dab->dab_code = code;
435192095f7Smpi 		dab->dab_tag = tag;
436192095f7Smpi 		dab->dab_children = children;
437192095f7Smpi 		SIMPLEQ_INIT(&dab->dab_attrs);
438192095f7Smpi 
439192095f7Smpi 		SIMPLEQ_INSERT_TAIL(dabq, dab, dab_next);
440192095f7Smpi 
441192095f7Smpi 		for (;;) {
442192095f7Smpi 			struct dwattr *dat;
443192095f7Smpi 			uint64_t attr = 0, form = 0;
444192095f7Smpi 
445192095f7Smpi 			if (dw_read_uleb128(abseg, &attr) ||
446192095f7Smpi 			    dw_read_uleb128(abseg, &form))
447192095f7Smpi 				return -1;
448192095f7Smpi 
449192095f7Smpi 			if ((attr == 0) && (form == 0))
450192095f7Smpi 				break;
451192095f7Smpi 
452192095f7Smpi 			dat = pmalloc(&dat_pool, sizeof(*dat));
453192095f7Smpi 			if (dat == NULL)
454192095f7Smpi 				return ENOMEM;
455192095f7Smpi 
456192095f7Smpi 			dat->dat_attr = attr;
457192095f7Smpi 			dat->dat_form = form;
458192095f7Smpi 
459192095f7Smpi 			SIMPLEQ_INSERT_TAIL(&dab->dab_attrs, dat, dat_next);
460192095f7Smpi 		}
461192095f7Smpi 	}
462192095f7Smpi 
463192095f7Smpi 	return 0;
464192095f7Smpi }
465192095f7Smpi 
466192095f7Smpi void
dw_dabq_purge(struct dwabbrev_queue * dabq)467192095f7Smpi dw_dabq_purge(struct dwabbrev_queue *dabq)
468192095f7Smpi {
469192095f7Smpi 	struct dwabbrev	*dab;
470192095f7Smpi 
471192095f7Smpi 	while ((dab = SIMPLEQ_FIRST(dabq)) != NULL) {
472192095f7Smpi 		struct dwattr *dat;
473192095f7Smpi 
474192095f7Smpi 		SIMPLEQ_REMOVE_HEAD(dabq, dab_next);
475192095f7Smpi 		while ((dat = SIMPLEQ_FIRST(&dab->dab_attrs)) != NULL) {
476192095f7Smpi 			SIMPLEQ_REMOVE_HEAD(&dab->dab_attrs, dat_next);
477192095f7Smpi 			pfree(&dat_pool, dat);
478192095f7Smpi 		}
479192095f7Smpi 
480192095f7Smpi 		pfree(&dab_pool, dab);
481192095f7Smpi 	}
482192095f7Smpi 
483192095f7Smpi 	SIMPLEQ_INIT(dabq);
484192095f7Smpi }
485192095f7Smpi 
486192095f7Smpi int
dw_cu_parse(struct dwbuf * info,struct dwbuf * abbrev,size_t seglen,struct dwcu ** dcup)487192095f7Smpi dw_cu_parse(struct dwbuf *info, struct dwbuf *abbrev, size_t seglen,
488192095f7Smpi     struct dwcu **dcup)
489192095f7Smpi {
490192095f7Smpi 	struct dwbuf	 abseg = *abbrev;
491192095f7Smpi 	struct dwbuf	 dwbuf;
492192095f7Smpi 	size_t		 segoff, nextoff, addrsize;
493192095f7Smpi 	struct dwcu	*dcu = NULL;
494192095f7Smpi 	uint32_t	 length = 0, abbroff = 0;
495192095f7Smpi 	uint16_t	 version;
496192095f7Smpi 	uint8_t		 psz;
497192095f7Smpi 	int		 error;
498192095f7Smpi #ifndef NOPOOL
499192095f7Smpi 	static int 	 dw_pool_inited = 0;
500192095f7Smpi 
501192095f7Smpi 	if (!dw_pool_inited) {
502192095f7Smpi 		pool_init(&dcu_pool, "dcu", 1, sizeof(struct dwcu));
503192095f7Smpi 		pool_init(&dab_pool, "dab", 32, sizeof(struct dwabbrev));
504192095f7Smpi 		pool_init(&dat_pool, "dat", 32, sizeof(struct dwattr));
505192095f7Smpi 		pool_init(&die_pool, "die", 512, sizeof(struct dwdie));
506192095f7Smpi 		pool_init(&dav_pool, "dav", 1024, sizeof(struct dwaval));
507192095f7Smpi 		dw_pool_inited = 1;
508192095f7Smpi 	}
509192095f7Smpi #endif /* NOPOOL */
510192095f7Smpi 
511192095f7Smpi 	if (info->len == 0 || abbrev->len == 0)
512192095f7Smpi 		return EINVAL;
513192095f7Smpi 
514192095f7Smpi 	/* Offset in the segment of the current Compile Unit. */
515192095f7Smpi 	segoff = seglen - info->len;
516192095f7Smpi 
517192095f7Smpi 	if (dw_read_u32(info, &length))
518192095f7Smpi 		return -1;
519192095f7Smpi 
520192095f7Smpi 	if (length >= 0xfffffff0 || length > info->len)
521192095f7Smpi 		return EOVERFLOW;
522192095f7Smpi 
523b7b99cbeSanton 	/* Offset of the next Compile Unit. */
524192095f7Smpi 	nextoff = segoff + length + sizeof(uint32_t);
525192095f7Smpi 
526192095f7Smpi 	if (dw_read_buf(info, &dwbuf, length))
527192095f7Smpi 		return -1;
528192095f7Smpi 
529192095f7Smpi 	addrsize = 4; /* XXX */
530192095f7Smpi 
531192095f7Smpi 	if (dw_read_u16(&dwbuf, &version) ||
532192095f7Smpi 	    dw_read_bytes(&dwbuf, &abbroff, addrsize) ||
533192095f7Smpi 	    dw_read_u8(&dwbuf, &psz))
534192095f7Smpi 		return -1;
535192095f7Smpi 
536192095f7Smpi 	if (dw_skip_bytes(&abseg, abbroff))
537192095f7Smpi 		return -1;
538192095f7Smpi 
539192095f7Smpi 	/* Only DWARF2 until extended. */
540192095f7Smpi 	if (version != 2)
541192095f7Smpi 		return ENOTSUP;
542192095f7Smpi 
543192095f7Smpi 	dcu = pmalloc(&dcu_pool, sizeof(*dcu));
544192095f7Smpi 	if (dcu == NULL)
545192095f7Smpi 		return ENOMEM;
546192095f7Smpi 
547192095f7Smpi 	dcu->dcu_offset = segoff;
548192095f7Smpi 	dcu->dcu_length = length;
549192095f7Smpi 	dcu->dcu_version = version;
550192095f7Smpi 	dcu->dcu_abbroff = abbroff;
551192095f7Smpi 	dcu->dcu_psize = psz;
552192095f7Smpi 	SIMPLEQ_INIT(&dcu->dcu_abbrevs);
553192095f7Smpi 	SIMPLEQ_INIT(&dcu->dcu_dies);
554192095f7Smpi 
555192095f7Smpi 	error = dw_ab_parse(&abseg, &dcu->dcu_abbrevs);
556192095f7Smpi 	if (error != 0) {
557192095f7Smpi 		dw_dcu_free(dcu);
558192095f7Smpi 		return error;
559192095f7Smpi 	}
560192095f7Smpi 
561192095f7Smpi 	error = dw_die_parse(&dwbuf, nextoff, psz, &dcu->dcu_abbrevs,
562192095f7Smpi 	    &dcu->dcu_dies);
563192095f7Smpi 	if (error != 0) {
564192095f7Smpi 		dw_dcu_free(dcu);
565192095f7Smpi 		return error;
566192095f7Smpi 	}
567192095f7Smpi 
568192095f7Smpi 	if (dcup != NULL)
569192095f7Smpi 		*dcup = dcu;
570192095f7Smpi 	else
571192095f7Smpi 		dw_dcu_free(dcu);
572192095f7Smpi 
573192095f7Smpi 	return 0;
574192095f7Smpi }
575192095f7Smpi 
576192095f7Smpi void
dw_dcu_free(struct dwcu * dcu)577192095f7Smpi dw_dcu_free(struct dwcu *dcu)
578192095f7Smpi {
579192095f7Smpi 	if (dcu == NULL)
580192095f7Smpi 		return;
581192095f7Smpi 
582192095f7Smpi 	dw_die_purge(&dcu->dcu_dies);
583192095f7Smpi 	dw_dabq_purge(&dcu->dcu_abbrevs);
584192095f7Smpi 	pfree(&dcu_pool, dcu);
585192095f7Smpi }
586192095f7Smpi 
587192095f7Smpi int
dw_loc_parse(struct dwbuf * dwbuf,uint8_t * pop,uint64_t * poper1,uint64_t * poper2)588192095f7Smpi dw_loc_parse(struct dwbuf *dwbuf, uint8_t *pop, uint64_t *poper1,
589192095f7Smpi     uint64_t *poper2)
590192095f7Smpi {
591192095f7Smpi 	uint64_t oper1 = 0, oper2 = 0;
592192095f7Smpi 	uint8_t op;
593192095f7Smpi 
594192095f7Smpi 	if (dw_read_u8(dwbuf, &op))
595192095f7Smpi 		return -1;
596192095f7Smpi 
597192095f7Smpi 	if (pop != NULL)
598192095f7Smpi 		*pop = op;
599192095f7Smpi 
600192095f7Smpi 	switch (op) {
601c4534afaSmpi 	case DW_OP_constu:
602192095f7Smpi 	case DW_OP_plus_uconst:
603c4534afaSmpi 	case DW_OP_regx:
604c4534afaSmpi 	case DW_OP_piece:
605192095f7Smpi 		dw_read_uleb128(dwbuf, &oper1);
606192095f7Smpi 		break;
607c4534afaSmpi 
608c4534afaSmpi 	case DW_OP_consts:
609c4534afaSmpi 	case DW_OP_breg0 ... DW_OP_breg31:
610c4534afaSmpi 	case DW_OP_fbreg:
611c4534afaSmpi 		dw_read_sleb128(dwbuf, &oper1);
612c4534afaSmpi 		break;
613192095f7Smpi 	default:
614192095f7Smpi 		return ENOTSUP;
615192095f7Smpi 	}
616192095f7Smpi 
617192095f7Smpi 	if (poper1 != NULL)
618192095f7Smpi 		*poper1 = oper1;
619192095f7Smpi 	if (poper2 != NULL)
620192095f7Smpi 		*poper2 = oper2;
621192095f7Smpi 
622192095f7Smpi 	return 0;
623192095f7Smpi }
624