0

我正在尝试在我的网络驱动程序上安装 XDP 程序,但我收到错误ELF 在条目 0 中包含非 {map,call} 相关的 relo 数据,指向第 4 节!编译器错误?!获取程序/地图时出错!

我试图运行的代码:

#define KBUILD_MODNAME "filter"
#include <arpa/inet.h>
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/udp.h>

static int (*bpf_trace_printk)(const char *fmt,...) = (void *)BPF_FUNC_trace_printk;

int udpfilter(struct xdp_md *ctx) {
    bpf_trace_printk("got a packet\n");
    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;
    struct ethhdr *eth = data;
    if ((void*)eth + sizeof(*eth) <= data_end) {
       struct iphdr *ip = data + sizeof(*eth);
       if ((void*)ip + sizeof(*ip) <= data_end) {
         if (ip->protocol == IPPROTO_UDP) {
            struct udphdr *udp = (void*)ip + sizeof(*ip);
            if ((void*)udp + sizeof(*udp) <= data_end) {
               if (udp->dest == ntohs(7999)) {
                  bpf_trace_printk("udp port 7999\n");
                  udp->dest = ntohs(7998);
              }
            }
          }
        }
      }
   return XDP_PASS;
   }

编译命令:clang -O2 -g -Wall -target bpf -c filter.c -o filter.o就可以了!

和我用来安装的命令:ip link set enp0s3 xdpgeneric obj filter.o然后我得到上述错误。

我不确定这条消息应该是什么意思,我错过了什么吗?

4

1 回答 1

0

获取程序/地图时出错!

您不必在编译时包含其他目录中提到的此目录。此错误是由于ip未在 ELF 文件中找到预期的部分。请参阅ip-link 手册页

如果未section传递任何选项,则将假定默认节名称(“prog”),否则将使用提供的节名称。

所以你有两个解决方案:

  1. 将您的函数放在progELF 部分中:

    __attribute__((section("prog"), used))
    int udpfilter(struct xdp_md *ctx) {
            [...]
    }
    
  2. section加载程序时使用该选项。默认情况下,clang 将您的代码放入以下.text部分:

    # ip link set enp0s3 xdpgeneric obj filter.o sec .text
    

ELF 在指向第 4 节的条目 0 中包含与 {map,call} 无关的 relo 数据!编译器错误?!

如前所述,这是由您使用bpf_trace_prink(). 您不能简单地将格式字符串直接传递给帮助程序,因为 clang 会将其放置在特定的 ELF 部分并标记为重定位数据,但加载程序(ip在这种情况下)将不知道如何处理它。当然,您必须至少传递第二个参数,以提供格式字符串的大小。有关详细信息,请参阅其文档

请注意,我们经常可以看到为该助手定义的包装器:

#define bpf_printk(fmt, ...)                    \
({                                              \
    char ____fmt[] = fmt;                       \
    bpf_trace_printk(____fmt, sizeof(____fmt),  \
             ##__VA_ARGS__);                    \
})

该字符串以不会触发任何重定位的方式声明,之后使用起来更方便,您只需执行bpf_printk("got a packet\n");.

于 2021-07-05T09:00:01.973 回答