Page MenuHomePhabricator

BPF: add extern func to data sections if specified
Needs ReviewPublic

Authored by yonghong-song on Dec 18 2020, 1:30 PM.

Details

Summary

This permits extern function (BTF_KIND_FUNC) be added
to BTF_KIND_DATASEC if a section name is specified.
For example,

-bash-4.4$ cat t.c 
void foo(int) __attribute__((section(".kernel.funcs")));
int test(void) {
  foo(5);
  return 0;
}

The extern function foo (BTF_KIND_FUNC) will be put into
BTF_KIND_DATASEC with name ".kernel.funcs".

This will help to differentiate two kinds of external functions,
functions in kernel and functions defined in other bpf programs.

Diff Detail

Event Timeline

yonghong-song created this revision.Dec 18 2020, 1:30 PM
yonghong-song requested review of this revision.Dec 18 2020, 1:30 PM
Herald added a project: Restricted Project. · View Herald TranscriptDec 18 2020, 1:30 PM

@iamkafai currently the var type points to a BTF_KIND_FUNC, if you feel this is a little bit redundant, I can change to point to BTF_KIND_FUNC_PROTO.

ast edited reviewers, added: anakryiko; removed: andriigrynenko.Dec 18 2020, 5:18 PM

Does it work already without specifying the section, right?
void foo1(int); works ?
void foo2(int) __ attribute __((section("bar")));
sort-of works, but this one goes into the same section as foo1 ?

If foo1 doesn't work, then "if (F->hasSection())" is incorrect. Both of the above should produce some BTF.
Assuming, of course, that both foo1 and foo2 are used in the code and there are relocations against them.

For:
void foo1(int); works ?
void foo2(int) __ attribute __((section("bar"))); sort-of works, but this one goes into the same section as foo1 ?

Current llvm without this patch, the following are generated BTFs:

BTF_KIND_FUNC (foo1) -> BTF_KIND_FUNC_PROTO (foo1)
BTF_KIND_FUNC (foo2) -> BTF_KIND_FUNC_PROTO (foo2)

there are no data sections generated.

With this patch, the following BTFs are generated:

BTF_KIND_FUNC (foo1) -> BTF_KIND_FUNC_PROTO (foo1)
BTF_KIND_FUNC (foo2) -> BTF_KIND_FUNC_PROTO (foo2)
BTF_KIND_VAR(foo2) -> BTF_KIND_FUNC(foo2)
BTF_KIND_DATASEC(bar) -> BTF_KIND_VAR(foo2)

The above is not perfect either.
extern functions are actually extern variables, ideally we should treat them the same as
extern variables, so we probably should not generate BTF_KIND_FUNC() in the above,
we should generate

BTF_KIND_VAR(foo1) -> BTF_KIND_FUNC_PROTO(foo1)
BTF_KIND_VAR(foo2) -> BTF_KIND_FUNC_PROTO(foo2)
BTF_KIND_DATASEC(.extern) -> BTF_KIND_VAR(foo1)
BTF_KIND_DATASEC(bar) -> BTF_KIND_VAR(foo2)

So we do not generate BTF_KIND_FUNC for extern functions.

But unfortunately, we have the following in uapi,
enum btf_func_linkage {

BTF_FUNC_STATIC = 0,
BTF_FUNC_GLOBAL = 1,
BTF_FUNC_EXTERN = 2,

};

But BTF_FUNC_EXTERN is not used yet, maybe we can remove this
and BTF_KIND_VAR to represent external function instead of
using BTF_KIND_FUNC? This will make things a little bit
easier to unify with section handling. Or alternatively we can
have BTF_KIND_VAR points to BTF_KIND_FUNC but it feels
a little bit redundant.

The only issue is consistency w.r.t. non-extern functions.
Currently we don't put them into any BTF_KIND_VAR.

ast added a comment.Dec 19 2020, 9:24 AM

why the current behavior is not enough?
BTF_KIND_FUNC (foo1) -> BTF_KIND_FUNC_PROTO (foo1)
BTF_KIND_FUNC (foo2) -> BTF_KIND_FUNC_PROTO (foo2)
libbpf can use order to find proper function. foo can be extern as it is right now. Then libbpf will first try to match between multiple elf bpf files. If still not found it will search in vmlinux/mods.
I think using KIND_VAR is incorrect. There is no variable here.

yonghong-song added a comment.EditedDec 19 2020, 10:29 AM

libbpf can use order to find proper function. foo can be extern as it is right now. Then libbpf will first try to match between multiple elf bpf files. If still not found it will search in vmlinux/mods.

This is something Martin and I discussed try to avoid. We want code level indication whether it is an extern func from bpf program or from kernel. We could do what you suggested here, but feel this way we may misinterpret user intentions. For example,

  • there exist a kernel function 'foo'.
  • library1 (v1): no extern functions
  • library1 (v2): provide an extern function 'foo'

bpf program links with v1 will use kernel 'foo'.
bpf program links with v2 will use library 'foo'.

But this may be a really really corner case and libbpf could warn user where extern func exists a match from both library and kernel.

I think using KIND_VAR is incorrect. There is no variable here.

In current BTF design, yes, KIND_VAR does not refer to func pointers. In LLVM IR, func_proto actually referred as a type. I agree that KIND_VAR referring to functions not good, and referring to extern func only (not other functions) seems not consistent and a little bit awkward.

Overall, I am okay not using sections (sections actually will be ignored), and libbpf does priority matching against bpf library first and then kernel functions.

Having an explicit special section for extern functions clearly indicating the intent (that it should match kernel function) is really good. Especially with static linking. Otherwise, this "try local function, otherwise fallback to kernel function" is going to be really painful for users if there is any error, because all such errors will be indicated at the load time instead of at the link time, where they should be detected and reported.

I do agree, though, that using VAR for "attaching" FUNC to datasec looks weird. We could extend DATASEC to list both VARs and FUNCs, of course. If ELF recorded section index for such externs that would solve the problem without any BTF extensions, because libbpf would be detecting what's the intent with extern func from ELF and then looking up function details in BTF, but I don't think that's the case with extern functions, just as it wasn't the case with extern variables.

Right, put BTF_KIND_FUNC in DATASEC is another option. In the beginning, we only need to put extern function there (with size of 0) for this use case.

yonghong-song edited the summary of this revision. (Show Details)

put BTF_KIND_FUNC directly to datasect instead of BTF_KIND_VAR(BTF_KIND_FUNC)