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