SchOS/ldls/btf.c
Nexoniarz 26bca020b6 licence: SchOS is GPL-2.0-or-later, no longer Apache-2.0
Nexoniarz is the sole author and SchOS was never published under
Apache-2.0, so this is a straight relicence. LICENSE is the FSF GPLv2 text,
every first-party SPDX header moves to GPL-2.0-or-later, and NOTICE,
README.md, docs/licensing.md and the site say so. projects/schfs-linux
follows the rest of the tree.

The GPL gives copyleft: a fork has to ship its source. It also lifts the
old "Linux drivers, never" rule, since GPL-2.0-only code combines with
GPL-2.0-or-later. "Or later" leaves room for GPLv3, but one binary still
has to pick one version, so docs/licensing.md now keeps Apache-2.0 and
GPLv3 code out of the kernel to keep Linux code possible there. BSD
4-clause code goes from "avoid" to "no", because the advertising clause
is GPL-incompatible.

LDLS still ships no .ko. That boundary is now project policy rather than
a legal requirement, and ldls.md, devices.md and licensing.md section 6
say so. refmod declares license=GPL, and ldls-check passes (27 checks).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 08:48:45 +02:00

332 lines
9.7 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
//
// Reading BTF. See ldls/btf.h.
//
// Implemented from Documentation/bpf/btf.rst. The format is small enough to
// hold in your head: a header, a run of variable-length type records, and a
// string table. The only fiddly part is that a record's length depends on its
// kind, so iterating means knowing how much trails each one -- which is the
// table in trailing_bytes() below and nothing more.
#include "btf.h"
static void
btf_fail(struct btf *b, const char *msg, const char *what)
{
int i = 0, j = 0;
while (msg[i] && i < (int)sizeof(b->err) - 1) { b->err[i] = msg[i]; i++; }
if (what)
while (what[j] && i < (int)sizeof(b->err) - 1) b->err[i++] = what[j++];
b->err[i] = 0;
}
static int
btf_streq(const char *a, const char *c)
{
while (*a && *a == *c) { a++; c++; }
return *a == 0 && *c == 0;
}
const char *btf_err(const struct btf *b) { return b->err[0] ? b->err : "no error"; }
// How many bytes follow a type record, given its kind and vlen.
static unsigned int
trailing_bytes(unsigned int info)
{
unsigned int kind = BTF_INFO_KIND(info);
unsigned int vlen = BTF_INFO_VLEN(info);
switch (kind) {
case BTF_KIND_INT: return 4;
case BTF_KIND_ARRAY: return 12; /* type, index_type, nelems */
case BTF_KIND_STRUCT:
case BTF_KIND_UNION: return vlen * 12; /* struct btf_member */
case BTF_KIND_ENUM: return vlen * 8;
case BTF_KIND_ENUM64: return vlen * 12;
case BTF_KIND_FUNC_PROTO: return vlen * 8; /* struct btf_param */
case BTF_KIND_VAR: return 4;
case BTF_KIND_DATASEC: return vlen * 12;
case BTF_KIND_DECL_TAG: return 4;
default: return 0; /* PTR, TYPEDEF, CONST, ... */
}
}
long
btf_count(const void *image, unsigned long long len)
{
const struct btf_header *h = (const struct btf_header *)image;
const unsigned char *types;
unsigned int at = 0;
long n = 0;
if (len < sizeof(struct btf_header))
return -1;
if (h->magic != BTF_MAGIC || h->version != 1)
return -1;
if ((unsigned long long)h->hdr_len + h->type_off + h->type_len > len)
return -1;
types = (const unsigned char *)image + h->hdr_len + h->type_off;
while (at + sizeof(struct btf_type) <= h->type_len) {
const struct btf_type *t = (const struct btf_type *)(types + at);
unsigned int step = sizeof(struct btf_type) + trailing_bytes(t->info);
if (step == 0 || at + step > h->type_len)
break;
at += step;
n++;
if (n > BTF_MAXTYPES)
return -1;
}
return n;
}
int
btf_open(struct btf *b, const void *image, unsigned long long len,
unsigned int *off, unsigned int noff)
{
const struct btf_header *h = (const struct btf_header *)image;
unsigned int at = 0, id = 1;
int i;
for (i = 0; i < (int)sizeof(b->err); i++)
b->err[i] = 0;
b->img = (const unsigned char *)image;
b->len = len;
b->off = off;
b->ntypes = 0;
if (len < sizeof(struct btf_header))
{ btf_fail(b, "too short to be BTF", 0); return -1; }
if (h->magic != BTF_MAGIC)
{ btf_fail(b, "not BTF: wrong magic", 0); return -1; }
if (h->version != 1)
{ btf_fail(b, "BTF version is not 1", 0); return -1; }
if ((unsigned long long)h->hdr_len + h->type_off + h->type_len > len ||
(unsigned long long)h->hdr_len + h->str_off + h->str_len > len)
{ btf_fail(b, "BTF sections run off the end of the file", 0); return -1; }
b->h = h;
b->types = b->img + h->hdr_len + h->type_off;
b->types_len = h->type_len;
b->str = (const char *)(b->img + h->hdr_len + h->str_off);
b->str_len = h->str_len;
// Index every type. Ids are assigned in order starting at 1; id 0 is void
// and is not stored.
while (at + sizeof(struct btf_type) <= b->types_len) {
const struct btf_type *t = (const struct btf_type *)(b->types + at);
unsigned int step = sizeof(struct btf_type) + trailing_bytes(t->info);
if (at + step > b->types_len)
break;
if (id >= noff)
{ btf_fail(b, "the caller's type index is too small", 0); return -1; }
off[id++] = at;
at += step;
}
b->ntypes = id - 1;
return 0;
}
static const struct btf_type *
btf_at(struct btf *b, int id)
{
if (id <= 0 || (unsigned int)id > b->ntypes)
return 0;
return (const struct btf_type *)(b->types + b->off[id]);
}
const char *
btf_name_of(struct btf *b, int id)
{
const struct btf_type *t = btf_at(b, id);
if (!t || t->name_off >= b->str_len)
return "";
return b->str + t->name_off;
}
int
btf_kind_of(struct btf *b, int id)
{
const struct btf_type *t = btf_at(b, id);
return t ? (int)BTF_INFO_KIND(t->info) : -1;
}
int
btf_find(struct btf *b, const char *name, int kind)
{
unsigned int id;
for (id = 1; id <= b->ntypes; id++) {
const struct btf_type *t = (const struct btf_type *)(b->types + b->off[id]);
if ((int)BTF_INFO_KIND(t->info) != kind)
continue;
if (t->name_off >= b->str_len)
continue;
if (btf_streq(b->str + t->name_off, name))
return (int)id;
}
return 0;
}
// The value of a named enumerator, wherever it lives.
//
// Not every constant a driver depends on is a struct offset. `netif_carrier_ok`
// is an inline that tests bit `__LINK_STATE_NOCARRIER` of `net_device.state`,
// so a host that wants to say "the link is down" has to set the right bit --
// and getting it wrong means a driver that believes the carrier is already up
// and skips everything it would have done on link-up. e1000 skips enabling its
// transmitter, which is not an error anywhere.
//
// The enum is anonymous, so there is no type to look up by name; the search is
// over enumerators rather than over types. Which is the same rule 3½ bargain as
// the offsets: the number is the installed kernel's, not ours.
//
// Returns 0 and sets *val on success, -1 if no enumerator has that name.
int
btf_enum_value(struct btf *b, const char *name, long long *val)
{
unsigned int id;
for (id = 1; id <= b->ntypes; id++) {
const struct btf_type *t = (const struct btf_type *)(b->types + b->off[id]);
int kind = (int)BTF_INFO_KIND(t->info);
unsigned int n = BTF_INFO_VLEN(t->info), i;
if (kind == BTF_KIND_ENUM) {
const unsigned int *e = (const unsigned int *)(t + 1);
for (i = 0; i < n; i++) {
unsigned int noff = e[i * 2];
if (noff < b->str_len && btf_streq(b->str + noff, name)) {
*val = (int)e[i * 2 + 1];
return 0;
}
}
} else if (kind == BTF_KIND_ENUM64) {
const unsigned int *e = (const unsigned int *)(t + 1);
for (i = 0; i < n; i++) {
unsigned int noff = e[i * 3];
if (noff < b->str_len && btf_streq(b->str + noff, name)) {
*val = (long long)e[i * 3 + 1] |
((long long)e[i * 3 + 2] << 32);
return 0;
}
}
}
}
btf_fail(b, "no enumerator named ", name);
return -1;
}
// Follow the things that wrap a type without changing its layout.
static int
btf_strip(struct btf *b, int id)
{
int guard = 0;
while (id > 0 && guard++ < 32) {
const struct btf_type *t = btf_at(b, id);
if (!t)
return 0;
switch (BTF_INFO_KIND(t->info)) {
case BTF_KIND_TYPEDEF:
case BTF_KIND_CONST:
case BTF_KIND_VOLATILE:
case BTF_KIND_RESTRICT:
case BTF_KIND_TYPE_TAG:
id = (int)t->u.type;
continue;
default:
return id;
}
}
return 0;
}
unsigned int
btf_type_size(struct btf *b, int id)
{
const struct btf_type *t;
id = btf_strip(b, id);
t = btf_at(b, id);
if (!t)
return 0;
switch (BTF_INFO_KIND(t->info)) {
case BTF_KIND_INT:
case BTF_KIND_STRUCT:
case BTF_KIND_UNION:
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
case BTF_KIND_FLOAT:
return t->u.size;
case BTF_KIND_PTR:
return 8; // x86-64; this reader is built for one ABI
case BTF_KIND_ARRAY: {
const unsigned int *a = (const unsigned int *)(t + 1);
return btf_type_size(b, (int)a[0]) * a[2];
}
default:
return 0;
}
}
int
btf_member_off(struct btf *b, int struct_id, const char *member,
unsigned int *off_bytes, unsigned int *size_bytes)
{
const struct btf_type *t;
const struct btf_member *m;
unsigned int i, vlen, kflag;
struct_id = btf_strip(b, struct_id);
t = btf_at(b, struct_id);
if (!t)
{ btf_fail(b, "no such type", 0); return -1; }
if (BTF_INFO_KIND(t->info) != BTF_KIND_STRUCT &&
BTF_INFO_KIND(t->info) != BTF_KIND_UNION)
{ btf_fail(b, "not a struct or union", 0); return -1; }
vlen = BTF_INFO_VLEN(t->info);
kflag = BTF_INFO_KFLAG(t->info);
m = (const struct btf_member *)(t + 1);
for (i = 0; i < vlen; i++, m++) {
const char *nm = (m->name_off < b->str_len) ? b->str + m->name_off : "";
unsigned int bit;
// An unnamed member is an anonymous struct or union, and what looks like
// a field of the outer type is really a field of that. Recurse, adding
// its own offset -- without this, every kernel struct with an anonymous
// union in it reports its later fields as missing.
if (nm[0] == 0) {
unsigned int sub_off, sub_size;
unsigned int here = kflag ? (m->offset & 0xffffff) : m->offset;
if (btf_member_off(b, (int)m->type, member, &sub_off, &sub_size) == 0) {
*off_bytes = here / 8 + sub_off;
*size_bytes = sub_size;
return 0;
}
continue;
}
if (!btf_streq(nm, member))
continue;
if (kflag && (m->offset >> 24) != 0)
{ btf_fail(b, "that member is a bitfield: ", member); return -1; }
bit = kflag ? (m->offset & 0xffffff) : m->offset;
if (bit % 8)
{ btf_fail(b, "that member is not byte-aligned: ", member); return -1; }
*off_bytes = bit / 8;
*size_bytes = btf_type_size(b, (int)m->type);
return 0;
}
btf_fail(b, "no such member: ", member);
return -1;
}