Search code examples
cebpfxdp-bpf

bpf_trace_printk causes program not loaded in kernel -- libbpf: Program 'xdp' contains unrecognized relo data pointing to section 6


following is my code and libbpf throws error that

libbpf: Program 'xdp' contains unrecognized relo data pointing to section 6
ERR: loading BPF-OBJ file(./k.o) (-2): No such file or directory
ERR: loading file: ./k.o

Why is that. This is how I am printing with bpf_trace_printk

      bpf_trace_printk("hello\n",sizeof("hello\n"));

this is full code

#include <stddef.h>
#include <linux/bpf.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/ipv6.h>
#include <linux/icmpv6.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
/* Defines xdp_stats_map from packet04 */
#include "../common/xdp_stats_kern_user.h"
#include "../common/xdp_stats_kern.h"
#include <../common/parsing_helpers.h>


#include <bpf/bpf_helpers.h>

#define ETH_ALEN 6

#define MAX_ENTRIES 1000

/*struct {
    __uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
    __type(key, __u32);
    __type(value, __u64);
    __uint(max_entries, MAX_ENTRIES);
} hash_map SEC(".maps");
*/

struct hash_elem {
    int cnt;
    struct bpf_spin_lock lock;
};
struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __type(key, __u32);
    __type(value, struct hash_elem);
    __uint(max_entries, 100);
} hash_map SEC(".maps");
struct a{struct bpf_spin_lock lock;};

struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __type(key, __u32);
    __type(value, long);
    __uint(max_entries, 2);
} hash_map1 SEC(".maps");
//static __u32 i=0;

struct icmphdr1
{
  __u8 type;        /* message type */
  __u8 code;        /* type sub-code */
  __u16 checksum;
  union
  {
    struct
    {
      __u16 id;
    __u16   sequence;
    } echo;         /* echo datagram */
    __u32   gateway;    /* gateway address */
    struct
    {
      __u16 __glibc_reserved;
      __u16 mtu;
    } frag;         /* path mtu discovery */
  } un;
};

SEC("xdp")
int  xdp_prog_simple(struct xdp_md *ctx)
{
    
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;
    struct ethhdr *eth = data;
    __u16 h_proto;

    if (eth + 1 > data_end)
        return XDP_DROP;
    
        
    
    h_proto = eth->h_proto;
    if (h_proto == bpf_htons(ETH_P_IP))
    {
    
        struct iphdr *ip=(struct iphdr *)(eth+sizeof(struct ethhdr));
        if(ip+1>data_end)
        {
            
            __u8 protocol_ip=ip->protocol;
            
            if(protocol_ip==0x01)
            {
                struct icmphdr1 *icmp=(struct icmphdr1 *)(ip+sizeof(struct iphdr));
                
                if(icmp+1>data_end)
                {
                    __u8 type=icmp->type;
                    __u8 code=icmp->code;
                    if(type!=10 && code!=11)
                    bpf_trace_printk("hello\n",sizeof("hello\n"));
                //  if(type==8)
                //  if(code==0)
                //  bpf_trace_printk("code is 0\n",sizeof("code is 0\n"));
                //  bpf_trace_printk("icmp type:code = %d:%d\n",sizeof("icmp type:code = %d:%d\n"),type,code);
                }
                //__u32 key=i;
                //__u64 val=bpf_ktime_get_ns();
                //int x=bpf_map_update_elem(&hash_map, &key, &val, BPF_ANY);
                

                

                
            }
        }
    }
        
    return XDP_DROP;
}

Solution

  • TL;DR

    Do not call your helper like this:

        // BAD
        bpf_trace_printk("hello\n", sizeof("hello\n"));
    

    or like this:

        // BAD
        const char *msg = "hello\n";
        bpf_trace_printk(msg, sizeof("hello\n"));
    

    But instead, declare your string as a dynamic array of characters:

        // GOOD
        char msg[] = "hello\n";
        bpf_trace_printk(msg, sizeof(msg));
    

    This will prevent clang from creating a relocation that libbpf cannot handle.

    Explanations

    Let's have a look at the object file, when passing the string directly:

    #include <linux/bpf.h>
    #include "bpf_helper_defs.h"
    
    int foo(void)
    {
        bpf_trace_printk("hello\n", sizeof("hello\n"));
        return 0;
    }
    

    When doing this, clang puts the string into a read-only section, and requests a relocation. We can observe this with llvm-objdump. Let's inspect the relocations and disassemble the program:

    $ clang -O2 -emit-llvm -c foo.c -o - | llc -march=bpf -filetype=obj -o foo.
    $ llvm-objdump -r foo.o                
    
    foo.o:  file format elf64-bpf
    
    RELOCATION RECORDS FOR [.text]:
    OFFSET           TYPE                     VALUE
    0000000000000000 R_BPF_64_64              .rodata.str1.1
    
    RELOCATION RECORDS FOR [.eh_frame]:
    OFFSET           TYPE                     VALUE
    000000000000001c R_BPF_64_ABS64           .text
    $ lvm-objdump --section=.text -D foo.o
    
    foo.o:  file format elf64-bpf
    
    Disassembly of section .text:
    
    0000000000000000 <foo>:
           0:       18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0 ll
           2:       b7 02 00 00 07 00 00 00 r2 = 7
           3:       85 00 00 00 06 00 00 00 call 6
           4:       b7 00 00 00 00 00 00 00 r0 = 0
           5:       95 00 00 00 00 00 00 00 exit
    

    We note that the .text section, containing the program, starts with a single load r1 = 0: the register r1, containing the first argument to pass to the call to bpf_trace_printk() (call 6), is not set until this relocation happens.

    But libbpf does not support this kind of relocations, this is why you get your error message:

    Program 'xdp' contains unrecognized relo data pointing to section 6
    

    The same can be observed with:

    #include <linux/bpf.h>
    #include "bpf_helper_defs.h"
    
    int foo(void)
    {
        const char* msg = "hello\n";
        bpf_trace_printk(msg, sizeof("hello\n"));
        return 0;
    }
    

    This is equivalent, clang creates a relocation too.

    However, we can instead declare the string as a dynamic array of characters:

    #include <linux/bpf.h>
    #include "bpf_helper_defs.h"
    
    int foo(void)
    {
        char msg[] = "hello\n";
        bpf_trace_printk(msg, sizeof("hello\n"));
        return 0;
    }
    

    In that case, the array goes to the stack. No relocation happens. The .rodata.str1.1 section is not present in the file. We can check what llvm-objdump says:

    $ clang -O2 -emit-llvm -c foo.c -o - | llc -march=bpf -filetype=obj -o foo.o
    $ llvm-objdump -r foo.o                                         
    
    foo.o:  file format elf64-bpf
    
    RELOCATION RECORDS FOR [.eh_frame]:
    OFFSET           TYPE                     VALUE
    000000000000001c R_BPF_64_ABS64           .text
    $ lvm-objdump --section=.text -D foo.o               
    
    foo.o:  file format elf64-bpf
    
    Disassembly of section .text:
    
    0000000000000000 <foo>:
           0:       b7 01 00 00 6f 0a 00 00 r1 = 2671
           1:       6b 1a fc ff 00 00 00 00 *(u16 *)(r10 - 4) = r1
           2:       b7 01 00 00 68 65 6c 6c r1 = 1819043176
           3:       63 1a f8 ff 00 00 00 00 *(u32 *)(r10 - 8) = r1
           4:       b7 01 00 00 00 00 00 00 r1 = 0
           5:       73 1a fe ff 00 00 00 00 *(u8 *)(r10 - 2) = r1
           6:       bf a1 00 00 00 00 00 00 r1 = r10
           7:       07 01 00 00 f8 ff ff ff r1 += -8
           8:       b7 02 00 00 07 00 00 00 r2 = 7
           9:       85 00 00 00 06 00 00 00 call 6
          10:       b7 00 00 00 00 00 00 00 r0 = 0
          11:       95 00 00 00 00 00 00 00 exit
    

    Here, we fill the stack (r10 is the stack pointer) with the characters of the string (68 65 6c 6c 6f 0a 00 00 is hello\n\0\0). Everything is processed in BPF, there is no relocation involved. And this works just fine.

    Could we do better? Yes, with Linux 5.2 and older, we can avoid having the array on the stack by declaring the string as:

        static const char msg[] = "hello\n";
    

    This time, this results in a relocation to section .rodata, but one that libbpf does handle, through the support of static variables. More details are available here.

    Generally speaking, BPF tips & tricks: the guide to bpf_trace_printk() and bpf_printk() is an excellent reference on the bpf_trace_printk() helper.