Invalid argument (EINVAL) when trying to load a BPF program - c

I am trying to load a BPF program using the bpf syscall but I am receiving invalid argument (EINVAL) on return. From the man page, the possible reasons for this are:
EINVAL
For BPF_PROG_LOAD, indicates an attempt to load an invalid program.
eBPF programs can be deemed invalid due to unrecognized instructions,
the use of reserved fields, jumps out of range, infinite loops or calls
of unknown functions.
So it seems there is something wrong with my BPF program. My BPF program is as follows:
#include <uapi/linux/bpf.h>
int prog(struct pt_regs *ctx)
{
return 0;
}
Which surely cannot have anything wrong with it.
I am compiling with the Makefile here (I removed most of the code from test_overhead_kprobe_kern.c to give a very simple program for testing).
What could be wrong with my program that caused it to get rejected?
uname -a: Linux ubuntu1710 4.13.0-32-generic #35-Ubuntu SMP Thu Jan 25 09:13:46 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
My full userspace code (in Go) is as follows:
package main
/*
#include <stdio.h>
#include <stdlib.h>
void print(char* s) {
printf("%s\n", s);
}
*/
import "C"
import (
"unsafe"
"golang.org/x/sys/unix"
"github.com/cilium/cilium/pkg/bpf"
)
import (
"fmt"
"io/ioutil"
)
const (
bufferSize = 256
sessionIDHTTPHeader = "X-Session-ID"
defaultServerAddress = "localhost"
defaultPort = 5050
)
const (
BPF_PROG_TYPE_UNSPEC = 0
BPF_PROG_TYPE_SOCKET_FILTER = 1
BPF_PROG_TYPE_KPROBE = 2
BPF_PROG_TYPE_SCHED_CLS = 3
BPF_PROG_TYPE_SCHED_ACT = 4
)
type ttyWrite struct {
Count int32
Buf [bufferSize]byte
SessionID int32
}
func main() {
//for i := 0; i < 6; i++ {
//b, err := ioutil.ReadFile(fmt.Sprintf("bpf/test%d.o", i))
b, err := ioutil.ReadFile("bpf/bpf_tty.o")
if err != nil {
fmt.Print(err)
}
err = loadProgram(BPF_PROG_TYPE_KPROBE, unsafe.Pointer(&b), len(b))
if err != nil {
fmt.Printf("%s\n", err)
}
//}
}
func loadProgram(progType int, insns unsafe.Pointer, insnCnt int) error {
licenseBuf := "GPL"
licenseStr := C.CString(licenseBuf)
defer C.free(unsafe.Pointer(licenseStr))
logStr := C.CString("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
defer C.free(unsafe.Pointer(logStr))
lba := struct {
progType uint32
//pad0 [4]byte
insnCnt uint32
//pad1 [4]byte
insns uint64
license uint64
logLevel uint32
//pad2 [4]byte
logSize uint32
//pad3 [4]byte
logBuf uint64
kernVersion uint32
//pad4 [4]byte
}{
progType: uint32(progType),
insnCnt: uint32(insnCnt),
insns: uint64(uintptr(insns)),
license: uint64(uintptr(unsafe.Pointer(licenseStr))),
logLevel: uint32(1),
logSize: uint32(50),
logBuf: uint64(uintptr(unsafe.Pointer(logStr))),
//logBuf: uint64(uintptr(unsafe.Pointer(bufStr))),
// /usr/src/linux-headers-4.13.0-32-generic/include/generated/uapi/linux/version.h
kernVersion: uint32(265485),
}
ret, _, err := unix.Syscall(
unix.SYS_BPF,
bpf.BPF_PROG_LOAD,
uintptr(unsafe.Pointer(&lba)),
unsafe.Sizeof(lba),
)
//fmt.Printf("%s\n", logBuf)
//cs := C.CString("XXXXXXXXXX")
C.print(logStr)
//fmt.Printf("%c\n", *logStr)
if ret != 0 || err != 0 {
//fmt.Printf("%#v %d\n", logBuf, unsafe.Sizeof(lba))
return fmt.Errorf("Unable to load program: ret: %d: %s", int(ret), err)
}
return nil
}

As Qeole pointed out in your previous question, your userspace Go program needs to extract the BPF instructions (.text section) from the object file. Otherwise, the kernel will try to interpret the binary content as BPF instructions and inevitably fail.

Related

Header file not found in XDP source compiled in Go program

My XDP module compiles by itself, but compilation within the context of a Go program fails because it can't find bpf/bpf_helpers.h file. Here's the code leading up the problem:
package main
import (
"fmt"
bpf "github.com/iovisor/gobpf/bcc"
log "github.com/sirupsen/logrus"
"io/ioutil"
"os"
)
/*
#cgo CFLAGS: -I/usr/include/bcc/compat
#cgo LDFLAGS: -lbcc
#include <bcc/bcc_common.h>
#include <bcc/libbpf.h>
void perf_reader_free(void *ptr);
*/
import "C"
func main() {
// Get the source code from disk
source, err := ioutil.ReadFile("xdp/collect_ips.c")
if err != nil {
log.Fatalln("Cannot read collect_ips.c")
}
// Compile module
module := bpf.NewModule(string(source), []string{
"-Wall",
"-O2",
})
defer module.Close()
// Load module
fn, err := module.Load("collect_ips", C.BPF_PROG_TYPE_XDP, 1, 65536) // Problem happens here
// ...
The Go program compiles fine, but I when I run the program, I get this:
/virtual/main.c:2:10: fatal error: 'bpf/bpf_helpers.h' file not found
#include <bpf/bpf_helpers.h>
^~~~~~~~~~~~~~~~~~~
9 warnings and 1 error generated.
panic: runtime error: invalid memory address or nil pointer dereference
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4b23d4]
goroutine 1 [running]:
github.com/iovisor/gobpf/bcc.(*Module).Close.func1(0x203000)
/home/me/go/pkg/mod/github.com/iovisor/gobpf#v0.2.0/bcc/module.go:132 +0x14
github.com/iovisor/gobpf/bcc.(*Module).Close(0x0)
/home/me/go/pkg/mod/github.com/iovisor/gobpf#v0.2.0/bcc/module.go:132 +0x36
panic({0x4da720, 0x59ec60})
/usr/lib/go-1.17/src/runtime/panic.go:1038 +0x215
github.com/iovisor/gobpf/bcc.(*Module).Load(0x0, {0x4ef9f2, 0xb}, 0x2, 0x2, 0x8001)
/home/me/go/pkg/mod/github.com/iovisor/gobpf#v0.2.0/bcc/module.go:202 +0x36
main.main()
/home/me/go/src/code.squarespace.net/net/prism-stream/cmd/server/main.go:35 +0x1b7
This problem is happening because of my XDP module because if I comment out this header file in the C source, the error moves to a different header file.
I think this is happening because of bpf_helpers.h does not exist here https://github.com/iovisor/gobpf/tree/master/elf/include. If this is the issue, is there a way to use the header file from /usr/include/bpf?
If I take out bpf_helpers.h from the XDP code, I get an error complaining about the use of SEC in my code:
struct bpf_map_def SEC("maps") addr_map = {
.type = BPF_MAP_TYPE_LRU_HASH,
.key_size = sizeof(struct addr_desc_struct),
.value_size = sizeof(long),
.max_entries = 4096
};
I copied the macro for SEC from bpf_helpers.h to my code, but I then get error: variable has incomplete type 'struct bpf_map_def'. I also use bpf_map_lookup_elem() and bpf_map_update_elem(), which are defined in the bpf/ directory.
I used the eBPF library from Cilium. Here's an example program that loads .o file to an interface, waits for 10 seconds, and prints whatever is in the BPF map.
You have to make sure your XDP function has SEC("xdp") before it. Whereas if you load the program using xdp-loader, you can pass whatever you want to SEC as long as it's different from the name of the function.
package main
import (
"bytes"
"fmt"
"github.com/cilium/ebpf"
log "github.com/sirupsen/logrus"
"github.com/vishvananda/netlink"
"time"
)
func main() {
var objects struct {
XCProg *ebpf.Program `ebpf:"collect_ips"` // Name of function containing the XDP program
XCMap *ebpf.Map `ebpf:"addr_map"` // Name of the map
}
spec, err := ebpf.LoadCollectionSpec("dist/collect_ips.o")
if err != nil {
log.Fatalln("ebpf.LoadCollectionSpec", err)
}
if err := spec.LoadAndAssign(&objects, nil); err != nil {
log.Fatalln("ebpf.LoadAndAssign", err)
}
link, err := netlink.LinkByName("enp0s8")
if err != nil {
log.Fatalln("netlink.LinkByName", err)
}
err = netlink.LinkSetXdpFdWithFlags(link, objects.XCProg.FD(), 2)
if err != nil {
log.Fatalln("netlink.LinkSetXdpFdWithFlags")
}
time.Sleep(time.Second*10)
var keyBytes, valueBytes []byte
iterator := objects.XCMap.Iterate()
if iterator == nil {
log.Fatalln("nil iterator")
}
for iterator.Next(&keyBytes, &valueBytes) != false {
fmt.Printf("%x: %x", keyBytes, valueBytes)
}
defer objects.XCProg.Close()
defer objects.XCMap.Close()
}

how to copy C.int address into C.char in cgo?

using cgo I am calling c function. I ran into situation where I have to copy C.int address into C.char[4]. Can I do that in go?
code snippet C- Structure:
struct data
{
char *a_add;
unsigned int length;
}
Go-Code
func main() {
var d[3] C.data
var filedescriptor C.int
d[0].a_add = &filedescriptor
d[0].length = 4
}
The problem is a_add is a char*. But I need to pass int variable address. The c-code is a legacy code, and I can't fix datatype now. Other C modules uses it, and it's working in C with a warning. where as in go, it is error.
Is there any way that I can copy address of int variable into char* array in cgo.
Update:
I tried d[0].a_add = (*C.char)(unsafe.Pointer(&filedescriptor )),
getting Error:
panic: runtime error: cgo argument has Go pointer to Go pointer
What Am I missing?
One of the problems you are running into is that in a call into C code, you may not pass a pointer to a Go pointer. The variable filedescriptor is a C.int, but &filedescriptor is a Go pointer, so you cannot use that (or rather, you cannot use that in the a_add field as a value).
There is a great deal about your C code that is not clear to me, but you can probably use the code below. Note that this code may be overkill for your particular situation. It is not meant to be particularly efficient or good, just as clear as possible while being extremely flexible—for instance, it can read from and write to packed C structures.
package main
// #include <stdio.h>
// #include <stdlib.h>
// #include <string.h>
//
// struct data {
// char *a_add;
// unsigned int length;
// };
//
// void f(struct data *p) {
// printf("p->a_add = %p, p->length = %u\n", p->a_add, p->length);
// printf("p->a_add as an int: %d\n", *(int *)p->a_add);
// *(int *)p->a_add = 0x12345678;
// }
import "C"
import (
"fmt"
"unsafe"
)
const cIntSize = C.sizeof_int
// Produce a Go int64 from a C int. The caller passes the address
// of the C int.
func int64FromCInt(ci unsafe.Pointer) int64 {
// Get a slice pointing to the bytes of the C int.
sci := (*[cIntSize]byte)(ci)[:]
switch {
case cIntSize == unsafe.Sizeof(int64(0)):
var gi int64
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi))[:]
copy(sgi, sci)
return gi
case cIntSize == unsafe.Sizeof(int32(0)):
var gi int32
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi))[:]
copy(sgi, sci)
return int64(gi)
case cIntSize == unsafe.Sizeof(int(0)):
var gi int
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi))[:]
copy(sgi, sci)
return int64(gi)
default:
panic("no Go integer size matches C integer size")
}
}
// Write C int (via an unsafe.Pointer) from Go int. The caller
// passes the address of the C int.
func writeCIntFromInt(gi int, ci unsafe.Pointer) {
// Get a slices covering the bytes of the C int.
sci := (*[cIntSize]byte)(ci)[:]
switch {
case cIntSize == unsafe.Sizeof(gi):
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi))[:]
copy(sci, sgi)
case cIntSize == unsafe.Sizeof(int64(0)):
// Copy value to int64 for copying purposes.
// Since int64 holds all int values, this always works.
gi2 := int64(gi)
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi2))[:]
copy(sci, sgi)
case cIntSize == unsafe.Sizeof(int32(0)):
// Copy value to int32 for copying purposes.
// Panic if we destroy the value via truncation.
gi2 := int32(gi)
if int(gi2) != gi {
panic(fmt.Sprintf("unable to send Go value %x to C: size of Go int=%d, size of C int=%d", gi, unsafe.Sizeof(gi), cIntSize))
}
sgi := (*[unsafe.Sizeof(gi)]byte)(unsafe.Pointer(&gi2))[:]
copy(sci, sgi)
default:
panic("no Go integer size matches C integer size")
}
}
func main() {
b := C.malloc(cIntSize)
defer C.free(b)
writeCIntFromInt(32767, b)
d := C.struct_data{a_add: (*C.char)(b), length: cIntSize}
fmt.Println("calling C.f(d)")
C.f(&d)
result := int64FromCInt(unsafe.Pointer(d.a_add))
fmt.Printf("result = %#x\n", result)
}

Pointer from C is removed from memory

In this situation, I pass a pointer from Go to a C function, the C function modifies that pointer value (fills in an array) and I use the same pointer again on the Go code, making sure to call C.free to release it after I am done.
I am sometimes getting a nil reference on that pointer, I am having a hard time understanding why.
Here, the object in Go is cpuTimesC, in C it is the cputicks.
I have also tried making the function return the pointer, with the same results. The weirdest thing is that if I put a printf statement at the end of the function, it takes longer before I eventually get the nil error.
package collector
/*
#cgo LDFLAGS: -lperfstat
#include <stdlib.h>
#include <stdio.h>
#include <libperfstat.h>
#include <string.h>
#include <time.h>
u_longlong_t **ref;
int getCPUTicks(uint64_t **cputicks, size_t *cpu_ticks_len) {
int i, ncpus, cputotal;
perfstat_id_t firstcpu;
perfstat_cpu_t *statp;
cputotal = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);
if (cputotal <= 0){
return -1;
}
statp = calloc(cputotal, sizeof(perfstat_cpu_t));
if(statp==NULL){
return -1;
}
ncpus = perfstat_cpu(&firstcpu, statp, sizeof(perfstat_cpu_t), cputotal);
*cpu_ticks_len = ncpus*4;
*cputicks = (uint64_t *) malloc(sizeof(uint64_t)*(*cpu_ticks_len));
for (i = 0; i < ncpus; i++) {
int offset = 4 * i;
(*cputicks)[offset] = statp[i].user;
(*cputicks)[offset+1] = statp[i].sys;
(*cputicks)[offset+2] = statp[i].wait;
(*cputicks)[offset+3] = statp[i].idle;
}
return 0;
}
*/
import "C"
import (
"errors"
"unsafe"
"fmt"
"github.com/prometheus/client_golang/prometheus"
)
const ClocksPerSec = float64(C.CLK_TCK)
const maxCPUTimesLen = 1024 * 4
type statCollector struct {
cpu *prometheus.Desc
}
func init() {
registerCollector("cpu", true, NewCPUCollector)
}
func NewCPUCollector() (Collector, error) {
return &statCollector{
cpu: nodeCPUSecondsDesc,
}, nil
}
func (c *statCollector) Update(ch chan<- prometheus.Metric) error {
var fieldsCount = 4
cpuFields := []string{"user", "sys", "wait", "idle"}
var (
cpuTimesC *C.uint64_t
cpuTimesLength C.size_t
)
if C.getCPUTicks(&cpuTimesC, &cpuTimesLength) == -1 {
return errors.New("could not retrieve CPU times")
}
defer C.free(unsafe.Pointer(cpuTimesC))
cput := (*[maxCPUTimesLen]C.u_longlong_t)(unsafe.Pointer(cpuTimesC))[:cpuTimesLength:cpuTimesLength]
cpuTicks := make([]float64, cpuTimesLength)
for i, value := range cput {
cpuTicks[i] = float64(value) / ClocksPerSec
}
for i, value := range cpuTicks {
cpux := fmt.Sprintf("CPU %d", i/fieldsCount)
ch <- prometheus.MustNewConstMetric(c.cpu, prometheus.CounterValue, value, cpux, cpuFields[i%fieldsCount])
}
return nil
}
The error is:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x33 addr=0x0 pc=0x1003fcec0]
goroutine 940 [running]:
github.com/dlopes7/aix-prometheus-exporter/collector.(*statCollector).Update(0xa000100000d21b8, 0xa0001000028c480, 0x0, 0x0)
/home/david/go/src/github.com/dlopes7/aix-prometheus-exporter/collector/cpu_aix.go:81 +0xf0
github.com/dlopes7/aix-prometheus-exporter/collector.execute(0x10043e7e7, 0x3, 0x1101120e0, 0xa000100000d21b8, 0xa0001000028c480)
/home/david/go/src/github.com/dlopes7/aix-prometheus-exporter/collector/collector.go:95 +0x6c
github.com/dlopes7/aix-prometheus-exporter/collector.AIXCollector.Collect.func1(0xa0001000028c480, 0xa000100000cd440, 0x10043e7e7, 0x3, 0x1101120e0, 0xa000100000d21b8)
/home/david/go/src/github.com/dlopes7/aix-prometheus-exporter/collector/collector.go:115 +0x4c
created by github.com/dlopes7/aix-prometheus-exporter/collector.AIXCollector.Collect
/home/david/go/src/github.com/dlopes7/aix-prometheus-exporter/collector/collector.go:114 +0xf8
And I know it happens here, because for some reason cpuTimesC is nil:
cput := (*[maxCPUTimesLen]C.u_longlong_t)(unsafe.Pointer(cpuTimesC))[:cpuTimesLength:cpuTimesLength]
Why would this object be nil sometimes, and how do I make it remain in memory until I call that C.free?
This is on AIX PPC64 with cgo if that makes any difference.
This had nothing to do with pointers being removed from memory, the cputicks was NULL because ncpus was -1 so the malloc call returned NULL, ncpu was -1 because the first parameter of the perfstat_cpu call was not initialized properly, adding:
strcpy(firstcpu.name, FIRST_CPU);
Before the call to perfstat_cpu fixed the issue, as suggested by #kostik

How to cast bytes to struct(c struct) in go?

package main
/*
#define _GNU_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <utmpx.h>
#include <fcntl.h>
#include <unistd.h>
char *path_utmpx = _PATH_UTMPX;
typedef struct utmpx utmpx;
*/
import "C"
import (
"fmt"
"io/ioutil"
)
type Record C.utmpx
func main() {
path := C.GoString(C.path_utmpx)
content, err := ioutil.ReadFile(path)
handleError(err)
var records []Record
// now we have the bytes(content), the struct(Record/C.utmpx)
// how can I cast bytes to struct ?
}
func handleError(err error) {
if err != nil {
panic("bad")
}
}
I'm trying to read content into Record
I have asked a few related questions.
Cannot access c variables in cgo
Can not read utmpx file in go
I have read some articles and posts but still cannot figure out a way to do this.
I think you're going about this the wrong way. If you were wanting to use the C library, you would use the C library to read the file.
Don't use cgo purely to have struct definitions, you should create these in Go yourself. You could then write the appropriate marshal / unmarshal code to read from the raw bytes.
A quick Google shows that someone has already done the work required to convert a look of the relevant C library to Go. See the utmp repository.
A short example of how this could be used is:
package main
import (
"bytes"
"fmt"
"log"
"github.com/ericlagergren/go-gnulib/utmp"
)
func handleError(err error) {
if err != nil {
log.Fatal(err)
}
}
func byteToStr(b []byte) string {
i := bytes.IndexByte(b, 0)
if i == -1 {
i = len(b)
}
return string(b[:i])
}
func main() {
list, err := utmp.ReadUtmp(utmp.UtmpxFile, 0)
handleError(err)
for _, u := range list {
fmt.Println(byteToStr(u.User[:]))
}
}
You can view the GoDoc for the utmp package for more information.

Pass double array by reference from C to Delphi DLL

For our Delphi (XE5) application we are developing an API. In order to communicate data from the Delphi DLL functions with the main program (C based; either (console) C or C++ code applications or Matlab and Simulink) arrays that are allocated by the caller need to be filled with doubles by the DLL.
I understand that open array (Delphi specific) are not very convenient for this purpose as they contain additional data that you then have to mimic in C. Instead I was planning to use pointer arithmetic (see dll function: inc(APDouble)) by directly pointing to the correct address. My question is if this is how you software developers would do this.
A demo is included below (full sources).
The DLL (made in DXE5):
library PDA;
uses
System.StrUtils,
System.SysUtils,
Vcl.Dialogs;
{$R *.res}
function ShowArrayContents( APDouble: PDouble;
size: Integer): integer; cdecl; export;
var
i: Integer;
begin
Result := 0;
for i := 0 to size-1 do
begin
// Show value!
MessageDlg(Format('%p -> %p -> %f', [#APDouble, APDouble, APDouble^]), mtWarning, [mbOK], 0);
Inc(APDouble);
end;
end;
exports
ShowArrayContents;
begin
end.
The C-code caller (made in C++ builder XE4):
#include <stdio.h>
#include <tchar.h>
#include <stdlib.h>
#include <windows.h>
typedef int (*_ShowArrayContents) (double *, int);
char *dllname = "PDA.dll";
static HINSTANCE hInstanceControl;
_ShowArrayContents ShowArrayContents = NULL;
int _tmain(int argc, _TCHAR* argv[])
{
double DVals[3] = {1.23, 4.56, 7.89};
int i;
hInstanceControl = LoadLibrary(dllname);
if( hInstanceControl != NULL){
ShowArrayContents =(_ShowArrayContents)GetProcAddress(hInstanceControl, "ShowArrayContents");
} else {
return 0;
}
// test program:
(*ShowArrayContents)(&DVals[0], 3);
FreeLibrary(hInstanceControl);
system("pause");
return 0;
}
Your code works fine, but as you observe it is awkward. For interop like this I would bite the bullet and use the $POINTERMATH directive. This allows you to treat a pointer as if it were an array, just as you do in C or C++. For example:
{$POINTERMATH ON}
function GetSum(arr: PDouble; len: Integer): Double; cdecl;
var
i: Integer;
begin
Result := 0.0;
for i := 0 to len-1 do
Result := Result + arr[i];
end;
Another option would be to copy to a native Delphi array and use that for onward processing. Obviously that involves a copy, but sometimes that's actually what you want. In that case you could do it like this:
var
x: TArray<Double>;
....
SetLength(x, len);
Move(arr^, Pointer(x)^, len*SizeOf(arr^));
Or if you don't like the use of Move, a good old loop:
{$POINTERMATH ON}
var
i: Integer;
x: TArray<Double>;
....
SetLength(x, len);
for i := 0 to len-1 do
x[i] := arr[i];

Resources