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>
332 lines
9.7 KiB
C
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;
|
|
}
|