296 lines · python
1#!/usr/bin/env python32 3# Ad-hoc script to print BTF file in a readable format.4# Follows the same printing conventions as bpftool with format 'raw'.5# Usage:6#7# ./print_btf.py <btf_file>8#9# Parameters:10#11# <btf_file> :: a file name or '-' to read from stdin.12#13# Intended usage:14#15# llvm-objcopy --dump-section .BTF=- <input> | ./print_btf.py -16#17# Kernel documentation contains detailed format description:18# https://www.kernel.org/doc/html/latest/bpf/btf.html19 20import struct21import ctypes22import sys23 24 25class SafeDict(dict):26 def __getitem__(self, key):27 try:28 return dict.__getitem__(self, key)29 except KeyError:30 return f"<BAD_KEY: {key}>"31 32 33KINDS = SafeDict(34 {35 0: "UNKN",36 1: "INT",37 2: "PTR",38 3: "ARRAY",39 4: "STRUCT",40 5: "UNION",41 6: "ENUM",42 7: "FWD",43 8: "TYPEDEF",44 9: "VOLATILE",45 10: "CONST",46 11: "RESTRICT",47 12: "FUNC",48 13: "FUNC_PROTO",49 14: "VAR",50 15: "DATASEC",51 16: "FLOAT",52 17: "DECL_TAG",53 18: "TYPE_TAG",54 19: "ENUM64",55 }56)57 58INT_ENCODING = SafeDict(59 {0 << 0: "(none)", 1 << 0: "SIGNED", 1 << 1: "CHAR", 1 << 2: "BOOL"}60)61 62ENUM_ENCODING = SafeDict({0: "UNSIGNED", 1: "SIGNED"})63 64FUNC_LINKAGE = SafeDict({0: "static", 1: "global", 2: "extern"})65 66VAR_LINKAGE = SafeDict({0: "static", 1: "global", 2: "extern"})67 68FWD_KIND = SafeDict(69 {70 0: "struct",71 1: "union",72 }73)74 75for val, name in KINDS.items():76 globals()["BTF_KIND_" + name] = val77 78 79def warn(message):80 print(message, file=sys.stderr)81 82 83def print_btf(filename):84 if filename == "-":85 buf = sys.stdin.buffer.read()86 else:87 with open(filename, "rb") as file:88 buf = file.read()89 90 fmt_cache = {}91 endian_pfx = ">" # big endian92 off = 093 94 def unpack(fmt):95 nonlocal off, endian_pfx96 fmt = endian_pfx + fmt97 if fmt not in fmt_cache:98 fmt_cache[fmt] = struct.Struct(fmt)99 st = fmt_cache[fmt]100 r = st.unpack_from(buf, off)101 off += st.size102 return r103 104 # Use magic number at the header start to determine endianness105 (magic,) = unpack("H")106 if magic == 0xEB9F:107 endian_pfx = ">" # big endian108 elif magic == 0x9FEB:109 endian_pfx = "<" # little endian110 else:111 warn(f"Unexpected BTF magic: {magic:02x}")112 return113 114 # Rest of the header115 version, flags, hdr_len = unpack("BBI")116 type_off, type_len, str_off, str_len = unpack("IIII")117 118 # Offsets in the header are relative to the end of a header119 type_off += hdr_len120 str_off += hdr_len121 off = hdr_len122 type_end = type_off + type_len123 124 def string(rel_off):125 try:126 start = str_off + rel_off127 end = buf.index(b"\0", start)128 if start == end:129 return "(anon)"130 return buf[start:end].decode("utf8")131 except ValueError as e:132 warn(f"Can't get string at offset {str_off} + {rel_off}: {e}")133 return f"<BAD_STRING {rel_off}>"134 135 idx = 1136 while off < type_end:137 name_off, info, size = unpack("III")138 kind = (info >> 24) & 0x1F139 vlen = info & 0xFFFF140 kflag = info >> 31141 kind_name = KINDS[kind]142 name = string(name_off)143 144 def warn_nonzero(val, name):145 nonlocal idx146 if val != 0:147 warn(f"<{idx}> {name} should be 0 but is {val}")148 149 if kind == BTF_KIND_INT:150 (info,) = unpack("I")151 encoding = (info & 0x0F000000) >> 24152 offset = (info & 0x00FF0000) >> 16153 bits = info & 0x000000FF154 enc_name = INT_ENCODING[encoding]155 print(156 f"[{idx}] {kind_name} '{name}' size={size} "157 f"bits_offset={offset} "158 f"nr_bits={bits} encoding={enc_name}"159 )160 warn_nonzero(kflag, "kflag")161 warn_nonzero(vlen, "vlen")162 163 elif kind in [164 BTF_KIND_PTR,165 BTF_KIND_CONST,166 BTF_KIND_VOLATILE,167 BTF_KIND_RESTRICT,168 ]:169 print(f"[{idx}] {kind_name} '{name}' type_id={size}")170 warn_nonzero(name_off, "name_off")171 warn_nonzero(kflag, "kflag")172 warn_nonzero(vlen, "vlen")173 174 elif kind == BTF_KIND_ARRAY:175 warn_nonzero(name_off, "name_off")176 warn_nonzero(kflag, "kflag")177 warn_nonzero(vlen, "vlen")178 warn_nonzero(size, "size")179 type, index_type, nelems = unpack("III")180 print(181 f"[{idx}] {kind_name} '{name}' type_id={type} "182 f"index_type_id={index_type} nr_elems={nelems}"183 )184 185 elif kind in [BTF_KIND_STRUCT, BTF_KIND_UNION]:186 print(f"[{idx}] {kind_name} '{name}' size={size} vlen={vlen}")187 if kflag not in [0, 1]:188 warn(f"<{idx}> kflag should 0 or 1: {kflag}")189 for _ in range(0, vlen):190 name_off, type, offset = unpack("III")191 if kflag == 0:192 print(193 f"\t'{string(name_off)}' type_id={type} "194 f"bits_offset={offset}"195 )196 else:197 bits_offset = offset & 0xFFFFFF198 bitfield_size = offset >> 24199 print(200 f"\t'{string(name_off)}' type_id={type} "201 f"bits_offset={bits_offset} "202 f"bitfield_size={bitfield_size}"203 )204 205 elif kind == BTF_KIND_ENUM:206 encoding = ENUM_ENCODING[kflag]207 print(208 f"[{idx}] {kind_name} '{name}' encoding={encoding} "209 f"size={size} vlen={vlen}"210 )211 for _ in range(0, vlen):212 (name_off,) = unpack("I")213 (val,) = unpack("i" if kflag == 1 else "I")214 print(f"\t'{string(name_off)}' val={val}")215 216 elif kind == BTF_KIND_ENUM64:217 encoding = ENUM_ENCODING[kflag]218 print(219 f"[{idx}] {kind_name} '{name}' encoding={encoding} "220 f"size={size} vlen={vlen}"221 )222 for _ in range(0, vlen):223 name_off, lo, hi = unpack("III")224 val = hi << 32 | lo225 if kflag == 1:226 val = ctypes.c_long(val).value227 print(f"\t'{string(name_off)}' val={val}LL")228 229 elif kind == BTF_KIND_FWD:230 print(f"[{idx}] {kind_name} '{name}' fwd_kind={FWD_KIND[kflag]}")231 warn_nonzero(vlen, "vlen")232 warn_nonzero(size, "size")233 234 elif kind in [BTF_KIND_TYPEDEF, BTF_KIND_TYPE_TAG]:235 print(f"[{idx}] {kind_name} '{name}' type_id={size}")236 warn_nonzero(kflag, "kflag")237 warn_nonzero(kflag, "vlen")238 239 elif kind == BTF_KIND_FUNC:240 linkage = FUNC_LINKAGE[vlen]241 print(f"[{idx}] {kind_name} '{name}' type_id={size} " f"linkage={linkage}")242 warn_nonzero(kflag, "kflag")243 244 elif kind == BTF_KIND_FUNC_PROTO:245 print(f"[{idx}] {kind_name} '{name}' ret_type_id={size} " f"vlen={vlen}")246 warn_nonzero(name_off, "name_off")247 warn_nonzero(kflag, "kflag")248 for _ in range(0, vlen):249 name_off, type = unpack("II")250 print(f"\t'{string(name_off)}' type_id={type}")251 252 elif kind == BTF_KIND_VAR:253 (linkage,) = unpack("I")254 linkage = VAR_LINKAGE[linkage]255 print(f"[{idx}] {kind_name} '{name}' type_id={size}, " f"linkage={linkage}")256 warn_nonzero(kflag, "kflag")257 warn_nonzero(vlen, "vlen")258 259 elif kind == BTF_KIND_DATASEC:260 print(f"[{idx}] {kind_name} '{name}' size={size} vlen={vlen}")261 warn_nonzero(kflag, "kflag")262 warn_nonzero(size, "size")263 for _ in range(0, vlen):264 type, offset, size = unpack("III")265 print(f"\ttype_id={type} offset={offset} size={size}")266 267 elif kind == BTF_KIND_FLOAT:268 print(f"[{idx}] {kind_name} '{name}' size={size}")269 warn_nonzero(kflag, "kflag")270 warn_nonzero(vlen, "vlen")271 272 elif kind == BTF_KIND_DECL_TAG:273 (component_idx,) = unpack("i")274 print(275 f"[{idx}] {kind_name} '{name}' type_id={size} "276 + f"component_idx={component_idx}"277 )278 warn_nonzero(kflag, "kflag")279 warn_nonzero(vlen, "vlen")280 281 else:282 warn(283 f"<{idx}> Unexpected entry: kind={kind_name} "284 f"name_off={name_off} "285 f"vlen={vlen} kflag={kflag} size={size}"286 )287 288 idx += 1289 290 291if __name__ == "__main__":292 if len(sys.argv) != 2:293 warn(f"Usage: {sys.argv[0]} <btf_file>")294 sys.exit(1)295 print_btf(sys.argv[1])296