1/*
2 * sym.c
3 * lockstat
4 *
5 * Created by Samuel Gosselin on 10/1/14.
6 * Copyright 2014 Apple Inc. All rights reserved.
7 *
8 */
9
10#include <CoreSymbolication/CoreSymbolication.h>
11#include <CoreSymbolication/CoreSymbolicationPrivate.h>
12
13#include <libkern/OSAtomic.h>
14#include <sys/sysctl.h>
15
16#define LAST_SEGMENT_TOKEN	"__LAST SEGMENT"
17#define SYMBOL_NAME_MAX_LENGTH	(16)
18#define MIN_PAGE_SIZE		(4 * 1024)
19
20typedef struct {
21	uintptr_t	location;
22	size_t		length;
23	char const	*name;
24} sym_t;
25
26static sym_t			*g_symtable;
27static size_t			g_nbsyms;
28static size_t			g_maxsyms;
29static CSSymbolicatorRef 	g_symbolicator;
30
31static int
32add_symbol(uintptr_t location, size_t length, char const* name)
33{
34	sym_t *sym;
35
36	if (g_symtable == NULL || g_nbsyms >= g_maxsyms)
37		return -1;
38
39	sym = &g_symtable[g_nbsyms++];
40	sym->location = location;
41	sym->length = length;
42	sym->name = name;
43
44	return 0;
45}
46
47static int
48create_fake_symbols(CSRange table_range)
49{
50	int i, ncpus;
51	size_t len = sizeof(ncpus);
52	char *name;
53
54	assert(table_range.length > 0);
55	assert(table_range.location > NULL);
56
57	/* retrieve the number of cpus */
58	if (sysctlbyname("hw.ncpu", &ncpus, &len, NULL, 0) < 0) {
59		fprintf(stderr, "could not retrieve the number of cpus in the system\n");
60		return -1;
61	}
62
63	assert(ncpus > 0);
64
65	/* Check if we will have enough room for the symbols */
66	if (ncpus > table_range.length) {
67		fprintf(stderr, "symbols table not big enough to store the fake cpu symbols\n");
68		return -1;
69	}
70
71	/*
72	 * Currently, we're only storing fake symbols for the cpus, and the size
73	 * is already known.
74	 */
75	g_maxsyms = ncpus;
76	if (!(g_symtable = malloc(sizeof(sym_t) * ncpus))) {
77		fprintf(stderr, "could not allocate memory for the symbols table\n");
78		return -1;
79	}
80
81	/* allocate a new symbol for each cpu in the system */
82	for (i = 0; i < ncpus; ++i) {
83		if (!(name = malloc(SYMBOL_NAME_MAX_LENGTH))) {
84			fprintf(stderr, "could not allocate memory for the cpu[%d] symbol\n", i);
85			return -1;
86		}
87
88		(void) snprintf(name, SYMBOL_NAME_MAX_LENGTH, "cpu[%d]", i);
89
90		if (add_symbol(table_range.location + i, 0, name) < 0) {
91			fprintf(stderr, "could not add the symbol [%s] to the symbol table", name);
92			return -1;
93		}
94	}
95
96	return 0;
97}
98
99static int
100find_symtab_range(CSRange *res)
101{
102	__block CSSegmentRef	last_segment;
103	__block CSRange		last_section_range;
104	CSRange			last_segment_range;
105
106	assert(res);
107
108	/* locate the kernel last segment */
109	CSSymbolicatorForeachSegmentAtTime(g_symbolicator, kCSNow, ^(CSSegmentRef it) {
110		if (!strcmp(LAST_SEGMENT_TOKEN, CSRegionGetName(it)))
111			last_segment = it;
112	});
113
114	/* ensure that the segment can store our symbols table */
115	last_segment_range = CSRegionGetRange(last_segment);
116	if (last_segment_range.length < MIN_PAGE_SIZE)
117		return -1;
118
119	/* locate the last section in the last segment */
120	CSSegmentForeachSection(last_segment, ^(CSSectionRef it) {
121		last_section_range = CSRegionGetRange(it);
122	});
123
124	/* no section found, create an empty section */
125	if (!last_section_range.location)
126		last_section_range = CSRangeMake(last_segment_range.location, 0);
127
128	/* initialize the symbols table range */
129	res->location = CSRangeMax(last_section_range);
130	res->length = CSRangeMax(last_segment_range) - res->location;
131
132	/* be sure that we're not returning an incorrect range */
133	assert(CSRangeContainsRange(last_segment_range, *res));
134
135	return 0;
136}
137
138int
139symtab_init(void)
140{
141	CSRange table_range;
142
143	enum CSSymbolicatorPrivateFlags symflags = 0x0;
144	symflags |= kCSSymbolicatorDefaultCreateFlags;
145	symflags |= kCSSymbolicatorUseSlidKernelAddresses;
146
147	/* retrieve the kernel symbolicator */
148	g_symbolicator = CSSymbolicatorCreateWithMachKernelFlagsAndNotification(symflags, NULL);
149	if (CSIsNull(g_symbolicator)) {
150		fprintf(stderr, "could not retrieve the kernel symbolicator\n");
151		return -1;
152	}
153
154	/* retrieve the range for the table */
155	if (find_symtab_range(&table_range) < 0) {
156		fprintf(stderr, "could not find a valid range for the symbols table\n");
157		return -1;
158	}
159
160	return create_fake_symbols(table_range);
161}
162
163char const*
164addr_to_sym(uintptr_t addr, uintptr_t *offset, size_t *sizep)
165{
166	CSSymbolRef symbol;
167	CSRange	range;
168	int i;
169
170	assert(offset);
171	assert(sizep);
172
173	symbol = CSSymbolicatorGetSymbolWithAddressAtTime(g_symbolicator, addr, kCSNow);
174	if (!CSIsNull(symbol)) {
175		range = CSSymbolGetRange(symbol);
176		*offset = addr - range.location;
177		*sizep = range.length;
178		return CSSymbolGetName(symbol);
179	}
180
181	for (i = 0; i < g_nbsyms; ++i) {
182		sym_t* sym = &g_symtable[i];
183		if ((addr >= sym->location) && (addr <= sym->location + sym->length)) {
184			*offset = addr - sym->location;
185			*sizep = sym->length;
186			return sym->name;
187		}
188	}
189
190	return NULL;
191}
192
193uintptr_t
194sym_to_addr(char *name)
195{
196	CSSymbolRef symbol;
197	int i;
198
199	symbol = CSSymbolicatorGetSymbolWithNameAtTime(g_symbolicator, name, kCSNow);
200	if (!CSIsNull(symbol))
201		return CSSymbolGetRange(symbol).location;
202
203	for (i = 0; i < g_nbsyms; ++i)
204		if (!strcmp(name, g_symtable[i].name))
205			return g_symtable[i].location;
206
207	return NULL;
208}
209
210size_t
211sym_size(char *name)
212{
213	CSSymbolRef symbol;
214	int i;
215
216	symbol = CSSymbolicatorGetSymbolWithNameAtTime(g_symbolicator, name, kCSNow);
217	if (!CSIsNull(symbol))
218		return CSSymbolGetRange(symbol).length;
219
220	for (i = 0; i < g_nbsyms; ++i)
221		if (!strcmp(name, g_symtable[i].name))
222			return g_symtable[i].length;
223
224	return NULL;
225}
226
227