aboutsummaryrefslogtreecommitdiff
path: root/src/libmach/dwarfabbrev.c
blob: 503d51492ae6dc694bdd624d9b35c8797978a267 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
 * Dwarf abbreviation parsing code.
 *
 * The convention here is that calling dwarfgetabbrevs relinquishes
 * access to any abbrevs returned previously.  Will have to add 
 * explicit reference counting if this turns out not to be acceptable.
 */

#include <u.h>
#include <libc.h>
#include <bio.h>
#include "elf.h"
#include "dwarf.h"

static int parseabbrevs(Dwarf*, ulong, DwarfAbbrev*, DwarfAttr*, int*, int*);
DwarfAbbrev *dwarfgetabbrev(Dwarf*, ulong, ulong);

static int
loadabbrevs(Dwarf *d, ulong off, DwarfAbbrev **aa)
{
	int nattr, nabbrev;
	DwarfAbbrev *abbrev;
	DwarfAttr *attr;

	if(d->acache.off == off && d->acache.na){
		*aa = d->acache.a;
		return d->acache.na;
	}

	/* two passes - once to count, then allocate, then a second to copy */
	if(parseabbrevs(d, off, nil, nil, &nabbrev, &nattr) < 0)
		return -1;

	abbrev = malloc(nabbrev*sizeof(DwarfAbbrev) + nattr*sizeof(DwarfAttr));
	attr = (DwarfAttr*)(abbrev+nabbrev);

	if(parseabbrevs(d, off, abbrev, attr, nil, nil) < 0){
		free(abbrev);
		return -1;
	}

	free(d->acache.a);
	d->acache.a = abbrev;
	d->acache.na = nabbrev;
	d->acache.off = off;

	*aa = abbrev;
	return nabbrev;
}

static int
parseabbrevs(Dwarf *d, ulong off, DwarfAbbrev *abbrev, DwarfAttr *attr, int *pnabbrev, int *pnattr)
{
	int i, nabbrev, nattr, haskids;
	ulong num, tag, name, form;
	DwarfBuf b;

	if(off >= d->abbrev.len){
		werrstr("bad abbrev section offset 0x%lux >= 0x%lux\n", off, d->abbrev.len);
		return -1;
	}

	memset(&b, 0, sizeof b);
	b.p = d->abbrev.data + off;
	b.ep = d->abbrev.data + d->abbrev.len;

	nabbrev = 0;
	nattr = 0;
	for(;;){
		if(b.p == nil){
			werrstr("malformed abbrev data");
			return -1;
		}
		num = dwarfget128(&b);
		if(num == 0)
			break;
		tag = dwarfget128(&b);
		haskids = dwarfget1(&b);
		for(i=0;; i++){
			name = dwarfget128(&b);
			form = dwarfget128(&b);
			if(name == 0 && form == 0)
				break;
			if(attr){
				attr[i].name = name;
				attr[i].form = form;
			}
		}
		if(abbrev){
			abbrev->num = num;
			abbrev->tag = tag;
			abbrev->haskids = haskids;
			abbrev->attr = attr;
			abbrev->nattr = i;
			abbrev++;
			attr += i;
		}
		nabbrev++;
		nattr += i;
	}
	if(pnabbrev)
		*pnabbrev = nabbrev;
	if(pnattr)
		*pnattr = nattr;
	return 0;
}

static DwarfAbbrev*
findabbrev(DwarfAbbrev *a, int na, ulong num)
{
	int i;

	for(i=0; i<na; i++)
		if(a[i].num == num)
			return &a[i];
	werrstr("abbrev not found");
	return nil;
}

DwarfAbbrev*
dwarfgetabbrev(Dwarf *d, ulong off, ulong num)
{
	DwarfAbbrev *a;
	int na;

	if((na = loadabbrevs(d, off, &a)) < 0){
		werrstr("loadabbrevs: %r");
		return nil;
	}
	return findabbrev(a, na, num);
}