ext_info.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. package btf
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "math"
  9. "sort"
  10. "github.com/cilium/ebpf/asm"
  11. "github.com/cilium/ebpf/internal"
  12. )
  13. // ExtInfos contains ELF section metadata.
  14. type ExtInfos struct {
  15. // The slices are sorted by offset in ascending order.
  16. funcInfos map[string][]funcInfo
  17. lineInfos map[string][]lineInfo
  18. relocationInfos map[string][]coreRelocationInfo
  19. }
  20. // loadExtInfosFromELF parses ext infos from the .BTF.ext section in an ELF.
  21. //
  22. // Returns an error wrapping ErrNotFound if no ext infos are present.
  23. func loadExtInfosFromELF(file *internal.SafeELFFile, ts types, strings *stringTable) (*ExtInfos, error) {
  24. section := file.Section(".BTF.ext")
  25. if section == nil {
  26. return nil, fmt.Errorf("btf ext infos: %w", ErrNotFound)
  27. }
  28. if section.ReaderAt == nil {
  29. return nil, fmt.Errorf("compressed ext_info is not supported")
  30. }
  31. return loadExtInfos(section.ReaderAt, file.ByteOrder, ts, strings)
  32. }
  33. // loadExtInfos parses bare ext infos.
  34. func loadExtInfos(r io.ReaderAt, bo binary.ByteOrder, ts types, strings *stringTable) (*ExtInfos, error) {
  35. // Open unbuffered section reader. binary.Read() calls io.ReadFull on
  36. // the header structs, resulting in one syscall per header.
  37. headerRd := io.NewSectionReader(r, 0, math.MaxInt64)
  38. extHeader, err := parseBTFExtHeader(headerRd, bo)
  39. if err != nil {
  40. return nil, fmt.Errorf("parsing BTF extension header: %w", err)
  41. }
  42. coreHeader, err := parseBTFExtCOREHeader(headerRd, bo, extHeader)
  43. if err != nil {
  44. return nil, fmt.Errorf("parsing BTF CO-RE header: %w", err)
  45. }
  46. buf := internal.NewBufferedSectionReader(r, extHeader.funcInfoStart(), int64(extHeader.FuncInfoLen))
  47. btfFuncInfos, err := parseFuncInfos(buf, bo, strings)
  48. if err != nil {
  49. return nil, fmt.Errorf("parsing BTF function info: %w", err)
  50. }
  51. funcInfos := make(map[string][]funcInfo, len(btfFuncInfos))
  52. for section, bfis := range btfFuncInfos {
  53. funcInfos[section], err = newFuncInfos(bfis, ts)
  54. if err != nil {
  55. return nil, fmt.Errorf("section %s: func infos: %w", section, err)
  56. }
  57. }
  58. buf = internal.NewBufferedSectionReader(r, extHeader.lineInfoStart(), int64(extHeader.LineInfoLen))
  59. btfLineInfos, err := parseLineInfos(buf, bo, strings)
  60. if err != nil {
  61. return nil, fmt.Errorf("parsing BTF line info: %w", err)
  62. }
  63. lineInfos := make(map[string][]lineInfo, len(btfLineInfos))
  64. for section, blis := range btfLineInfos {
  65. lineInfos[section], err = newLineInfos(blis, strings)
  66. if err != nil {
  67. return nil, fmt.Errorf("section %s: line infos: %w", section, err)
  68. }
  69. }
  70. if coreHeader == nil || coreHeader.COREReloLen == 0 {
  71. return &ExtInfos{funcInfos, lineInfos, nil}, nil
  72. }
  73. var btfCORERelos map[string][]bpfCORERelo
  74. buf = internal.NewBufferedSectionReader(r, extHeader.coreReloStart(coreHeader), int64(coreHeader.COREReloLen))
  75. btfCORERelos, err = parseCORERelos(buf, bo, strings)
  76. if err != nil {
  77. return nil, fmt.Errorf("parsing CO-RE relocation info: %w", err)
  78. }
  79. coreRelos := make(map[string][]coreRelocationInfo, len(btfCORERelos))
  80. for section, brs := range btfCORERelos {
  81. coreRelos[section], err = newRelocationInfos(brs, ts, strings)
  82. if err != nil {
  83. return nil, fmt.Errorf("section %s: CO-RE relocations: %w", section, err)
  84. }
  85. }
  86. return &ExtInfos{funcInfos, lineInfos, coreRelos}, nil
  87. }
  88. type funcInfoMeta struct{}
  89. type coreRelocationMeta struct{}
  90. // Assign per-section metadata from BTF to a section's instructions.
  91. func (ei *ExtInfos) Assign(insns asm.Instructions, section string) {
  92. funcInfos := ei.funcInfos[section]
  93. lineInfos := ei.lineInfos[section]
  94. reloInfos := ei.relocationInfos[section]
  95. iter := insns.Iterate()
  96. for iter.Next() {
  97. if len(funcInfos) > 0 && funcInfos[0].offset == iter.Offset {
  98. iter.Ins.Metadata.Set(funcInfoMeta{}, funcInfos[0].fn)
  99. funcInfos = funcInfos[1:]
  100. }
  101. if len(lineInfos) > 0 && lineInfos[0].offset == iter.Offset {
  102. *iter.Ins = iter.Ins.WithSource(lineInfos[0].line)
  103. lineInfos = lineInfos[1:]
  104. }
  105. if len(reloInfos) > 0 && reloInfos[0].offset == iter.Offset {
  106. iter.Ins.Metadata.Set(coreRelocationMeta{}, reloInfos[0].relo)
  107. reloInfos = reloInfos[1:]
  108. }
  109. }
  110. }
  111. // MarshalExtInfos encodes function and line info embedded in insns into kernel
  112. // wire format.
  113. func MarshalExtInfos(insns asm.Instructions, typeID func(Type) (TypeID, error)) (funcInfos, lineInfos []byte, _ error) {
  114. iter := insns.Iterate()
  115. var fiBuf, liBuf bytes.Buffer
  116. for iter.Next() {
  117. if fn := FuncMetadata(iter.Ins); fn != nil {
  118. fi := &funcInfo{
  119. fn: fn,
  120. offset: iter.Offset,
  121. }
  122. if err := fi.marshal(&fiBuf, typeID); err != nil {
  123. return nil, nil, fmt.Errorf("write func info: %w", err)
  124. }
  125. }
  126. if line, ok := iter.Ins.Source().(*Line); ok {
  127. li := &lineInfo{
  128. line: line,
  129. offset: iter.Offset,
  130. }
  131. if err := li.marshal(&liBuf); err != nil {
  132. return nil, nil, fmt.Errorf("write line info: %w", err)
  133. }
  134. }
  135. }
  136. return fiBuf.Bytes(), liBuf.Bytes(), nil
  137. }
  138. // btfExtHeader is found at the start of the .BTF.ext section.
  139. type btfExtHeader struct {
  140. Magic uint16
  141. Version uint8
  142. Flags uint8
  143. // HdrLen is larger than the size of struct btfExtHeader when it is
  144. // immediately followed by a btfExtCOREHeader.
  145. HdrLen uint32
  146. FuncInfoOff uint32
  147. FuncInfoLen uint32
  148. LineInfoOff uint32
  149. LineInfoLen uint32
  150. }
  151. // parseBTFExtHeader parses the header of the .BTF.ext section.
  152. func parseBTFExtHeader(r io.Reader, bo binary.ByteOrder) (*btfExtHeader, error) {
  153. var header btfExtHeader
  154. if err := binary.Read(r, bo, &header); err != nil {
  155. return nil, fmt.Errorf("can't read header: %v", err)
  156. }
  157. if header.Magic != btfMagic {
  158. return nil, fmt.Errorf("incorrect magic value %v", header.Magic)
  159. }
  160. if header.Version != 1 {
  161. return nil, fmt.Errorf("unexpected version %v", header.Version)
  162. }
  163. if header.Flags != 0 {
  164. return nil, fmt.Errorf("unsupported flags %v", header.Flags)
  165. }
  166. if int64(header.HdrLen) < int64(binary.Size(&header)) {
  167. return nil, fmt.Errorf("header length shorter than btfExtHeader size")
  168. }
  169. return &header, nil
  170. }
  171. // funcInfoStart returns the offset from the beginning of the .BTF.ext section
  172. // to the start of its func_info entries.
  173. func (h *btfExtHeader) funcInfoStart() int64 {
  174. return int64(h.HdrLen + h.FuncInfoOff)
  175. }
  176. // lineInfoStart returns the offset from the beginning of the .BTF.ext section
  177. // to the start of its line_info entries.
  178. func (h *btfExtHeader) lineInfoStart() int64 {
  179. return int64(h.HdrLen + h.LineInfoOff)
  180. }
  181. // coreReloStart returns the offset from the beginning of the .BTF.ext section
  182. // to the start of its CO-RE relocation entries.
  183. func (h *btfExtHeader) coreReloStart(ch *btfExtCOREHeader) int64 {
  184. return int64(h.HdrLen + ch.COREReloOff)
  185. }
  186. // btfExtCOREHeader is found right after the btfExtHeader when its HdrLen
  187. // field is larger than its size.
  188. type btfExtCOREHeader struct {
  189. COREReloOff uint32
  190. COREReloLen uint32
  191. }
  192. // parseBTFExtCOREHeader parses the tail of the .BTF.ext header. If additional
  193. // header bytes are present, extHeader.HdrLen will be larger than the struct,
  194. // indicating the presence of a CO-RE extension header.
  195. func parseBTFExtCOREHeader(r io.Reader, bo binary.ByteOrder, extHeader *btfExtHeader) (*btfExtCOREHeader, error) {
  196. extHdrSize := int64(binary.Size(&extHeader))
  197. remainder := int64(extHeader.HdrLen) - extHdrSize
  198. if remainder == 0 {
  199. return nil, nil
  200. }
  201. var coreHeader btfExtCOREHeader
  202. if err := binary.Read(r, bo, &coreHeader); err != nil {
  203. return nil, fmt.Errorf("can't read header: %v", err)
  204. }
  205. return &coreHeader, nil
  206. }
  207. type btfExtInfoSec struct {
  208. SecNameOff uint32
  209. NumInfo uint32
  210. }
  211. // parseExtInfoSec parses a btf_ext_info_sec header within .BTF.ext,
  212. // appearing within func_info and line_info sub-sections.
  213. // These headers appear once for each program section in the ELF and are
  214. // followed by one or more func/line_info records for the section.
  215. func parseExtInfoSec(r io.Reader, bo binary.ByteOrder, strings *stringTable) (string, *btfExtInfoSec, error) {
  216. var infoHeader btfExtInfoSec
  217. if err := binary.Read(r, bo, &infoHeader); err != nil {
  218. return "", nil, fmt.Errorf("read ext info header: %w", err)
  219. }
  220. secName, err := strings.Lookup(infoHeader.SecNameOff)
  221. if err != nil {
  222. return "", nil, fmt.Errorf("get section name: %w", err)
  223. }
  224. if secName == "" {
  225. return "", nil, fmt.Errorf("extinfo header refers to empty section name")
  226. }
  227. if infoHeader.NumInfo == 0 {
  228. return "", nil, fmt.Errorf("section %s has zero records", secName)
  229. }
  230. return secName, &infoHeader, nil
  231. }
  232. // parseExtInfoRecordSize parses the uint32 at the beginning of a func_infos
  233. // or line_infos segment that describes the length of all extInfoRecords in
  234. // that segment.
  235. func parseExtInfoRecordSize(r io.Reader, bo binary.ByteOrder) (uint32, error) {
  236. const maxRecordSize = 256
  237. var recordSize uint32
  238. if err := binary.Read(r, bo, &recordSize); err != nil {
  239. return 0, fmt.Errorf("can't read record size: %v", err)
  240. }
  241. if recordSize < 4 {
  242. // Need at least InsnOff worth of bytes per record.
  243. return 0, errors.New("record size too short")
  244. }
  245. if recordSize > maxRecordSize {
  246. return 0, fmt.Errorf("record size %v exceeds %v", recordSize, maxRecordSize)
  247. }
  248. return recordSize, nil
  249. }
  250. // The size of a FuncInfo in BTF wire format.
  251. var FuncInfoSize = uint32(binary.Size(bpfFuncInfo{}))
  252. type funcInfo struct {
  253. fn *Func
  254. offset asm.RawInstructionOffset
  255. }
  256. type bpfFuncInfo struct {
  257. // Instruction offset of the function within an ELF section.
  258. InsnOff uint32
  259. TypeID TypeID
  260. }
  261. func newFuncInfo(fi bpfFuncInfo, ts types) (*funcInfo, error) {
  262. typ, err := ts.ByID(fi.TypeID)
  263. if err != nil {
  264. return nil, err
  265. }
  266. fn, ok := typ.(*Func)
  267. if !ok {
  268. return nil, fmt.Errorf("type ID %d is a %T, but expected a Func", fi.TypeID, typ)
  269. }
  270. // C doesn't have anonymous functions, but check just in case.
  271. if fn.Name == "" {
  272. return nil, fmt.Errorf("func with type ID %d doesn't have a name", fi.TypeID)
  273. }
  274. return &funcInfo{
  275. fn,
  276. asm.RawInstructionOffset(fi.InsnOff),
  277. }, nil
  278. }
  279. func newFuncInfos(bfis []bpfFuncInfo, ts types) ([]funcInfo, error) {
  280. fis := make([]funcInfo, 0, len(bfis))
  281. for _, bfi := range bfis {
  282. fi, err := newFuncInfo(bfi, ts)
  283. if err != nil {
  284. return nil, fmt.Errorf("offset %d: %w", bfi.InsnOff, err)
  285. }
  286. fis = append(fis, *fi)
  287. }
  288. sort.Slice(fis, func(i, j int) bool {
  289. return fis[i].offset <= fis[j].offset
  290. })
  291. return fis, nil
  292. }
  293. // marshal into the BTF wire format.
  294. func (fi *funcInfo) marshal(w io.Writer, typeID func(Type) (TypeID, error)) error {
  295. id, err := typeID(fi.fn)
  296. if err != nil {
  297. return err
  298. }
  299. bfi := bpfFuncInfo{
  300. InsnOff: uint32(fi.offset),
  301. TypeID: id,
  302. }
  303. return binary.Write(w, internal.NativeEndian, &bfi)
  304. }
  305. // parseLineInfos parses a func_info sub-section within .BTF.ext ito a map of
  306. // func infos indexed by section name.
  307. func parseFuncInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfFuncInfo, error) {
  308. recordSize, err := parseExtInfoRecordSize(r, bo)
  309. if err != nil {
  310. return nil, err
  311. }
  312. result := make(map[string][]bpfFuncInfo)
  313. for {
  314. secName, infoHeader, err := parseExtInfoSec(r, bo, strings)
  315. if errors.Is(err, io.EOF) {
  316. return result, nil
  317. }
  318. if err != nil {
  319. return nil, err
  320. }
  321. records, err := parseFuncInfoRecords(r, bo, recordSize, infoHeader.NumInfo)
  322. if err != nil {
  323. return nil, fmt.Errorf("section %v: %w", secName, err)
  324. }
  325. result[secName] = records
  326. }
  327. }
  328. // parseFuncInfoRecords parses a stream of func_infos into a funcInfos.
  329. // These records appear after a btf_ext_info_sec header in the func_info
  330. // sub-section of .BTF.ext.
  331. func parseFuncInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32) ([]bpfFuncInfo, error) {
  332. var out []bpfFuncInfo
  333. var fi bpfFuncInfo
  334. if exp, got := FuncInfoSize, recordSize; exp != got {
  335. // BTF blob's record size is longer than we know how to parse.
  336. return nil, fmt.Errorf("expected FuncInfo record size %d, but BTF blob contains %d", exp, got)
  337. }
  338. for i := uint32(0); i < recordNum; i++ {
  339. if err := binary.Read(r, bo, &fi); err != nil {
  340. return nil, fmt.Errorf("can't read function info: %v", err)
  341. }
  342. if fi.InsnOff%asm.InstructionSize != 0 {
  343. return nil, fmt.Errorf("offset %v is not aligned with instruction size", fi.InsnOff)
  344. }
  345. // ELF tracks offset in bytes, the kernel expects raw BPF instructions.
  346. // Convert as early as possible.
  347. fi.InsnOff /= asm.InstructionSize
  348. out = append(out, fi)
  349. }
  350. return out, nil
  351. }
  352. var LineInfoSize = uint32(binary.Size(bpfLineInfo{}))
  353. // Line represents the location and contents of a single line of source
  354. // code a BPF ELF was compiled from.
  355. type Line struct {
  356. fileName string
  357. line string
  358. lineNumber uint32
  359. lineColumn uint32
  360. // TODO: We should get rid of the fields below, but for that we need to be
  361. // able to write BTF.
  362. fileNameOff uint32
  363. lineOff uint32
  364. }
  365. func (li *Line) FileName() string {
  366. return li.fileName
  367. }
  368. func (li *Line) Line() string {
  369. return li.line
  370. }
  371. func (li *Line) LineNumber() uint32 {
  372. return li.lineNumber
  373. }
  374. func (li *Line) LineColumn() uint32 {
  375. return li.lineColumn
  376. }
  377. func (li *Line) String() string {
  378. return li.line
  379. }
  380. type lineInfo struct {
  381. line *Line
  382. offset asm.RawInstructionOffset
  383. }
  384. // Constants for the format of bpfLineInfo.LineCol.
  385. const (
  386. bpfLineShift = 10
  387. bpfLineMax = (1 << (32 - bpfLineShift)) - 1
  388. bpfColumnMax = (1 << bpfLineShift) - 1
  389. )
  390. type bpfLineInfo struct {
  391. // Instruction offset of the line within the whole instruction stream, in instructions.
  392. InsnOff uint32
  393. FileNameOff uint32
  394. LineOff uint32
  395. LineCol uint32
  396. }
  397. func newLineInfo(li bpfLineInfo, strings *stringTable) (*lineInfo, error) {
  398. line, err := strings.Lookup(li.LineOff)
  399. if err != nil {
  400. return nil, fmt.Errorf("lookup of line: %w", err)
  401. }
  402. fileName, err := strings.Lookup(li.FileNameOff)
  403. if err != nil {
  404. return nil, fmt.Errorf("lookup of filename: %w", err)
  405. }
  406. lineNumber := li.LineCol >> bpfLineShift
  407. lineColumn := li.LineCol & bpfColumnMax
  408. return &lineInfo{
  409. &Line{
  410. fileName,
  411. line,
  412. lineNumber,
  413. lineColumn,
  414. li.FileNameOff,
  415. li.LineOff,
  416. },
  417. asm.RawInstructionOffset(li.InsnOff),
  418. }, nil
  419. }
  420. func newLineInfos(blis []bpfLineInfo, strings *stringTable) ([]lineInfo, error) {
  421. lis := make([]lineInfo, 0, len(blis))
  422. for _, bli := range blis {
  423. li, err := newLineInfo(bli, strings)
  424. if err != nil {
  425. return nil, fmt.Errorf("offset %d: %w", bli.InsnOff, err)
  426. }
  427. lis = append(lis, *li)
  428. }
  429. sort.Slice(lis, func(i, j int) bool {
  430. return lis[i].offset <= lis[j].offset
  431. })
  432. return lis, nil
  433. }
  434. // marshal writes the binary representation of the LineInfo to w.
  435. func (li *lineInfo) marshal(w io.Writer) error {
  436. line := li.line
  437. if line.lineNumber > bpfLineMax {
  438. return fmt.Errorf("line %d exceeds %d", line.lineNumber, bpfLineMax)
  439. }
  440. if line.lineColumn > bpfColumnMax {
  441. return fmt.Errorf("column %d exceeds %d", line.lineColumn, bpfColumnMax)
  442. }
  443. bli := bpfLineInfo{
  444. uint32(li.offset),
  445. line.fileNameOff,
  446. line.lineOff,
  447. (line.lineNumber << bpfLineShift) | line.lineColumn,
  448. }
  449. return binary.Write(w, internal.NativeEndian, &bli)
  450. }
  451. // parseLineInfos parses a line_info sub-section within .BTF.ext ito a map of
  452. // line infos indexed by section name.
  453. func parseLineInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfLineInfo, error) {
  454. recordSize, err := parseExtInfoRecordSize(r, bo)
  455. if err != nil {
  456. return nil, err
  457. }
  458. result := make(map[string][]bpfLineInfo)
  459. for {
  460. secName, infoHeader, err := parseExtInfoSec(r, bo, strings)
  461. if errors.Is(err, io.EOF) {
  462. return result, nil
  463. }
  464. if err != nil {
  465. return nil, err
  466. }
  467. records, err := parseLineInfoRecords(r, bo, recordSize, infoHeader.NumInfo)
  468. if err != nil {
  469. return nil, fmt.Errorf("section %v: %w", secName, err)
  470. }
  471. result[secName] = records
  472. }
  473. }
  474. // parseLineInfoRecords parses a stream of line_infos into a lineInfos.
  475. // These records appear after a btf_ext_info_sec header in the line_info
  476. // sub-section of .BTF.ext.
  477. func parseLineInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32) ([]bpfLineInfo, error) {
  478. var out []bpfLineInfo
  479. var li bpfLineInfo
  480. if exp, got := uint32(binary.Size(li)), recordSize; exp != got {
  481. // BTF blob's record size is longer than we know how to parse.
  482. return nil, fmt.Errorf("expected LineInfo record size %d, but BTF blob contains %d", exp, got)
  483. }
  484. for i := uint32(0); i < recordNum; i++ {
  485. if err := binary.Read(r, bo, &li); err != nil {
  486. return nil, fmt.Errorf("can't read line info: %v", err)
  487. }
  488. if li.InsnOff%asm.InstructionSize != 0 {
  489. return nil, fmt.Errorf("offset %v is not aligned with instruction size", li.InsnOff)
  490. }
  491. // ELF tracks offset in bytes, the kernel expects raw BPF instructions.
  492. // Convert as early as possible.
  493. li.InsnOff /= asm.InstructionSize
  494. out = append(out, li)
  495. }
  496. return out, nil
  497. }
  498. // bpfCORERelo matches the kernel's struct bpf_core_relo.
  499. type bpfCORERelo struct {
  500. InsnOff uint32
  501. TypeID TypeID
  502. AccessStrOff uint32
  503. Kind coreKind
  504. }
  505. type CORERelocation struct {
  506. typ Type
  507. accessor coreAccessor
  508. kind coreKind
  509. }
  510. func CORERelocationMetadata(ins *asm.Instruction) *CORERelocation {
  511. relo, _ := ins.Metadata.Get(coreRelocationMeta{}).(*CORERelocation)
  512. return relo
  513. }
  514. type coreRelocationInfo struct {
  515. relo *CORERelocation
  516. offset asm.RawInstructionOffset
  517. }
  518. func newRelocationInfo(relo bpfCORERelo, ts types, strings *stringTable) (*coreRelocationInfo, error) {
  519. typ, err := ts.ByID(relo.TypeID)
  520. if err != nil {
  521. return nil, err
  522. }
  523. accessorStr, err := strings.Lookup(relo.AccessStrOff)
  524. if err != nil {
  525. return nil, err
  526. }
  527. accessor, err := parseCOREAccessor(accessorStr)
  528. if err != nil {
  529. return nil, fmt.Errorf("accessor %q: %s", accessorStr, err)
  530. }
  531. return &coreRelocationInfo{
  532. &CORERelocation{
  533. typ,
  534. accessor,
  535. relo.Kind,
  536. },
  537. asm.RawInstructionOffset(relo.InsnOff),
  538. }, nil
  539. }
  540. func newRelocationInfos(brs []bpfCORERelo, ts types, strings *stringTable) ([]coreRelocationInfo, error) {
  541. rs := make([]coreRelocationInfo, 0, len(brs))
  542. for _, br := range brs {
  543. relo, err := newRelocationInfo(br, ts, strings)
  544. if err != nil {
  545. return nil, fmt.Errorf("offset %d: %w", br.InsnOff, err)
  546. }
  547. rs = append(rs, *relo)
  548. }
  549. sort.Slice(rs, func(i, j int) bool {
  550. return rs[i].offset < rs[j].offset
  551. })
  552. return rs, nil
  553. }
  554. var extInfoReloSize = binary.Size(bpfCORERelo{})
  555. // parseCORERelos parses a core_relos sub-section within .BTF.ext ito a map of
  556. // CO-RE relocations indexed by section name.
  557. func parseCORERelos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map[string][]bpfCORERelo, error) {
  558. recordSize, err := parseExtInfoRecordSize(r, bo)
  559. if err != nil {
  560. return nil, err
  561. }
  562. if recordSize != uint32(extInfoReloSize) {
  563. return nil, fmt.Errorf("expected record size %d, got %d", extInfoReloSize, recordSize)
  564. }
  565. result := make(map[string][]bpfCORERelo)
  566. for {
  567. secName, infoHeader, err := parseExtInfoSec(r, bo, strings)
  568. if errors.Is(err, io.EOF) {
  569. return result, nil
  570. }
  571. if err != nil {
  572. return nil, err
  573. }
  574. records, err := parseCOREReloRecords(r, bo, recordSize, infoHeader.NumInfo)
  575. if err != nil {
  576. return nil, fmt.Errorf("section %v: %w", secName, err)
  577. }
  578. result[secName] = records
  579. }
  580. }
  581. // parseCOREReloRecords parses a stream of CO-RE relocation entries into a
  582. // coreRelos. These records appear after a btf_ext_info_sec header in the
  583. // core_relos sub-section of .BTF.ext.
  584. func parseCOREReloRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32) ([]bpfCORERelo, error) {
  585. var out []bpfCORERelo
  586. var relo bpfCORERelo
  587. for i := uint32(0); i < recordNum; i++ {
  588. if err := binary.Read(r, bo, &relo); err != nil {
  589. return nil, fmt.Errorf("can't read CO-RE relocation: %v", err)
  590. }
  591. if relo.InsnOff%asm.InstructionSize != 0 {
  592. return nil, fmt.Errorf("offset %v is not aligned with instruction size", relo.InsnOff)
  593. }
  594. // ELF tracks offset in bytes, the kernel expects raw BPF instructions.
  595. // Convert as early as possible.
  596. relo.InsnOff /= asm.InstructionSize
  597. out = append(out, relo)
  598. }
  599. return out, nil
  600. }