1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2024 Google LLC
4 */
5
6 #define _GNU_SOURCE
7 #include <errno.h>
8 #include <stdio.h>
9
10 #include "gendwarfksyms.h"
11
12 #define KABI_RULE_SECTION ".discard.gendwarfksyms.kabi_rules"
13 #define KABI_RULE_VERSION "1"
14
15 /*
16 * The rule section consists of four null-terminated strings per
17 * entry:
18 *
19 * 1. version
20 * Entry format version. Must match KABI_RULE_VERSION.
21 *
22 * 2. type
23 * Type of the kABI rule. Must be one of the tags defined below.
24 *
25 * 3. target
26 * Rule-dependent target, typically the fully qualified name of
27 * the target DIE.
28 *
29 * 4. value
30 * Rule-dependent value.
31 */
32 #define KABI_RULE_MIN_ENTRY_SIZE \
33 (/* version\0 */ 2 + /* type\0 */ 2 + /* target\0" */ 1 + \
34 /* value\0 */ 1)
35 #define KABI_RULE_EMPTY_VALUE ""
36
37 /*
38 * Rule: declonly
39 * - For the struct/enum/union in the target field, treat it as a
40 * declaration only even if a definition is available.
41 */
42 #define KABI_RULE_TAG_DECLONLY "declonly"
43
44 /*
45 * Rule: enumerator_ignore
46 * - For the enum_field in the target field, ignore the enumerator.
47 */
48 #define KABI_RULE_TAG_ENUMERATOR_IGNORE "enumerator_ignore"
49
50 /*
51 * Rule: enumerator_value
52 * - For the fqn_field in the target field, set the value to the
53 * unsigned integer in the value field.
54 */
55 #define KABI_RULE_TAG_ENUMERATOR_VALUE "enumerator_value"
56
57 enum kabi_rule_type {
58 KABI_RULE_TYPE_UNKNOWN,
59 KABI_RULE_TYPE_DECLONLY,
60 KABI_RULE_TYPE_ENUMERATOR_IGNORE,
61 KABI_RULE_TYPE_ENUMERATOR_VALUE,
62 };
63
64 #define RULE_HASH_BITS 7
65
66 struct rule {
67 enum kabi_rule_type type;
68 const char *target;
69 const char *value;
70 struct hlist_node hash;
71 };
72
73 /* { type, target } -> struct rule */
74 static HASHTABLE_DEFINE(rules, 1 << RULE_HASH_BITS);
75
rule_values_hash(enum kabi_rule_type type,const char * target)76 static inline unsigned int rule_values_hash(enum kabi_rule_type type,
77 const char *target)
78 {
79 return hash_32(type) ^ hash_str(target);
80 }
81
rule_hash(const struct rule * rule)82 static inline unsigned int rule_hash(const struct rule *rule)
83 {
84 return rule_values_hash(rule->type, rule->target);
85 }
86
get_rule_field(const char ** pos,ssize_t * left)87 static inline const char *get_rule_field(const char **pos, ssize_t *left)
88 {
89 const char *start = *pos;
90 size_t len;
91
92 if (*left <= 0)
93 error("unexpected end of kABI rules");
94
95 len = strnlen(start, *left) + 1;
96 *pos += len;
97 *left -= len;
98
99 return start;
100 }
101
kabi_read_rules(int fd)102 void kabi_read_rules(int fd)
103 {
104 GElf_Shdr shdr_mem;
105 GElf_Shdr *shdr;
106 Elf_Data *rule_data = NULL;
107 Elf_Scn *scn;
108 Elf *elf;
109 size_t shstrndx;
110 const char *rule_str;
111 ssize_t left;
112 int i;
113
114 const struct {
115 enum kabi_rule_type type;
116 const char *tag;
117 } rule_types[] = {
118 {
119 .type = KABI_RULE_TYPE_DECLONLY,
120 .tag = KABI_RULE_TAG_DECLONLY,
121 },
122 {
123 .type = KABI_RULE_TYPE_ENUMERATOR_IGNORE,
124 .tag = KABI_RULE_TAG_ENUMERATOR_IGNORE,
125 },
126 {
127 .type = KABI_RULE_TYPE_ENUMERATOR_VALUE,
128 .tag = KABI_RULE_TAG_ENUMERATOR_VALUE,
129 },
130 };
131
132 if (!stable)
133 return;
134
135 if (elf_version(EV_CURRENT) != EV_CURRENT)
136 error("elf_version failed: %s", elf_errmsg(-1));
137
138 elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
139 if (!elf)
140 error("elf_begin failed: %s", elf_errmsg(-1));
141
142 if (elf_getshdrstrndx(elf, &shstrndx) < 0)
143 error("elf_getshdrstrndx failed: %s", elf_errmsg(-1));
144
145 scn = elf_nextscn(elf, NULL);
146
147 while (scn) {
148 const char *sname;
149
150 shdr = gelf_getshdr(scn, &shdr_mem);
151 if (!shdr)
152 error("gelf_getshdr failed: %s", elf_errmsg(-1));
153
154 sname = elf_strptr(elf, shstrndx, shdr->sh_name);
155 if (!sname)
156 error("elf_strptr failed: %s", elf_errmsg(-1));
157
158 if (!strcmp(sname, KABI_RULE_SECTION)) {
159 rule_data = elf_getdata(scn, NULL);
160 if (!rule_data)
161 error("elf_getdata failed: %s", elf_errmsg(-1));
162 break;
163 }
164
165 scn = elf_nextscn(elf, scn);
166 }
167
168 if (!rule_data) {
169 debug("kABI rules not found");
170 check(elf_end(elf));
171 return;
172 }
173
174 rule_str = rule_data->d_buf;
175 left = shdr->sh_size;
176
177 if (left < KABI_RULE_MIN_ENTRY_SIZE)
178 error("kABI rule section too small: %zd bytes", left);
179
180 if (rule_str[left - 1] != '\0')
181 error("kABI rules are not null-terminated");
182
183 while (left > KABI_RULE_MIN_ENTRY_SIZE) {
184 enum kabi_rule_type type = KABI_RULE_TYPE_UNKNOWN;
185 const char *field;
186 struct rule *rule;
187
188 /* version */
189 field = get_rule_field(&rule_str, &left);
190
191 if (strcmp(field, KABI_RULE_VERSION))
192 error("unsupported kABI rule version: '%s'", field);
193
194 /* type */
195 field = get_rule_field(&rule_str, &left);
196
197 for (i = 0; i < ARRAY_SIZE(rule_types); i++) {
198 if (!strcmp(field, rule_types[i].tag)) {
199 type = rule_types[i].type;
200 break;
201 }
202 }
203
204 if (type == KABI_RULE_TYPE_UNKNOWN)
205 error("unsupported kABI rule type: '%s'", field);
206
207 rule = xmalloc(sizeof(struct rule));
208
209 rule->type = type;
210 rule->target = xstrdup(get_rule_field(&rule_str, &left));
211 rule->value = xstrdup(get_rule_field(&rule_str, &left));
212
213 hash_add(rules, &rule->hash, rule_hash(rule));
214
215 debug("kABI rule: type: '%s', target: '%s', value: '%s'", field,
216 rule->target, rule->value);
217 }
218
219 if (left > 0)
220 warn("unexpected data at the end of the kABI rules section");
221
222 check(elf_end(elf));
223 }
224
kabi_is_declonly(const char * fqn)225 bool kabi_is_declonly(const char *fqn)
226 {
227 struct rule *rule;
228
229 if (!stable)
230 return false;
231 if (!fqn || !*fqn)
232 return false;
233
234 hash_for_each_possible(rules, rule, hash,
235 rule_values_hash(KABI_RULE_TYPE_DECLONLY, fqn)) {
236 if (rule->type == KABI_RULE_TYPE_DECLONLY &&
237 !strcmp(fqn, rule->target))
238 return true;
239 }
240
241 return false;
242 }
243
get_enumerator_target(const char * fqn,const char * field)244 static char *get_enumerator_target(const char *fqn, const char *field)
245 {
246 char *target = NULL;
247
248 if (asprintf(&target, "%s %s", fqn, field) < 0)
249 error("asprintf failed for '%s %s'", fqn, field);
250
251 return target;
252 }
253
get_ulong_value(const char * value)254 static unsigned long get_ulong_value(const char *value)
255 {
256 unsigned long result = 0;
257 char *endptr = NULL;
258
259 errno = 0;
260 result = strtoul(value, &endptr, 10);
261
262 if (errno || *endptr)
263 error("invalid unsigned value '%s'", value);
264
265 return result;
266 }
267
kabi_is_enumerator_ignored(const char * fqn,const char * field)268 bool kabi_is_enumerator_ignored(const char *fqn, const char *field)
269 {
270 bool match = false;
271 struct rule *rule;
272 char *target;
273
274 if (!stable)
275 return false;
276 if (!fqn || !*fqn || !field || !*field)
277 return false;
278
279 target = get_enumerator_target(fqn, field);
280
281 hash_for_each_possible(
282 rules, rule, hash,
283 rule_values_hash(KABI_RULE_TYPE_ENUMERATOR_IGNORE, target)) {
284 if (rule->type == KABI_RULE_TYPE_ENUMERATOR_IGNORE &&
285 !strcmp(target, rule->target)) {
286 match = true;
287 break;
288 }
289 }
290
291 free(target);
292 return match;
293 }
294
kabi_get_enumerator_value(const char * fqn,const char * field,unsigned long * value)295 bool kabi_get_enumerator_value(const char *fqn, const char *field,
296 unsigned long *value)
297 {
298 bool match = false;
299 struct rule *rule;
300 char *target;
301
302 if (!stable)
303 return false;
304 if (!fqn || !*fqn || !field || !*field)
305 return false;
306
307 target = get_enumerator_target(fqn, field);
308
309 hash_for_each_possible(rules, rule, hash,
310 rule_values_hash(KABI_RULE_TYPE_ENUMERATOR_VALUE,
311 target)) {
312 if (rule->type == KABI_RULE_TYPE_ENUMERATOR_VALUE &&
313 !strcmp(target, rule->target)) {
314 *value = get_ulong_value(rule->value);
315 match = true;
316 break;
317 }
318 }
319
320 free(target);
321 return match;
322 }
323
kabi_free(void)324 void kabi_free(void)
325 {
326 struct hlist_node *tmp;
327 struct rule *rule;
328
329 hash_for_each_safe(rules, rule, tmp, hash) {
330 free((void *)rule->target);
331 free((void *)rule->value);
332 free(rule);
333 }
334
335 hash_init(rules);
336 }
337