github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/elf.go (about)

     1  package gobpfld
     2  
     3  import (
     4  	"debug/elf"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/dylandreimerink/gobpfld/bpftypes"
    13  	"github.com/dylandreimerink/gobpfld/ebpf"
    14  	"github.com/dylandreimerink/gobpfld/internal/cstr"
    15  	"github.com/dylandreimerink/gobpfld/perf"
    16  )
    17  
    18  type ELFParseSettings struct {
    19  	// If true, names which are to large will be truncated, this can cause unexpected behavior
    20  	// Otherwise an error will be generated.
    21  	TruncateNames bool
    22  }
    23  
    24  // BPFELF is the result of parsing an eBPF ELF file. It can contain multiple programs and maps.
    25  type BPFELF struct {
    26  	ByteOrder binary.ByteOrder
    27  	// Programs contained within the ELF
    28  	Programs map[string]BPFProgram
    29  	// Maps defined in the ELF
    30  	Maps map[string]BPFMap
    31  	// BTF contains type and debugging information regarding the programs and maps.
    32  	BTF *BTF
    33  }
    34  
    35  // bpfELF is a temporary structure which we used to store data across multiple stages of parsing the ELF file.
    36  type bpfELF struct {
    37  	ByteOrder binary.ByteOrder
    38  	// ElfPrograms contained within the ELF
    39  	ElfPrograms map[string]*elfBPFProgram
    40  	Programs    map[string]BPFProgram
    41  	// AbstractMaps defined in the ELF
    42  	AbstractMaps map[string]AbstractMap
    43  	Maps         map[string]BPFMap
    44  	// BTF contains type and debugging information regarding the programs and maps.
    45  	BTF *BTF
    46  
    47  	// eBPF code found in the .text section, often called "sub programs".
    48  	// Used for library code and code shared by multiple programs by way of BPF to BPF calls
    49  	txtInstr []ebpf.RawInstruction
    50  	// A list of relocation tables by section name
    51  	relTables map[string]elfRelocTable
    52  
    53  	// Store the data of the .BTF.ext section, since we need to parse it after
    54  	// the .BTF section.
    55  	btfExtBytes []byte
    56  
    57  	settings ELFParseSettings
    58  	elfFile  *elf.File
    59  	license  string
    60  }
    61  
    62  type elfBPFProgram struct {
    63  	AbstractBPFProgram
    64  
    65  	// The ELF section where the program came from
    66  	section string
    67  	// The offset from the start of the section to the start of the program in bytes.
    68  	offset int
    69  	// The size of the program code in bytes, which is not always the size of the instructions slice
    70  	// due to bpf_to_bpf linking
    71  	size int
    72  }
    73  
    74  func LoadProgramFromELF(r io.ReaderAt, settings ELFParseSettings) (BPFELF, error) {
    75  	elfFile, err := elf.NewFile(r)
    76  	if err != nil {
    77  		return BPFELF{}, err
    78  	}
    79  
    80  	if elfFile.Machine != elf.EM_BPF {
    81  		return BPFELF{}, fmt.Errorf("elf file machine type is not BPF, machine type: '%s'", elfFile.Machine)
    82  	}
    83  
    84  	if elfFile.Class != elf.ELFCLASS64 {
    85  		return BPFELF{}, fmt.Errorf("elf file class is not 64 bit, class: '%s'", elfFile.Class)
    86  	}
    87  
    88  	bpfElf := newBPFELF(elfFile, settings)
    89  
    90  	err = bpfElf.parseElf()
    91  	if err != nil {
    92  		return BPFELF{}, fmt.Errorf("parse ELF: %w", err)
    93  	}
    94  
    95  	err = bpfElf.processBTF()
    96  	if err != nil {
    97  		return BPFELF{}, fmt.Errorf("process BTF: %w", err)
    98  	}
    99  
   100  	err = bpfElf.linkAndRelocate()
   101  	if err != nil {
   102  		return BPFELF{}, fmt.Errorf("link and relocate: %w", err)
   103  	}
   104  
   105  	err = bpfElf.specializePrograms()
   106  	if err != nil {
   107  		return BPFELF{}, fmt.Errorf("link and relocate: %w", err)
   108  	}
   109  
   110  	return bpfElf.toBPFELF(), nil
   111  }
   112  
   113  func newBPFELF(elfFile *elf.File, settings ELFParseSettings) *bpfELF {
   114  	return &bpfELF{
   115  		ByteOrder:    elfFile.ByteOrder,
   116  		ElfPrograms:  make(map[string]*elfBPFProgram),
   117  		Programs:     make(map[string]BPFProgram),
   118  		AbstractMaps: make(map[string]AbstractMap),
   119  		Maps:         make(map[string]BPFMap),
   120  		relTables:    make(map[string]elfRelocTable),
   121  
   122  		settings: settings,
   123  		elfFile:  elfFile,
   124  		license:  "Unknown",
   125  	}
   126  }
   127  
   128  // Parse the ELF file into the separate components which will need to be combined later
   129  func (bpfElf *bpfELF) parseElf() error {
   130  	for sectionIndex, section := range bpfElf.elfFile.Sections {
   131  		// TODO instead of making an exception for .bss, handle all datasections differently
   132  		if section.Type == elf.SHT_PROGBITS || section.Name == ".bss" {
   133  			err := bpfElf.parseProgBits(sectionIndex, section)
   134  			if err != nil {
   135  				return fmt.Errorf("parse prog bits: %w", err)
   136  			}
   137  
   138  			continue
   139  		}
   140  
   141  		if section.Type == elf.SHT_REL {
   142  			err := bpfElf.parseRelocationTables(section)
   143  			if err != nil {
   144  				return fmt.Errorf("parse relocation tables: %w", err)
   145  			}
   146  
   147  			continue
   148  		}
   149  	}
   150  
   151  	return nil
   152  }
   153  
   154  // processBTF parses extended BTF information and patches DataSec entries
   155  func (bpfElf *bpfELF) processBTF() error {
   156  	// If set, parse the .BTF.ext section now, since we have to guarantee it happens after
   157  	// BTF parsing since the Ext uses the string table and types from the main section.
   158  	if bpfElf.btfExtBytes != nil {
   159  		if bpfElf.BTF == nil {
   160  			bpfElf.BTF = NewBTF()
   161  		}
   162  
   163  		err := bpfElf.BTF.ParseBTFExt(bpfElf.btfExtBytes)
   164  		if err != nil {
   165  			return fmt.Errorf("parse .BTF.ext: %w", err)
   166  		}
   167  	}
   168  
   169  	symbols, err := bpfElf.elfFile.Symbols()
   170  	if err != nil {
   171  		return fmt.Errorf("get symbols: %w", err)
   172  	}
   173  
   174  	// Patch total Size of DataSec types, and offsets for the individual variables
   175  	for _, btfType := range bpfElf.BTF.Types {
   176  		dataSec, ok := btfType.(*BTFDataSecType)
   177  		if !ok {
   178  			continue
   179  		}
   180  
   181  		section := bpfElf.elfFile.Section(dataSec.Name)
   182  		if section != nil {
   183  			dataSec.Size = uint32(section.Size)
   184  
   185  			for i, variable := range dataSec.Variables {
   186  				for _, sym := range symbols {
   187  					// Ignore any symbols which are not for the current section
   188  					if len(bpfElf.elfFile.Sections) <= int(sym.Section) ||
   189  						bpfElf.elfFile.Sections[sym.Section] != section {
   190  						continue
   191  					}
   192  
   193  					// The symbols name must match the name of the DataSec variable type
   194  					if sym.Name != variable.Type.GetName() {
   195  						continue
   196  					}
   197  
   198  					// The value of the symbol is the offset from the start of the section
   199  					dataSec.Variables[i].Offset = uint32(sym.Value)
   200  
   201  					break
   202  				}
   203  			}
   204  		}
   205  	}
   206  
   207  	// If we have a BTF object, add it to all programs
   208  	if bpfElf.BTF != nil {
   209  		for _, program := range bpfElf.ElfPrograms {
   210  			program.BTF = bpfElf.BTF
   211  		}
   212  	}
   213  
   214  	return nil
   215  }
   216  
   217  // parseRelocationTables converts the binary relocation tables into Go types
   218  func (bpfElf *bpfELF) parseRelocationTables(section *elf.Section) error {
   219  	data, err := section.Data()
   220  	if err != nil {
   221  		return fmt.Errorf("error while loading section '%s': %w", section.Name, err)
   222  	}
   223  
   224  	if len(data)%16 != 0 {
   225  		return fmt.Errorf("size of relocation table '%s' not devisable by 16", section.Name)
   226  	}
   227  
   228  	symbols, err := bpfElf.elfFile.Symbols()
   229  	if err != nil {
   230  		return fmt.Errorf("get symbols: %w", err)
   231  	}
   232  
   233  	relTable := make(elfRelocTable, len(data)/16)
   234  	for i := 0; i < len(data); i += 16 {
   235  		entry := elfRelocEntry{
   236  			Rel64: elf.Rel64{
   237  				Off:  bpfElf.elfFile.ByteOrder.Uint64(data[i : i+8]),
   238  				Info: bpfElf.elfFile.ByteOrder.Uint64(data[i+8 : i+16]),
   239  			},
   240  		}
   241  		symNum := elf.R_SYM64(entry.Info)
   242  		if uint32(len(symbols)) < symNum {
   243  			return fmt.Errorf(
   244  				"symbol number in relocation table '%s' does not exist in symbol table",
   245  				section.Name,
   246  			)
   247  		}
   248  
   249  		entry.Symbol = &symbols[symNum-1]
   250  		entry.Type = elf_r_bpf(elf.R_TYPE64(entry.Info))
   251  
   252  		relTable[i/16] = entry
   253  	}
   254  
   255  	bpfElf.relTables[section.Name] = relTable
   256  
   257  	return nil
   258  }
   259  
   260  // parseProgBits parses ELF sections of type progbits, which can contain a number of different types of data
   261  // depending on the name of the section.
   262  func (bpfElf *bpfELF) parseProgBits(sectionIndex int, section *elf.Section) error {
   263  	data, err := section.Data()
   264  	if err != nil {
   265  		return fmt.Errorf("error while loading section '%s': %w", section.Name, err)
   266  	}
   267  
   268  	switch section.Name {
   269  	case "license":
   270  		bpfElf.license = cstr.BytesToString(data)
   271  
   272  	case "maps", ".maps":
   273  		err := bpfElf.parseMaps(sectionIndex, section)
   274  		if err != nil {
   275  			return fmt.Errorf("parse maps: %w", err)
   276  		}
   277  
   278  	case ".data", ".rodata", ".bss":
   279  		err := bpfElf.dataToMap(sectionIndex, section)
   280  		if err != nil {
   281  			return fmt.Errorf("data to map: %w", err)
   282  		}
   283  
   284  	case ".BTF":
   285  		// BTF type and string information
   286  
   287  		if bpfElf.BTF == nil {
   288  			bpfElf.BTF = NewBTF()
   289  		}
   290  
   291  		err := bpfElf.BTF.ParseBTF(data)
   292  		if err != nil {
   293  			return fmt.Errorf("parse .BTF: %w", err)
   294  		}
   295  
   296  	case ".BTF.ext":
   297  		// BTF line and function information
   298  
   299  		// Just save the data, we have to process it later to guarantee it happens after .BTF parsing
   300  		bpfElf.btfExtBytes = data
   301  
   302  	// TODO parse .BTF_ids (Used to identify specific types in the kernel for tracing etc.)
   303  	default:
   304  		// Assume program
   305  		err := bpfElf.parseProgram(sectionIndex, section)
   306  		if err != nil {
   307  			return fmt.Errorf("parse program: %w", err)
   308  		}
   309  	}
   310  
   311  	return nil
   312  }
   313  
   314  // parseProgram parses an ELF section as an BPF program
   315  func (bpfElf *bpfELF) parseProgram(sectionIndex int, section *elf.Section) error {
   316  	// If the section flags don't indicate it contains instructions, it not a program
   317  	if section.Flags&elf.SHF_EXECINSTR == 0 {
   318  		return nil
   319  	}
   320  
   321  	data, err := section.Data()
   322  	if err != nil {
   323  		return fmt.Errorf("error while loading section '%s': %w", section.Name, err)
   324  	}
   325  
   326  	if len(data)%ebpf.BPFInstSize != 0 {
   327  		return fmt.Errorf("elf section is incorrect size for BPF program, should be divisible by 8")
   328  	}
   329  
   330  	instructions := make([]ebpf.RawInstruction, len(data)/ebpf.BPFInstSize)
   331  	for i := 0; i < len(data); i += ebpf.BPFInstSize {
   332  		instructions[i/ebpf.BPFInstSize] = ebpf.RawInstruction{
   333  			Op:  data[i],
   334  			Reg: data[i+1],
   335  			Off: int16(bpfElf.elfFile.ByteOrder.Uint16(data[i+2 : i+4])),
   336  			Imm: int32(bpfElf.elfFile.ByteOrder.Uint32(data[i+4 : i+8])),
   337  		}
   338  	}
   339  
   340  	// If this is the .text section, save the instructions in a separate slice without a program struct
   341  	if section.Name == ".text" {
   342  		bpfElf.txtInstr = instructions
   343  		return nil
   344  	}
   345  
   346  	// For other sections, create it as a separate program
   347  
   348  	sectionParts := strings.Split(section.Name, "/")
   349  	progType := sectionNameToProgType[sectionParts[0]]
   350  
   351  	if progType == bpftypes.BPF_PROG_TYPE_UNSPEC {
   352  		return fmt.Errorf(
   353  			"unknown elf section '%s', doesn't match any eBPF program type",
   354  			sectionParts[0],
   355  		)
   356  	}
   357  
   358  	symbols, err := bpfElf.elfFile.Symbols()
   359  	if err != nil {
   360  		return fmt.Errorf("get symbols: %w", err)
   361  	}
   362  
   363  	// Loop over all symbols for this section
   364  	for _, sym := range symbols {
   365  		if sym.Section != elf.SectionIndex(sectionIndex) {
   366  			continue
   367  		}
   368  
   369  		// BPF programs appear as global functions in the ELF file
   370  		if elf.ST_BIND(sym.Info) != elf.STB_GLOBAL || elf.ST_TYPE(sym.Info) != elf.STT_FUNC {
   371  			continue
   372  		}
   373  
   374  		program := NewAbstractBPFProgram()
   375  		program.ProgramType = progType
   376  		start := int(sym.Value) / ebpf.BPFInstSize
   377  		end := (int(sym.Value) + int(sym.Size)) / ebpf.BPFInstSize
   378  		program.Instructions = instructions[start:end]
   379  
   380  		err = program.Name.SetString(sym.Name)
   381  		if err != nil {
   382  			if bpfElf.settings.TruncateNames && errors.Is(err, ErrObjNameToLarge) {
   383  				err = program.Name.SetString(sym.Name[:bpftypes.BPF_OBJ_NAME_LEN-1])
   384  				if err != nil {
   385  					return fmt.Errorf("failed to truncate program name: %w", err)
   386  				}
   387  			} else {
   388  				return fmt.Errorf("failed to set program name '%s': %w", sym.Name, err)
   389  			}
   390  		}
   391  
   392  		bpfElf.ElfPrograms[sym.Name] = &elfBPFProgram{
   393  			AbstractBPFProgram: program,
   394  			section:            section.Name,
   395  			offset:             int(sym.Value),
   396  			size:               int(sym.Size),
   397  		}
   398  	}
   399  
   400  	return nil
   401  }
   402  
   403  // The .data, .rodata, and .rss sections are loaded as maps with a single value containing the data blob
   404  // of the ELF section.
   405  func (bpfElf *bpfELF) dataToMap(sectionIndex int, section *elf.Section) error {
   406  	secData, err := section.Data()
   407  	if err != nil {
   408  		return fmt.Errorf("get section data: %w", err)
   409  	}
   410  
   411  	datasecMap := &dataMap{
   412  		AbstractMap: AbstractMap{
   413  			Definition: BPFMapDef{
   414  				Type:       bpftypes.BPF_MAP_TYPE_ARRAY,
   415  				KeySize:    4,
   416  				ValueSize:  uint32(len(secData)),
   417  				MaxEntries: 1,
   418  			},
   419  		},
   420  	}
   421  
   422  	datasecMap.InitialData = map[interface{}]interface{}{
   423  		0: secData,
   424  	}
   425  
   426  	switch section.Name {
   427  	case ".rodata":
   428  		datasecMap.readOnly = true
   429  		datasecMap.Name = MustNewObjName("rodata")
   430  	case ".bss":
   431  		datasecMap.Name = MustNewObjName("bss")
   432  		// .bss is zero initialzed, the kernel will zero-init the map, so if we just don't write
   433  		// to it, we are good.
   434  		datasecMap.InitialData = nil
   435  	case ".data":
   436  		datasecMap.Name = MustNewObjName("data")
   437  	}
   438  
   439  	bpfElf.Maps[datasecMap.Name.String()] = datasecMap
   440  
   441  	return nil
   442  }
   443  
   444  // parseMaps parses an ELF section as containing map data
   445  func (bpfElf *bpfELF) parseMaps(sectionIndex int, section *elf.Section) error {
   446  	data, err := section.Data()
   447  	if err != nil {
   448  		return fmt.Errorf("error while loading section '%s': %w", section.Name, err)
   449  	}
   450  
   451  	// If section flag does not have alloc, its not a proper map def
   452  	if section.Flags&elf.SHF_ALLOC == 0 {
   453  		return errors.New("maps section has no ALLOC flag")
   454  	}
   455  
   456  	symbols, err := bpfElf.elfFile.Symbols()
   457  	if err != nil {
   458  		return fmt.Errorf("get symbols: %w", err)
   459  	}
   460  
   461  	for i := 0; i < len(data); i += bpfMapDefSize {
   462  		abstractMap := AbstractMap{
   463  			Definition: BPFMapDef{
   464  				Type:       bpftypes.BPFMapType(bpfElf.elfFile.ByteOrder.Uint32(data[i : i+4])),
   465  				KeySize:    bpfElf.elfFile.ByteOrder.Uint32(data[i+4 : i+8]),
   466  				ValueSize:  bpfElf.elfFile.ByteOrder.Uint32(data[i+8 : i+12]),
   467  				MaxEntries: bpfElf.elfFile.ByteOrder.Uint32(data[i+12 : i+16]),
   468  				Flags:      bpftypes.BPFMapFlags(bpfElf.elfFile.ByteOrder.Uint32(data[i+16 : i+20])),
   469  			},
   470  		}
   471  
   472  		longName := ""
   473  		for _, symbol := range symbols {
   474  			// If the symbol isn't for this section
   475  			if int(symbol.Section) != sectionIndex {
   476  				continue
   477  			}
   478  
   479  			// if the symbol is not for the current data offset
   480  			if symbol.Value != uint64(i) {
   481  				continue
   482  			}
   483  
   484  			longName = symbol.Name
   485  			err = abstractMap.Name.SetString(symbol.Name)
   486  			if err != nil {
   487  				if bpfElf.settings.TruncateNames && errors.Is(err, ErrObjNameToLarge) {
   488  					err = abstractMap.Name.SetString(symbol.Name[:bpftypes.BPF_OBJ_NAME_LEN-1])
   489  					if err != nil {
   490  						return fmt.Errorf("failed to truncate map name: %w", err)
   491  					}
   492  				} else {
   493  					return fmt.Errorf("failed to set map name: %w", err)
   494  				}
   495  			}
   496  
   497  			break
   498  		}
   499  
   500  		if abstractMap.Name.String() == "" {
   501  			return fmt.Errorf(
   502  				"unable to find name in symbol table for map at index %d in section '%s'",
   503  				i,
   504  				section.Name,
   505  			)
   506  		}
   507  
   508  		// TODO map name duplicate check
   509  
   510  		bpfElf.AbstractMaps[longName] = abstractMap
   511  	}
   512  
   513  	return nil
   514  }
   515  
   516  // linkAndRelocate links data parsed from different ELF sections together and relocates any addresses/pointers
   517  // that have changed as result of the linking.
   518  func (bpfElf *bpfELF) linkAndRelocate() error {
   519  	// Set BTF k/v type for dataMaps
   520  	for name, bpfMap := range bpfElf.Maps {
   521  		dataMap, ok := bpfMap.(*dataMap)
   522  		if !ok {
   523  			continue
   524  		}
   525  
   526  		dataMap.BTF = bpfElf.BTF
   527  		if bpfElf.BTF != nil {
   528  			dataSec := bpfElf.BTF.typesByName["."+name]
   529  			dataMap.BTFMapType = BTFMap{
   530  				Key:   &BTFVoidType{},
   531  				Value: dataSec,
   532  			}
   533  		}
   534  		bpfElf.Maps[name] = dataMap
   535  	}
   536  
   537  	// Add BTF info to the abstract maps and resolve the actual map type, to be used during map loading.
   538  	for name, bpfMap := range bpfElf.AbstractMaps {
   539  		bpfMap.BTF = bpfElf.BTF
   540  		if bpfElf.BTF != nil {
   541  			func() {
   542  				// TODO resolve more information about the map from BTF
   543  				// bpfMap.BTFMapType = bpfElf.BTF.typesByName[name]
   544  				// }
   545  
   546  				// The "new" way of doing it is to allow users to specify a 'key' and 'value' field instread of the
   547  				// sizes.These fields should be pointers to the types used in the key and or value types.
   548  				// We can use the BTF types to infer the size of these types for the map_def.
   549  				// https://lwn.net/ml/netdev/20190531202132.379386-7-andriin@fb.com/
   550  				// We can't use this method yet until the map parsing in an earlier phase can postpone map creation
   551  				// until now. So use the "old school" method instread :( .
   552  
   553  				// The "old school" way of associating BTF Key/Value types to a map is with an annotation which no
   554  				// longer exists(BPF_ANNOTATE_KV_PAIR). This would produce an additional struct ____btf_map_##name
   555  				// which refers to the key and value types.
   556  				// https://github.com/libbpf/libbpf/blob/3febb8a16597f4605450f6c947f4deefb446cb8a/src/btf.c#L1401
   557  
   558  				container, found := bpfElf.BTF.typesByName["____btf_map_"+name]
   559  				if !found {
   560  					return
   561  				}
   562  
   563  				annotationVar, ok := container.(*BTFVarType)
   564  				if !ok {
   565  					return
   566  				}
   567  
   568  				annotationType, ok := annotationVar.Type.(*BTFStructType)
   569  				if !ok {
   570  					return
   571  				}
   572  
   573  				for _, m := range annotationType.Members {
   574  					if m.Name == "key" {
   575  						bpfMap.BTFMapType.Key = m.Type
   576  					}
   577  					if m.Name == "value" {
   578  						bpfMap.BTFMapType.Value = m.Type
   579  					}
   580  				}
   581  			}()
   582  		}
   583  
   584  		bpfElf.Maps[name] = bpfMapFromAbstractMap(bpfMap)
   585  	}
   586  
   587  	// Index lines and funcs by section since we will be relocating per section
   588  	btfLinesPerSection := make(map[string][]BTFLine)
   589  	btfFuncsPerSection := make(map[string][]BTFFunc)
   590  	if bpfElf.BTF != nil {
   591  		for _, line := range bpfElf.BTF.Lines {
   592  			lines := btfLinesPerSection[line.Section]
   593  			lines = append(lines, line)
   594  			btfLinesPerSection[line.Section] = lines
   595  		}
   596  		for _, f := range bpfElf.BTF.Funcs {
   597  			funcs := btfFuncsPerSection[f.Section]
   598  			funcs = append(funcs, f)
   599  			btfFuncsPerSection[f.Section] = funcs
   600  		}
   601  	}
   602  
   603  	for _, program := range bpfElf.ElfPrograms {
   604  		progRelocTable, found := bpfElf.relTables[".rel"+program.section]
   605  		if !found {
   606  			continue
   607  		}
   608  
   609  		// The offset where main program instructions ends and .text instructions start.
   610  		txtInsOff := -1
   611  
   612  		// If there is any code in the .text section
   613  		if len(bpfElf.txtInstr) > 0 {
   614  			// Check if this program uses any code from the .text section.
   615  			// If there are multiple programs, not all may need the .text code.
   616  			// Adding it causes the verifier to throw dead code errors.
   617  			//
   618  			// TODO look into only adding the sub programs needed, not the whole .text block (if doable)
   619  			//  if there is dead-code the verifier will refuse to load, so this might be a good feature.
   620  			usesTxt := false
   621  			for _, relocEntry := range progRelocTable {
   622  				// The absolute offset from the start of the section to the location where the entry should be linked
   623  				absOff, err := relocEntry.AbsoluteOffset()
   624  				if err != nil {
   625  					return fmt.Errorf("unable to calculate absolute offset for relocation entry: %w", err)
   626  				}
   627  
   628  				// Calculate the offset from the start of the program within a section
   629  				progOff := int(absOff) - program.offset
   630  
   631  				// If the relocation entry is for before or after a current program (another program in the same
   632  				// section). Ignore it.
   633  				if progOff < 0 || progOff >= program.size {
   634  					continue
   635  				}
   636  
   637  				if bpfElf.elfFile.Sections[int(relocEntry.Symbol.Section)].Name == ".text" {
   638  					usesTxt = true
   639  					break
   640  				}
   641  			}
   642  
   643  			if usesTxt {
   644  				// Make a new instructions slice which can hold the main instruction and .text instructions
   645  				newInst := make([]ebpf.RawInstruction, len(program.Instructions)+len(bpfElf.txtInstr))
   646  				txtInsOff = copy(newInst, program.Instructions)
   647  				copy(newInst[txtInsOff:], bpfElf.txtInstr)
   648  				program.Instructions = newInst
   649  
   650  				// Add the BTF lines of the .text section to the ones of the program section
   651  				progLines := btfLinesPerSection[program.section]
   652  				for _, textLine := range btfLinesPerSection[".text"] {
   653  					progLines = append(progLines, BTFLine{
   654  						Section:           program.section,
   655  						SectionOffset:     progLines[0].SectionOffset,
   656  						InstructionOffset: textLine.InstructionOffset + uint32(txtInsOff*ebpf.BPFInstSize),
   657  						FileName:          textLine.FileName,
   658  						FileNameOffset:    textLine.FileNameOffset,
   659  						Line:              textLine.Line,
   660  						LineOffset:        textLine.LineOffset,
   661  						LineNumber:        textLine.LineNumber,
   662  						ColumnNumber:      textLine.ColumnNumber,
   663  					})
   664  				}
   665  				btfLinesPerSection[program.section] = progLines
   666  
   667  				progFuncs := btfFuncsPerSection[program.section]
   668  				for _, progFunc := range btfFuncsPerSection[".text"] {
   669  					progFuncs = append(progFuncs, BTFFunc{
   670  						Section:           program.section,
   671  						SectionOffset:     progLines[0].SectionOffset,
   672  						InstructionOffset: progFunc.InstructionOffset + uint32(txtInsOff*ebpf.BPFInstSize),
   673  						Type:              progFunc.Type,
   674  						TypeID:            progFunc.TypeID,
   675  					})
   676  				}
   677  				btfFuncsPerSection[program.section] = progFuncs
   678  			}
   679  		}
   680  
   681  		for _, progLines := range btfLinesPerSection[program.section] {
   682  			line := progLines.ToKernel()
   683  			// The offsets in ELF are in bytes from section start, for the kernel we need the offset of instructions
   684  			// Since the program is at the top, we can just divide by the instruction size.
   685  			line.InstructionOffset = line.InstructionOffset / uint32(ebpf.BPFInstSize)
   686  			program.BTFLines = append(program.BTFLines, line)
   687  		}
   688  		for _, progFuncs := range btfFuncsPerSection[program.section] {
   689  			f := progFuncs.ToKernel()
   690  			// The offsets in ELF are in bytes from section start, for the kernel we need the offset of instructions
   691  			// Since the program is at the top, we can just divide by the instruction size.
   692  			f.InstructionOffset = f.InstructionOffset / uint32(ebpf.BPFInstSize)
   693  			program.BTFFuncs = append(program.BTFFuncs, f)
   694  		}
   695  
   696  		if bpfElf.BTF != nil {
   697  			// Each program is only interested in lines and funcs applicable to itself, not other programs in the
   698  			// ELF file. So copy the BTF struct, this will keep pointers to types etc.
   699  			progBTF := *bpfElf.BTF
   700  
   701  			// Just replace the lines and funcs slices in this copy
   702  			progBTF.Lines = btfLinesPerSection[program.section]
   703  			progBTF.Funcs = btfFuncsPerSection[program.section]
   704  
   705  			program.BTF = &progBTF
   706  		}
   707  
   708  		// Handle relocation entries which can includes:
   709  		//  - Map references(need to be resolved at load time)
   710  		//  - BPF to BPF function calls (can be resolved here)
   711  		//  - Global data (.data, .bss, .rodata)
   712  		for _, relocEntry := range progRelocTable {
   713  			section := bpfElf.elfFile.Sections[relocEntry.Symbol.Section]
   714  
   715  			// The absolute offset from the start of the section to the location where the entry should be linked
   716  			absOff, err := relocEntry.AbsoluteOffset()
   717  			if err != nil {
   718  				return fmt.Errorf("unable to calculate absolute offset for relocation entry: %w", err)
   719  			}
   720  
   721  			// Calculate the offset from the start of the program within a section
   722  			progOff := int(absOff) - program.offset
   723  
   724  			// If the relocation entry is for before or after a current program (another program in the same section)
   725  			// Ignore it.
   726  			if progOff < 0 || progOff >= program.size {
   727  				continue
   728  			}
   729  
   730  			if section.Name == ".text" {
   731  				if txtInsOff == -1 {
   732  					return fmt.Errorf("unable to relocate .text entry since it is empty")
   733  				}
   734  
   735  				// Update the imm of the call instruction which points to a relocated function
   736  				// in the .text section to reflect the current relative offset
   737  				callInst := &program.Instructions[progOff/ebpf.BPFInstSize]
   738  				callInst.Imm = (int32(txtInsOff) + callInst.Imm) - (int32(progOff) / int32(ebpf.BPFInstSize))
   739  
   740  				continue
   741  			}
   742  
   743  			globalData := section.Name == ".data" || section.Name == ".rodata" || section.Name == ".bss"
   744  
   745  			// TODO maps and .maps should be handled differently. The maps section always contains "old school" 20byte
   746  			// map definitions. The .maps section, contains BTF-defined maps, the map definition for these maps should
   747  			// be created based on both the info in the section as well as BTF info.
   748  			// https://lwn.net/ml/netdev/20190531202132.379386-7-andriin@fb.com/
   749  			if section.Name == "maps" || section.Name == ".maps" || globalData {
   750  				// The map name is the name of the symbol truncated to BPF_OBJ_NAME_LEN
   751  				mapName := relocEntry.Symbol.Name
   752  
   753  				// the dot of the .data, .rodata, and .bss sections are removed
   754  				if globalData {
   755  					mapName = section.Name[1:]
   756  
   757  					firstInst := &program.Instructions[progOff/ebpf.BPFInstSize]
   758  					firstInst.SetSourceReg(ebpf.BPF_PSEUDO_MAP_FD_VALUE)
   759  				}
   760  
   761  				bpfMap, found := bpfElf.Maps[mapName]
   762  				if !found {
   763  					return fmt.Errorf("program references undefined map named '%s'", mapName)
   764  				}
   765  
   766  				// Add map to list of maps used by program if not already in list
   767  				_, found = program.Maps[mapName]
   768  				if !found {
   769  					program.Maps[mapName] = bpfMap
   770  				}
   771  
   772  				relLocations := program.MapFDLocations[mapName]
   773  				if relLocations == nil {
   774  					relLocations = []uint64{}
   775  				}
   776  
   777  				relLocations = append(relLocations, uint64(progOff))
   778  
   779  				program.MapFDLocations[mapName] = relLocations
   780  			}
   781  		}
   782  
   783  		// If this program has the .text section appended, we need to resolve any map relocations from that section
   784  		if txtInsOff != -1 {
   785  			txtRelocTable, found := bpfElf.relTables[".rel.text"]
   786  			if !found {
   787  				continue
   788  			}
   789  
   790  			for _, relocEntry := range txtRelocTable {
   791  				section := bpfElf.elfFile.Sections[relocEntry.Symbol.Section]
   792  
   793  				if section.Name == ".text" {
   794  					absOff, err := relocEntry.AbsoluteOffset()
   795  					if err != nil {
   796  						return fmt.Errorf("unable to calculate absolute offset for relocation entry: %w", err)
   797  					}
   798  
   799  					// Update the imm of the call instruction which points to a relocated function
   800  					// in the .text section to reflect the current relative offset
   801  					callInst := &program.Instructions[txtInsOff+int(absOff)/ebpf.BPFInstSize]
   802  					callInst.Imm = (int32(txtInsOff) + callInst.Imm) - (int32(absOff) / int32(ebpf.BPFInstSize))
   803  
   804  					continue
   805  				}
   806  
   807  				if section.Name == "maps" {
   808  					mapName := relocEntry.Symbol.Name
   809  					if bpfElf.settings.TruncateNames && len(mapName) > bpftypes.BPF_OBJ_NAME_LEN-1 {
   810  						mapName = mapName[:bpftypes.BPF_OBJ_NAME_LEN-1]
   811  					}
   812  
   813  					bpfMap, found := bpfElf.Maps[mapName]
   814  					if !found {
   815  						return fmt.Errorf("program .text references undefined map named '%s'", mapName)
   816  					}
   817  
   818  					// Add map to list of maps used by program if not already in list
   819  					_, found = program.Maps[mapName]
   820  					if !found {
   821  						program.Maps[mapName] = bpfMap
   822  					}
   823  
   824  					relLocations := program.MapFDLocations[mapName]
   825  					if relLocations == nil {
   826  						relLocations = []uint64{}
   827  					}
   828  
   829  					absOff, err := relocEntry.AbsoluteOffset()
   830  					if err != nil {
   831  						return fmt.Errorf("unable to calculate absolute offset for relocation entry: %w", err)
   832  					}
   833  
   834  					// Since the .text section is appended to the main program and the relocation offset is relative
   835  					// to the start of the .text section we need to add to offset of the .text instructions to the
   836  					// absolute address.
   837  					absOff += uint64(txtInsOff * ebpf.BPFInstSize)
   838  
   839  					relLocations = append(relLocations, absOff)
   840  
   841  					program.MapFDLocations[mapName] = relLocations
   842  				}
   843  			}
   844  		}
   845  	}
   846  
   847  	return nil
   848  }
   849  
   850  // specializePrograms takes the abstract programs from the ElfPrograms map and turns them into specialized program
   851  // types.
   852  func (bpfElf *bpfELF) specializePrograms() error {
   853  	for name, prog := range bpfElf.ElfPrograms {
   854  		// Assign license and BTF to abstract program before specializing them.
   855  		prog.License = bpfElf.license
   856  
   857  		specificProgInt := BPFProgramFromAbstract(prog.AbstractBPFProgram)
   858  
   859  		sectionParts := strings.Split(prog.section, "/")
   860  
   861  		// For some program types the section name can be used to pass additional information about
   862  		// how the program should be attached
   863  		switch specificProg := specificProgInt.(type) {
   864  		case *ProgramTracepoint:
   865  			if len(sectionParts) >= 2 {
   866  				specificProg.DefaultCategory = sectionParts[1]
   867  			}
   868  			if len(sectionParts) >= 3 {
   869  				specificProg.DefaultName = sectionParts[2]
   870  			}
   871  
   872  		case *ProgramKProbe:
   873  			uprobe := false
   874  			switch sectionParts[0] {
   875  			case "kprobe":
   876  				specificProg.DefaultType = perf.TypeKProbe
   877  
   878  			case "kretprobe":
   879  				specificProg.DefaultType = perf.TypeKRetprobe
   880  
   881  			case "uprobe":
   882  				uprobe = true
   883  				specificProg.DefaultType = perf.TypeUProbe
   884  
   885  			case "uretprobe":
   886  				uprobe = true
   887  				specificProg.DefaultType = perf.TypeURetProbe
   888  			}
   889  
   890  			if uprobe {
   891  				var path, offsetStr string
   892  				if len(sectionParts) == 2 {
   893  					path = "/" + sectionParts[1]
   894  				}
   895  
   896  				if len(sectionParts) == 3 {
   897  					path = "/" + sectionParts[1]
   898  					offsetStr = sectionParts[2]
   899  				}
   900  
   901  				if len(sectionParts) > 3 {
   902  					path = "/" + strings.Join(sectionParts[1:len(sectionParts)-1], "/")
   903  					offsetStr = sectionParts[len(sectionParts)-1]
   904  				}
   905  
   906  				specificProg.DefaultPath = path
   907  				if off, err := strconv.ParseInt(offsetStr, 0, 64); err == nil {
   908  					specificProg.DefaultOffset = int(off)
   909  				}
   910  
   911  			} else {
   912  				specificProg.DefaultEvent = name
   913  				if len(sectionParts) == 2 {
   914  					specificProg.DefaultSymbol = sectionParts[1]
   915  				}
   916  				if len(sectionParts) >= 3 {
   917  					specificProg.DefaultModule = sectionParts[1]
   918  					specificProg.DefaultSymbol = sectionParts[2]
   919  				}
   920  			}
   921  		}
   922  
   923  		bpfElf.Programs[name] = specificProgInt
   924  	}
   925  
   926  	return nil
   927  }
   928  
   929  // toBPFELF converts the internal bpfELF to the external BPFELF version which doesn't contain the intermediate
   930  // variables produced during parsing.
   931  func (bpfElf *bpfELF) toBPFELF() BPFELF {
   932  	retBpfELF := BPFELF{
   933  		ByteOrder: bpfElf.ByteOrder,
   934  		Maps:      bpfElf.Maps,
   935  		Programs:  bpfElf.Programs,
   936  		BTF:       bpfElf.BTF,
   937  	}
   938  
   939  	return retBpfELF
   940  }
   941  
   942  // This map translates ELF section names to program types.
   943  // https://github.com/libbpf/libbpf/blob/eaea2bce024fa6ae0db54af1e78b4d477d422791/src/libbpf.c#L8270
   944  var sectionNameToProgType = map[string]bpftypes.BPFProgType{
   945  	"sock_filter":             bpftypes.BPF_PROG_TYPE_SOCKET_FILTER,
   946  	"socket":                  bpftypes.BPF_PROG_TYPE_SOCKET_FILTER,
   947  	"kprobe":                  bpftypes.BPF_PROG_TYPE_KPROBE,
   948  	"kretprobe":               bpftypes.BPF_PROG_TYPE_KPROBE,
   949  	"uprobe":                  bpftypes.BPF_PROG_TYPE_KPROBE,
   950  	"uretprobe":               bpftypes.BPF_PROG_TYPE_KPROBE,
   951  	"tc_cls":                  bpftypes.BPF_PROG_TYPE_SCHED_CLS,
   952  	"tc_act":                  bpftypes.BPF_PROG_TYPE_SCHED_ACT,
   953  	"tracepoint":              bpftypes.BPF_PROG_TYPE_TRACEPOINT,
   954  	"xdp":                     bpftypes.BPF_PROG_TYPE_XDP,
   955  	"perf_event":              bpftypes.BPF_PROG_TYPE_PERF_EVENT,
   956  	"cgroup_skb":              bpftypes.BPF_PROG_TYPE_CGROUP_SKB,
   957  	"cgroup_sock":             bpftypes.BPF_PROG_TYPE_CGROUP_SOCK,
   958  	"lwt_in":                  bpftypes.BPF_PROG_TYPE_LWT_IN,
   959  	"lwt_out":                 bpftypes.BPF_PROG_TYPE_LWT_OUT,
   960  	"lwt_xmit":                bpftypes.BPF_PROG_TYPE_LWT_XMIT,
   961  	"sock_opts":               bpftypes.BPF_PROG_TYPE_SOCK_OPS,
   962  	"sk_skb":                  bpftypes.BPF_PROG_TYPE_SK_SKB,
   963  	"cgroup_device":           bpftypes.BPF_PROG_TYPE_CGROUP_DEVICE,
   964  	"raw_tracepoint":          bpftypes.BPF_PROG_TYPE_RAW_TRACEPOINT,
   965  	"cgroup_sock_addr":        bpftypes.BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
   966  	"lwt_seg6local":           bpftypes.BPF_PROG_TYPE_LWT_SEG6LOCAL,
   967  	"lirc_mode2":              bpftypes.BPF_PROG_TYPE_LIRC_MODE2,
   968  	"sk_reuseport":            bpftypes.BPF_PROG_TYPE_SK_REUSEPORT,
   969  	"flow_dissector":          bpftypes.BPF_PROG_TYPE_FLOW_DISSECTOR,
   970  	"cgroup_sysctl":           bpftypes.BPF_PROG_TYPE_CGROUP_SYSCTL,
   971  	"raw_tracepoint_writable": bpftypes.BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
   972  	"cgroup_sockopt":          bpftypes.BPF_PROG_TYPE_CGROUP_SOCKOPT,
   973  	"tracing":                 bpftypes.BPF_PROG_TYPE_TRACING,
   974  	"struct_ops":              bpftypes.BPF_PROG_TYPE_STRUCT_OPS,
   975  	"ext":                     bpftypes.BPF_PROG_TYPE_EXT,
   976  	"lsm":                     bpftypes.BPF_PROG_TYPE_LSM,
   977  	"sk_lookup":               bpftypes.BPF_PROG_TYPE_SK_LOOKUP,
   978  	"syscall":                 bpftypes.BPF_PROG_TYPE_SYSCALL,
   979  }
   980  
   981  type elfRelocTable []elfRelocEntry
   982  
   983  // elf_r_bpf The BPF ELF reloc types for BPF.
   984  // https://github.com/llvm/llvm-project/blob/74d9a76ad3f55c16982ceaa8b6b4a6b7744109b1/llvm/include/llvm/BinaryFormat/ELFRelocs/BPF.def
   985  //nolint:lll
   986  type elf_r_bpf int
   987  
   988  const (
   989  	// r_bpf_none is an invalid relocation type
   990  	//nolint:deadcode,varcheck // want to keep this here for completeness
   991  	r_bpf_none elf_r_bpf = 0
   992  	// r_bpf_64_64 indicates that 32 bits should be relocated
   993  	r_bpf_64_64 elf_r_bpf = 1
   994  	// r_bpf_64_32 insicates that 64 bits should be relocated
   995  	r_bpf_64_32 elf_r_bpf = 10
   996  )
   997  
   998  type elfRelocEntry struct {
   999  	elf.Rel64
  1000  
  1001  	Symbol *elf.Symbol
  1002  	Type   elf_r_bpf
  1003  }
  1004  
  1005  func (e *elfRelocEntry) AbsoluteOffset() (uint64, error) {
  1006  	switch e.Type {
  1007  	case r_bpf_64_64, r_bpf_none:
  1008  		// Just the absolute offset from the beginning of the program section
  1009  		return e.Off, nil
  1010  	case r_bpf_64_32:
  1011  		// Just the absolute offset from the beginning of the program section truncated to 32 bits
  1012  		const _32bitMask = 0x00000000FFFFFFFF
  1013  		return e.Off & _32bitMask, nil
  1014  	}
  1015  
  1016  	return 0, fmt.Errorf("reloc type not implemented: '%d'", e.Type)
  1017  }