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