dwarf_loader: Handle subprogram ret type with abstract_origin properly

With latest bpf-next built with clang LTO (thin or full), I hit one test
failures:

  $ ./test_progs -t tcp
  ...
  libbpf: extern (func ksym) 'tcp_slow_start': func_proto [23] incompatible with kernel [115303]
  libbpf: failed to load object 'bpf_cubic'
  libbpf: failed to load BPF skeleton 'bpf_cubic': -22
  test_cubic:FAIL:bpf_cubic__open_and_load failed
  #9/2 cubic:FAIL
  ...

The reason of the failure is due to bpf program 'tcp_slow_start' func
signature is different from vmlinux BTF. bpf program uses the following
signature:

  extern __u32 tcp_slow_start(struct tcp_sock *tp, __u32 acked);

which is identical to the kernel definition in linux:include/net/tcp.h:

  u32 tcp_slow_start(struct tcp_sock *tp, u32 acked);

While vmlinux BTF definition like:

  [115303] FUNC_PROTO '(anon)' ret_type_id=0 vlen=2
          'tp' type_id=39373
          'acked' type_id=18
  [115304] FUNC 'tcp_slow_start' type_id=115303 linkage=static

The above is dumped with `bpftool btf dump file vmlinux`.

You can see the ret_type_id is 0 and this caused the problem.

Looking at dwarf, we have:

0x11f2ec67:   DW_TAG_subprogram
                DW_AT_low_pc    (0xffffffff81ed2330)
                DW_AT_high_pc   (0xffffffff81ed235c)
                DW_AT_frame_base        ()
                DW_AT_GNU_all_call_sites        (true)
                DW_AT_abstract_origin   (0x11f2ed66 "tcp_slow_start")
...
0x11f2ed66:   DW_TAG_subprogram
                DW_AT_name      ("tcp_slow_start")
                DW_AT_decl_file ("/home/yhs/work/bpf-next/net/ipv4/tcp_cong.c")
                DW_AT_decl_line (392)
                DW_AT_prototyped        (true)
                DW_AT_type      (0x11f130c2 "u32")
                DW_AT_external  (true)
                DW_AT_inline    (DW_INL_inlined)

We have a subprogram which has an abstract_origin pointing to the
subprogram prototype with return type. Current one pass recoding cannot
easily resolve this easily since at the time recoding for 0x11f2ec67,
the return type in 0x11f2ed66 has not been resolved.

To simplify implementation, I just added another pass to go through all
functions after recoding pass. This should resolve the above issue.

With this patch, among total 250999 functions in vmlinux, 4821 functions
needs return type adjustment from type id 0 to correct values. The above
failed bpf selftest passed too.

Committer testing:

Before:

  $ pfunct tcp_slow_start
  void tcp_slow_start(struct tcp_sock * tp, u32 acked);
  $
  $ pfunct --prototypes /sys/kernel/btf/vmlinux > before
  $ head before
  int fb_is_primary_device(struct fb_info * info);
  int arch_resume_nosmt(void);
  int relocate_restore_code(void);
  int arch_hibernation_header_restore(void * addr);
  int get_e820_md5(struct e820_table * table, void * buf);
  int arch_hibernation_header_save(void * addr, unsigned int max_size);
  int pfn_is_nosave(long unsigned int pfn);
  int swsusp_arch_resume(void);
  int amd_bus_cpu_online(unsigned int cpu);
  void pci_enable_pci_io_ecs(void);
  $

After:

  $ pfunct -F btf ../build/bpf_clang_thin_lto/vmlinux -f tcp_slow_start
  u32 tcp_slow_start(struct tcp_sock * tp, u32 acked);
  $
  $ pfunct -F btf --prototypes ../build/bpf_clang_thin_lto/vmlinux > after
  $
  $ head after
  int fb_is_primary_device(struct fb_info * info);
  int arch_resume_nosmt(void);
  int relocate_restore_code(void);
  int arch_hibernation_header_restore(void * addr);
  int get_e820_md5(struct e820_table * table, void * buf);
  int arch_hibernation_header_save(void * addr, unsigned int max_size);
  int pfn_is_nosave(long unsigned int pfn);
  int swsusp_arch_resume(void);
  int amd_bus_cpu_online(unsigned int cpu);
  void pci_enable_pci_io_ecs(void);
  $
  $ diff -u before after | grep ^+ | wc -l
  1604
  $

  $ diff -u before after | grep tcp_slow_start
  -void tcp_slow_start(struct tcp_sock * tp, u32 acked);
  +u32 tcp_slow_start(struct tcp_sock * tp, u32 acked);
  $
  $ diff -u before after | grep ^[+-] | head
  --- before	2021-04-02 11:35:15.578160795 -0300
  +++ after	2021-04-02 11:33:34.204847317 -0300
  -void set_bf_sort(const struct dmi_system_id  * d);
  +int set_bf_sort(const struct dmi_system_id  * d);
  -void raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 val);
  -void raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 * val);
  +int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 val);
  +int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 * val);
  -void xen_find_device_domain_owner(struct pci_dev * dev);
  +int xen_find_device_domain_owner(struct pci_dev * dev);
  $

The same results are obtained if using /sys/kernel/btf/vmlinux after
rebooting with the kernel built from the ../build/bpf_clang_thin_lto/vmlinux
file used in the above 'after' examples.

Signed-off-by: Yonghong Song <yhs@fb.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Acked-by: David Blaikie <dblaikie@gmail.com>
Link: https://lore.kernel.org/bpf/82dfd420-96f9-aedc-6cdc-bf20042455db@fb.com/
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Bill Wendling <morbo@google.com>
Cc: Nick Desaulniers <ndesaulniers@google.com>
Cc: bpf@vger.kernel.org
Cc: dwarves@vger.kernel.org
Cc: kernel-team@fb.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Yonghong Song 2021-04-01 16:55:34 -07:00 committed by Arnaldo Carvalho de Melo
parent 5752d1951d
commit 9adb014930
1 changed files with 43 additions and 1 deletions

View File

@ -2198,6 +2198,34 @@ out:
return 0;
}
static int cu__resolve_func_ret_types(struct cu *cu)
{
struct ptr_table *pt = &cu->functions_table;
uint32_t i;
for (i = 0; i < pt->nr_entries; ++i) {
struct tag *tag = pt->entries[i];
if (tag == NULL || tag->type != 0)
continue;
struct function *fn = tag__function(tag);
if (!fn->abstract_origin)
continue;
struct dwarf_tag *dtag = tag->priv;
struct dwarf_tag *dfunc;
dfunc = dwarf_cu__find_tag_by_ref(cu->priv, &dtag->abstract_origin);
if (dfunc == NULL) {
tag__print_abstract_origin_not_found(tag);
return -1;
}
tag->type = dfunc->tag->type;
}
return 0;
}
static int cu__recode_dwarf_types_table(struct cu *cu,
struct ptr_table *pt,
uint32_t i)
@ -2295,7 +2323,11 @@ static int die__process_and_recode(Dwarf_Die *die, struct cu *cu)
int ret = die__process(die, cu);
if (ret != 0)
return ret;
return cu__recode_dwarf_types(cu);
ret = cu__recode_dwarf_types(cu);
if (ret != 0)
return ret;
return cu__resolve_func_ret_types(cu);
}
static int class_member__cache_byte_size(struct tag *tag, struct cu *cu,
@ -2637,6 +2669,16 @@ static int cus__merge_and_process_cu(struct cus *cus, struct conf_load *conf,
/* process merged cu */
if (cu__recode_dwarf_types(cu) != LSK__KEEPIT)
return DWARF_CB_ABORT;
/*
* for lto build, the function return type may not be
* resolved due to the return type of a subprogram is
* encoded in another subprogram through abstract_origin
* tag. Let us visit all subprograms again to resolve this.
*/
if (cu__resolve_func_ret_types(cu) != LSK__KEEPIT)
return DWARF_CB_ABORT;
if (finalize_cu_immediately(cus, cu, dcu, conf)
== LSK__STOP_LOADING)
return DWARF_CB_ABORT;