One of the big reasons is that if you're using kernel headers and internal structures, like with kprobes, they're unstable APIs, and the struct offsets may change between kernel versions.
You can compile for each kernel type you're going to use, but that is more effort and foresight. There's also the rewriting of map file descriptors in the bytecode itself on load time, but libbpf generally handles this.