in pkg/elfparser/elf.go [462:540]
func (e *elfLoader) parseAndApplyRelocSection(progIndex uint32, loadedMaps map[string]ebpf_maps.BpfMap) ([]byte, map[int]string, error) {
progEntry := e.progSectionMap[progIndex]
reloSection := e.reloSectionMap[progIndex]
data, err := progEntry.progSection.Data()
if err != nil {
return nil, nil, err
}
log.Infof("Loading Program with relocation section; Info:%v; Name: %s, Type: %s; Size: %v", reloSection.Info,
reloSection.Name, reloSection.Type, reloSection.Size)
relocationEntries, err := e.parseRelocationSection(reloSection, e.elfFile)
if err != nil || len(relocationEntries) == 0 {
return nil, nil, fmt.Errorf("unable to parse relocation entries....")
}
log.Infof("Applying Relocations..")
associatedMaps := make(map[int]string)
for _, relocationEntry := range relocationEntries {
if relocationEntry.relOffset >= len(data) {
return nil, nil, fmt.Errorf("invalid offset for the relocation entry %d", relocationEntry.relOffset)
}
//eBPF has one 16-byte instruction: BPF_LD | BPF_DW | BPF_IMM which consists
//of two consecutive 'struct bpf_insn' 8-byte blocks and interpreted as single
//instruction that loads 64-bit immediate value into a dst_reg.
ebpfInstruction := &utils.BPFInsn{
Code: data[relocationEntry.relOffset],
DstReg: data[relocationEntry.relOffset+1] & 0xf,
SrcReg: data[relocationEntry.relOffset+1] >> 4,
Off: int16(binary.LittleEndian.Uint16(data[relocationEntry.relOffset+2:])),
Imm: int32(binary.LittleEndian.Uint32(data[relocationEntry.relOffset+4:])),
}
log.Infof("BPF Instruction code: %s; offset: %d; imm: %d", ebpfInstruction.Code, ebpfInstruction.Off, ebpfInstruction.Imm)
//Validate for Invalid BPF instructions
if ebpfInstruction.Code != (unix.BPF_LD | unix.BPF_IMM | unix.BPF_DW) {
return nil, nil, fmt.Errorf("invalid BPF instruction (at %d): %d",
relocationEntry.relOffset, ebpfInstruction.Code)
}
// Point BPF instruction to the FD of the map referenced. Update the last 4 bytes of
// instruction (immediate constant) with the map's FD.
// BPF_MEM | <size> | BPF_STX: *(size *) (dst_reg + off) = src_reg
// BPF_MEM | <size> | BPF_ST: *(size *) (dst_reg + off) = imm32
mapName := relocationEntry.symbol.Name
log.Infof("Map to be relocated; Name: %s", mapName)
var mapFD int
var map_id int
// Relocated maps can be defined in the same BPF file or defined elsewhere but
// using it here. So during relocation we search if it is a local map or
// it is a global map.
if progMap, ok := loadedMaps[mapName]; ok {
map_id = int(progMap.MapID)
associatedMaps[map_id] = mapName
mapFD = int(progMap.MapFD)
} else if globalMapFd, ok := sdkCache.Get(mapName); ok {
log.Infof("Found FD %d in SDK cache", globalMapFd)
mapFD = globalMapFd
} else {
return nil, nil, fmt.Errorf("failed to get map FD '%s' doesn't exist", mapName)
}
log.Infof("Map found. Replace the offset with corresponding Map FD: %v", mapFD)
ebpfInstruction.SrcReg = 1 //dummy value for now
ebpfInstruction.Imm = int32(mapFD)
copy(data[relocationEntry.relOffset:relocationEntry.relOffset+8], ebpfInstruction.ConvertBPFInstructionToByteStream())
log.Infof("From data: BPF Instruction code: %d; offset: %d; imm: %d",
uint8(data[relocationEntry.relOffset]),
uint16(binary.LittleEndian.Uint16(data[relocationEntry.relOffset+2:relocationEntry.relOffset+4])),
uint32(binary.LittleEndian.Uint32(data[relocationEntry.relOffset+4:relocationEntry.relOffset+8])))
}
return data, associatedMaps, nil
}