32-bit pointers, ARM and BPF

Hi,

We're trying to use BPF on a 32-bit ARM. However, we have issues with
llvm.bpf.pseudo.

Let's consider a simple program (t.c):

   #include <uapi/linux/bpf.h>

   typedef unsigned long long u64;

   u64 bpf_pseudo_fd(u64, u64) asm("llvm.bpf.pseudo");

   static void *(*bpf_map_lookup_elem)(void *map, void *key) =
     (void *) BPF_FUNC_map_lookup_elem;

   int test_ok(void *ctx) {
                          // 0s below are for example
     void *v = bpf_map_lookup_elem(bpf_pseudo_fd(0,0), 0);
     return 0;
   }

We put it into
clang -O2 -target armv7l-unknown-linux-gnueabi -emit-llvm -S t.c

to get

   define i32 @test_ok(i8* nocapture readnone) local_unnamed_addr #0 {
     %2 = tail call i64 @llvm.bpf.pseudo(i64 0, i64 0)
     %3 = trunc i64 %2 to i32
     %4 = inttoptr i32 %3 to i8*
     %5 = tail call i8* inttoptr (i32 1 to i8* (i8*, i8*)*)(i8* %4, i8*
null) #1
     ret i32 0
   }

Note the 'trunc' instruction, which is there, because return type of
llvm.bpf.pseudo is i64, and it is converted to 32-bit pointer.

Now, after
llc -march=bpf t.ll

we get
   test_ok: # @test_ok
  ld_pseudo r1, 0, 0
  r1 <<= 32
  r1 >>= 32
  r2 = 0
  call 1
  r0 = 0
  exit

The return value of llvm.bpf.pseudo (u64) is truncated by shift-left +
shift-right. After the operation BPF verifier does not consider the
value in r1 as pointer anymore, and complains when it is passed as a
pointer to bpf_map_lookup_elem (call 1).

We have changed the return type of llvm.bpf.pseudo to pointer for our
purposes (see below), but what is the "correct" way of handling it?

include/llvm/IR/IntrinsicsBPF.td:
      def int_bpf_pseudo : GCCBuiltin<"__builtin_bpf_pseudo">,
   - Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_i64_ty]>;
   + Intrinsic<[llvm_ptr_ty], [llvm_i64_ty, llvm_i64_ty]>;

Thanks,
Adrian

I think your clang command line should have “-target bpf”. It needs to know you’re targeting bpf instructions not ARM instructions.