[cfe-dev] support __attribute__((weak)) for non-primitive types

Aaron Ballman via cfe-dev cfe-dev at lists.llvm.org
Wed Aug 25 05:34:17 PDT 2021


On Tue, Aug 24, 2021 at 1:18 AM Y Song <ys114321 at gmail.com> wrote:
>
> Hi,
>
> This is a BPF use case. We would like to check whether
> __attribute__((weak)) support for non-primitive types would be
> possible in clang or not. For example, if we have two types like
>    struct t { int a; } __attribute__((weak));
>    struct t { int a; };
>
>    typedef unsigned __u32;
>    typedef unsigned __u32 __attribute__((weak));
> the compilation should not fail. It should just ignore weak definitions.
>
> In BPF, to support CO-RE (compile once and run everywhere), we
> generate a *generic* vmlinux.h
> (https://facebookmicrosites.github.io/bpf/blog/2020/02/19/bpf-portability-and-co-re.html)
> which is included in the bpf program. The bpf loader (libbpf) will
> adjust field offsets properly based on host record layout so the same
> program can run on different kernels which may have different record
> layouts.
>
> But vmlinux.h does not support macros (and simple static inline helper
> functions in kernel header files). Currently, users need to define
> these macros and static inline functions explicitly in their bpf
> program. What we are thinking is to permit users to include kernel
> header files containing these macros/static inline functions. But this
> may introduce duplicated types as these types have been defined in
> vmlinux.h.
>
> For example, we may have:
>     socket.h:
>         struct socket { ... };
>         #define TCP    6
>     vmlinux.h:
>         struct socket { ... };
>     bpf.c:
>         #include <socket.h>
>         #include <vmlinux.h>
>         ... TCP ...
>
> In this case, struct "socket" is defined twice. If we can have something like
>     bpf.c:
>       #pragma clang attribute push (__attribute__((weak)), apply_to = record)
>       #include <socket.h>
>       #pragma clang attribute pop
>       #include <vmlinux.h>
>         ... TCP ...
>
>
> Then bpf program can get header file macro definitions without copying
> explicitly.
>
> We need support for "apply_to = typedef" in the above pragma, so we
> can deal with typedef types as well.
>
> Another issue is related to what if two types are not compatible. For example,
>     struct t { int a; } __attribute__((weak));
>     struct t { int a; int b; };
> I think we could have a flag to control whether we allow incompatible
> weak type or not.

What's the use case for allowing incompatible weak types? Having
multiple distinct types seems like it will make for confusion. e.g.,

struct foo { int a; };
struct __attribute__((whatever_we_name_it)) foo { float f; };

int func(struct foo *f) {
  return f->f + f->a;
}

Which lookup causes the error (or do they succeed somehow)?

> It would be good if I can get some suggestions/directions on whether
> such a feature is possible or not for clang frontend.

What happens if the only definition seen in a TU is marked as being weak?

In terms of incompatible weak types, are these incompatible?

struct base {
  int a;
};

struct foo : base {
  int b;
};

struct __attribute__((whatever_we_name_it)) foo {
  int a, b;
}

I'm not comfortable with the idea of having two different type
definitions in the same TU unless it's clear and consistent which one
"wins". If the type definitions are the same between the types, then I
don't see a problem because that limits the chances for surprises.
When the type definitions differ, it becomes harder to reason about
the way lookup works, how the type is laid out at runtime, what the
ABI implications are, etc. Is there a consistent rule for which type
is the one to be honored (e.g., only one definition is allowed to
elide the attribute and that's the canonical definition of the type
which always "wins")?

~Aaron

>
> Thanks,
>
> Yonghong


More information about the cfe-dev mailing list