// SPDX-License-Identifier: Apache-2.0 // // 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; }