mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-08-15 14:37:33 +00:00

The preferred form is to use the variable being allocated to, rather than explicitly supplying a type name which might become stale. Also do this for memset. Suggested-by: Masahiro Yamada <masahiroy@kernel.org> Signed-off-by: Giuliano Procida <gprocida@google.com> Reviewed-by: Sami Tolvanen <samitolvanen@google.com> Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
374 lines
7.8 KiB
C
374 lines
7.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2024 Google LLC
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
|
|
#include "gendwarfksyms.h"
|
|
|
|
#define KABI_RULE_SECTION ".discard.gendwarfksyms.kabi_rules"
|
|
#define KABI_RULE_VERSION "1"
|
|
|
|
/*
|
|
* The rule section consists of four null-terminated strings per
|
|
* entry:
|
|
*
|
|
* 1. version
|
|
* Entry format version. Must match KABI_RULE_VERSION.
|
|
*
|
|
* 2. type
|
|
* Type of the kABI rule. Must be one of the tags defined below.
|
|
*
|
|
* 3. target
|
|
* Rule-dependent target, typically the fully qualified name of
|
|
* the target DIE.
|
|
*
|
|
* 4. value
|
|
* Rule-dependent value.
|
|
*/
|
|
#define KABI_RULE_MIN_ENTRY_SIZE \
|
|
(/* version\0 */ 2 + /* type\0 */ 2 + /* target\0" */ 1 + \
|
|
/* value\0 */ 1)
|
|
#define KABI_RULE_EMPTY_VALUE ""
|
|
|
|
/*
|
|
* Rule: declonly
|
|
* - For the struct/enum/union in the target field, treat it as a
|
|
* declaration only even if a definition is available.
|
|
*/
|
|
#define KABI_RULE_TAG_DECLONLY "declonly"
|
|
|
|
/*
|
|
* Rule: enumerator_ignore
|
|
* - For the enum_field in the target field, ignore the enumerator.
|
|
*/
|
|
#define KABI_RULE_TAG_ENUMERATOR_IGNORE "enumerator_ignore"
|
|
|
|
/*
|
|
* Rule: enumerator_value
|
|
* - For the fqn_field in the target field, set the value to the
|
|
* unsigned integer in the value field.
|
|
*/
|
|
#define KABI_RULE_TAG_ENUMERATOR_VALUE "enumerator_value"
|
|
|
|
/*
|
|
* Rule: byte_size
|
|
* - For the fqn_field in the target field, set the byte_size
|
|
* attribute to the value in the value field.
|
|
*/
|
|
#define KABI_RULE_TAG_BYTE_SIZE "byte_size"
|
|
|
|
/*
|
|
* Rule: type_string
|
|
* - For the type reference in the fqn field, use the type string
|
|
* in the value field.
|
|
*/
|
|
#define KABI_RULE_TAG_TYPE_STRING "type_string"
|
|
|
|
enum kabi_rule_type {
|
|
KABI_RULE_TYPE_UNKNOWN,
|
|
KABI_RULE_TYPE_DECLONLY,
|
|
KABI_RULE_TYPE_ENUMERATOR_IGNORE,
|
|
KABI_RULE_TYPE_ENUMERATOR_VALUE,
|
|
KABI_RULE_TYPE_BYTE_SIZE,
|
|
KABI_RULE_TYPE_TYPE_STRING,
|
|
};
|
|
|
|
#define RULE_HASH_BITS 7
|
|
|
|
struct rule {
|
|
enum kabi_rule_type type;
|
|
const char *target;
|
|
const char *value;
|
|
struct hlist_node hash;
|
|
};
|
|
|
|
/* { type, target } -> struct rule */
|
|
static HASHTABLE_DEFINE(rules, 1 << RULE_HASH_BITS);
|
|
|
|
static inline unsigned int rule_values_hash(enum kabi_rule_type type,
|
|
const char *target)
|
|
{
|
|
return hash_32(type) ^ hash_str(target);
|
|
}
|
|
|
|
static inline unsigned int rule_hash(const struct rule *rule)
|
|
{
|
|
return rule_values_hash(rule->type, rule->target);
|
|
}
|
|
|
|
static inline const char *get_rule_field(const char **pos, ssize_t *left)
|
|
{
|
|
const char *start = *pos;
|
|
size_t len;
|
|
|
|
if (*left <= 0)
|
|
error("unexpected end of kABI rules");
|
|
|
|
len = strnlen(start, *left) + 1;
|
|
*pos += len;
|
|
*left -= len;
|
|
|
|
return start;
|
|
}
|
|
|
|
void kabi_read_rules(int fd)
|
|
{
|
|
GElf_Shdr shdr_mem;
|
|
GElf_Shdr *shdr;
|
|
Elf_Data *rule_data = NULL;
|
|
Elf_Scn *scn;
|
|
Elf *elf;
|
|
size_t shstrndx;
|
|
const char *rule_str;
|
|
ssize_t left;
|
|
int i;
|
|
|
|
const struct {
|
|
enum kabi_rule_type type;
|
|
const char *tag;
|
|
} rule_types[] = {
|
|
{
|
|
.type = KABI_RULE_TYPE_DECLONLY,
|
|
.tag = KABI_RULE_TAG_DECLONLY,
|
|
},
|
|
{
|
|
.type = KABI_RULE_TYPE_ENUMERATOR_IGNORE,
|
|
.tag = KABI_RULE_TAG_ENUMERATOR_IGNORE,
|
|
},
|
|
{
|
|
.type = KABI_RULE_TYPE_ENUMERATOR_VALUE,
|
|
.tag = KABI_RULE_TAG_ENUMERATOR_VALUE,
|
|
},
|
|
{
|
|
.type = KABI_RULE_TYPE_BYTE_SIZE,
|
|
.tag = KABI_RULE_TAG_BYTE_SIZE,
|
|
},
|
|
{
|
|
.type = KABI_RULE_TYPE_TYPE_STRING,
|
|
.tag = KABI_RULE_TAG_TYPE_STRING,
|
|
},
|
|
};
|
|
|
|
if (!stable)
|
|
return;
|
|
|
|
if (elf_version(EV_CURRENT) != EV_CURRENT)
|
|
error("elf_version failed: %s", elf_errmsg(-1));
|
|
|
|
elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
|
|
if (!elf)
|
|
error("elf_begin failed: %s", elf_errmsg(-1));
|
|
|
|
if (elf_getshdrstrndx(elf, &shstrndx) < 0)
|
|
error("elf_getshdrstrndx failed: %s", elf_errmsg(-1));
|
|
|
|
scn = elf_nextscn(elf, NULL);
|
|
|
|
while (scn) {
|
|
const char *sname;
|
|
|
|
shdr = gelf_getshdr(scn, &shdr_mem);
|
|
if (!shdr)
|
|
error("gelf_getshdr failed: %s", elf_errmsg(-1));
|
|
|
|
sname = elf_strptr(elf, shstrndx, shdr->sh_name);
|
|
if (!sname)
|
|
error("elf_strptr failed: %s", elf_errmsg(-1));
|
|
|
|
if (!strcmp(sname, KABI_RULE_SECTION)) {
|
|
rule_data = elf_getdata(scn, NULL);
|
|
if (!rule_data)
|
|
error("elf_getdata failed: %s", elf_errmsg(-1));
|
|
break;
|
|
}
|
|
|
|
scn = elf_nextscn(elf, scn);
|
|
}
|
|
|
|
if (!rule_data) {
|
|
debug("kABI rules not found");
|
|
check(elf_end(elf));
|
|
return;
|
|
}
|
|
|
|
rule_str = rule_data->d_buf;
|
|
left = shdr->sh_size;
|
|
|
|
if (left < KABI_RULE_MIN_ENTRY_SIZE)
|
|
error("kABI rule section too small: %zd bytes", left);
|
|
|
|
if (rule_str[left - 1] != '\0')
|
|
error("kABI rules are not null-terminated");
|
|
|
|
while (left > KABI_RULE_MIN_ENTRY_SIZE) {
|
|
enum kabi_rule_type type = KABI_RULE_TYPE_UNKNOWN;
|
|
const char *field;
|
|
struct rule *rule;
|
|
|
|
/* version */
|
|
field = get_rule_field(&rule_str, &left);
|
|
|
|
if (strcmp(field, KABI_RULE_VERSION))
|
|
error("unsupported kABI rule version: '%s'", field);
|
|
|
|
/* type */
|
|
field = get_rule_field(&rule_str, &left);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(rule_types); i++) {
|
|
if (!strcmp(field, rule_types[i].tag)) {
|
|
type = rule_types[i].type;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (type == KABI_RULE_TYPE_UNKNOWN)
|
|
error("unsupported kABI rule type: '%s'", field);
|
|
|
|
rule = xmalloc(sizeof(*rule));
|
|
|
|
rule->type = type;
|
|
rule->target = xstrdup(get_rule_field(&rule_str, &left));
|
|
rule->value = xstrdup(get_rule_field(&rule_str, &left));
|
|
|
|
hash_add(rules, &rule->hash, rule_hash(rule));
|
|
|
|
debug("kABI rule: type: '%s', target: '%s', value: '%s'", field,
|
|
rule->target, rule->value);
|
|
}
|
|
|
|
if (left > 0)
|
|
warn("unexpected data at the end of the kABI rules section");
|
|
|
|
check(elf_end(elf));
|
|
}
|
|
|
|
static char *get_enumerator_target(const char *fqn, const char *field)
|
|
{
|
|
char *target = NULL;
|
|
|
|
if (asprintf(&target, "%s %s", fqn, field) < 0)
|
|
error("asprintf failed for '%s %s'", fqn, field);
|
|
|
|
return target;
|
|
}
|
|
|
|
static struct rule *find_rule(enum kabi_rule_type type, const char *target)
|
|
{
|
|
struct rule *rule;
|
|
|
|
if (!stable)
|
|
return NULL;
|
|
if (!target || !*target)
|
|
return NULL;
|
|
|
|
hash_for_each_possible(rules, rule, hash,
|
|
rule_values_hash(type, target)) {
|
|
if (rule->type == type && !strcmp(target, rule->target))
|
|
return rule;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct rule *find_enumerator_rule(enum kabi_rule_type type,
|
|
const char *fqn, const char *field)
|
|
{
|
|
struct rule *rule;
|
|
char *target;
|
|
|
|
if (!stable)
|
|
return NULL;
|
|
if (!fqn || !*fqn || !field || !*field)
|
|
return NULL;
|
|
|
|
target = get_enumerator_target(fqn, field);
|
|
rule = find_rule(type, target);
|
|
|
|
free(target);
|
|
return rule;
|
|
}
|
|
|
|
bool kabi_is_declonly(const char *fqn)
|
|
{
|
|
return !!find_rule(KABI_RULE_TYPE_DECLONLY, fqn);
|
|
}
|
|
|
|
static unsigned long get_ulong_value(const char *value)
|
|
{
|
|
unsigned long result = 0;
|
|
char *endptr = NULL;
|
|
|
|
errno = 0;
|
|
result = strtoul(value, &endptr, 10);
|
|
|
|
if (errno || *endptr)
|
|
error("invalid unsigned value '%s'", value);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool kabi_is_enumerator_ignored(const char *fqn, const char *field)
|
|
{
|
|
return !!find_enumerator_rule(KABI_RULE_TYPE_ENUMERATOR_IGNORE, fqn,
|
|
field);
|
|
}
|
|
|
|
bool kabi_get_enumerator_value(const char *fqn, const char *field,
|
|
unsigned long *value)
|
|
{
|
|
struct rule *rule;
|
|
|
|
rule = find_enumerator_rule(KABI_RULE_TYPE_ENUMERATOR_VALUE, fqn,
|
|
field);
|
|
if (rule) {
|
|
*value = get_ulong_value(rule->value);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool kabi_get_byte_size(const char *fqn, unsigned long *value)
|
|
{
|
|
struct rule *rule;
|
|
|
|
rule = find_rule(KABI_RULE_TYPE_BYTE_SIZE, fqn);
|
|
if (rule) {
|
|
*value = get_ulong_value(rule->value);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool kabi_get_type_string(const char *type, const char **str)
|
|
{
|
|
struct rule *rule;
|
|
|
|
rule = find_rule(KABI_RULE_TYPE_TYPE_STRING, type);
|
|
if (rule) {
|
|
*str = rule->value;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void kabi_free(void)
|
|
{
|
|
struct hlist_node *tmp;
|
|
struct rule *rule;
|
|
|
|
hash_for_each_safe(rules, rule, tmp, hash) {
|
|
free((void *)rule->target);
|
|
free((void *)rule->value);
|
|
free(rule);
|
|
}
|
|
|
|
hash_init(rules);
|
|
}
|