[Changes from v1: - Add enum btf_func_linkage to include/btf.h and use it. - Minor updates to comments based on review. ]
The eBPF loader expects to find entries for functions declared as extern in the corresponding BTF_KIND_DATASEC record, but we were not generating these entries. This patch adds support for the 'extern' linkage of function types in BTF, and creates entries for for them BTF_KIND_DATASEC records as needed. PR target/106773 gcc/ * btfout.cc (get_section_name): New function. (btf_collect_datasec): Use it here. Process functions, marking them 'extern' and generating DATASEC entries for them as appropriate. Move creation of BTF_KIND_FUNC records to here... (btf_dtd_emit_preprocess_cb): ... from here. gcc/testsuite/ * gcc.dg/debug/btf/btf-datasec-2.c: New test. * gcc.dg/debug/btf/btf-function-6.c: New test. include/ * btf.h (enum btf_func_linkage): New. (struct btf_var_secinfo): Update comments with notes about extern functions. --- gcc/btfout.cc | 129 ++++++++++++------ .../gcc.dg/debug/btf/btf-datasec-2.c | 28 ++++ .../gcc.dg/debug/btf/btf-function-6.c | 19 +++ include/btf.h | 18 ++- 4 files changed, 148 insertions(+), 46 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-datasec-2.c create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-function-6.c diff --git a/gcc/btfout.cc b/gcc/btfout.cc index 204b11d4e9f..a423fabc0b5 100644 --- a/gcc/btfout.cc +++ b/gcc/btfout.cc @@ -290,7 +290,35 @@ btf_datasec_push_entry (ctf_container_ref ctfc, const char *secname, ds.entries.safe_push (info); datasecs.safe_push (ds); - num_types_created++; +} + + +/* Return the section name, as of interest to btf_collect_datasec, for the + given symtab node. Note that this deliberately returns NULL for objects + which do not go in a section btf_collect_datasec cares about. */ +static const char * +get_section_name (symtab_node *node) +{ + const char *section_name = node->get_section (); + + if (section_name == NULL) + { + switch (categorize_decl_for_section (node->decl, 0)) + { + case SECCAT_BSS: + section_name = ".bss"; + break; + case SECCAT_DATA: + section_name = ".data"; + break; + case SECCAT_RODATA: + section_name = ".rodata"; + break; + default:; + } + } + + return section_name; } /* Construct all BTF_KIND_DATASEC records for CTFC. One such record is created @@ -301,7 +329,60 @@ btf_datasec_push_entry (ctf_container_ref ctfc, const char *secname, static void btf_collect_datasec (ctf_container_ref ctfc) { - /* See cgraph.h struct symtab_node, which varpool_node extends. */ + cgraph_node *func; + FOR_EACH_FUNCTION (func) + { + dw_die_ref die = lookup_decl_die (func->decl); + if (die == NULL) + continue; + + ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, die); + if (dtd == NULL) + continue; + + /* Functions actually get two types: a BTF_KIND_FUNC_PROTO, and + also a BTF_KIND_FUNC. But the CTF container only allocates one + type per function, which matches closely with BTF_KIND_FUNC_PROTO. + For each such function, also allocate a BTF_KIND_FUNC entry. + These will be output later. */ + ctf_dtdef_ref func_dtd = ggc_cleared_alloc<ctf_dtdef_t> (); + func_dtd->dtd_data = dtd->dtd_data; + func_dtd->dtd_data.ctti_type = dtd->dtd_type; + func_dtd->linkage = dtd->linkage; + func_dtd->dtd_type = num_types_added + num_types_created; + + /* Only the BTF_KIND_FUNC type actually references the name. The + BTF_KIND_FUNC_PROTO is always anonymous. */ + dtd->dtd_data.ctti_name = 0; + + vec_safe_push (funcs, func_dtd); + num_types_created++; + + /* Mark any 'extern' funcs and add DATASEC entries for them. */ + if (DECL_EXTERNAL (func->decl)) + { + func_dtd->linkage = BTF_FUNC_EXTERN; + + const char *section_name = get_section_name (func); + /* Note: get_section_name () returns NULL for functions in text + section. This is intentional, since we do not want to generate + DATASEC entries for them. */ + if (section_name == NULL) + continue; + + struct btf_var_secinfo info; + + /* +1 for the sentinel type not in the types map. */ + info.type = func_dtd->dtd_type + 1; + + /* Both zero at compile time. */ + info.size = 0; + info.offset = 0; + + btf_datasec_push_entry (ctfc, section_name, info); + } + } + varpool_node *node; FOR_EACH_VARIABLE (node) { @@ -313,28 +394,13 @@ btf_collect_datasec (ctf_container_ref ctfc) if (dvd == NULL) continue; - const char *section_name = node->get_section (); /* Mark extern variables. */ if (DECL_EXTERNAL (node->decl)) dvd->dvd_visibility = BTF_VAR_GLOBAL_EXTERN; + const char *section_name = get_section_name (node); if (section_name == NULL) - { - switch (categorize_decl_for_section (node->decl, 0)) - { - case SECCAT_BSS: - section_name = ".bss"; - break; - case SECCAT_DATA: - section_name = ".data"; - break; - case SECCAT_RODATA: - section_name = ".rodata"; - break; - default: - continue; - } - } + continue; struct btf_var_secinfo info; @@ -359,6 +425,8 @@ btf_collect_datasec (ctf_container_ref ctfc) btf_datasec_push_entry (ctfc, section_name, info); } + + num_types_created += datasecs.length (); } /* Return true if the type ID is that of a type which will not be emitted (for @@ -463,29 +531,6 @@ btf_dtd_emit_preprocess_cb (ctf_container_ref ctfc, ctf_dtdef_ref dtd) if (!btf_emit_id_p (dtd->dtd_type)) return; - uint32_t btf_kind - = get_btf_kind (CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info)); - - if (btf_kind == BTF_KIND_FUNC_PROTO) - { - /* Functions actually get two types: a BTF_KIND_FUNC_PROTO, and - also a BTF_KIND_FUNC. But the CTF container only allocates one - type per function, which matches closely with BTF_KIND_FUNC_PROTO. - For each such function, also allocate a BTF_KIND_FUNC entry. - These will be output later. */ - ctf_dtdef_ref func_dtd = ggc_cleared_alloc<ctf_dtdef_t> (); - func_dtd->dtd_data = dtd->dtd_data; - func_dtd->dtd_data.ctti_type = dtd->dtd_type; - func_dtd->linkage = dtd->linkage; - - vec_safe_push (funcs, func_dtd); - num_types_created++; - - /* Only the BTF_KIND_FUNC type actually references the name. The - BTF_KIND_FUNC_PROTO is always anonymous. */ - dtd->dtd_data.ctti_name = 0; - } - ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (dtd); } diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-datasec-2.c b/gcc/testsuite/gcc.dg/debug/btf/btf-datasec-2.c new file mode 100644 index 00000000000..f4b298cf019 --- /dev/null +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-datasec-2.c @@ -0,0 +1,28 @@ +/* Test BTF generation of DATASEC records for extern functions. + + Only functions declared extern should have entries in DATASEC records. */ + +/* { dg-do compile } */ +/* { dg-options "-O0 -gbtf -dA" } */ + +/* Expect one DATASEC with vlen=1 (.foo_sec) and one with vlen=2 (.bar_sec) */ +/* { dg-final { scan-assembler-times "0xf000002\[\t \]+\[^\n\]*btt_info" 1 } } */ +/* { dg-final { scan-assembler-times "0xf000001\[\t \]+\[^\n\]*btt_info" 1 } } */ + +/* Function entries should have offset and size of 0 at compile time. */ +/* { dg-final { scan-assembler-times "0\[\t \]+\[^\n\]*bts_offset" 3 } } */ +/* { dg-final { scan-assembler-times "0\[\t \]+\[^\n\]*bts_size" 3 } } */ + +extern int foo (int a) __attribute__((section(".foo_sec"))); + + +extern int bar (int b) __attribute__((section(".bar_sec"))); +extern void chacha (void) __attribute__((section(".bar_sec"))); + +__attribute__((section(".foo_sec"))) +void baz (int *x) +{ + chacha (); + + *x = foo (bar (*x)); +} diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-function-6.c b/gcc/testsuite/gcc.dg/debug/btf/btf-function-6.c new file mode 100644 index 00000000000..48a946ab14b --- /dev/null +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-function-6.c @@ -0,0 +1,19 @@ +/* Test BTF extern linkage for functions. + + We expect to see one BTF_KIND_FUNC type with global linkage (foo), and + one BTF_KIND_FUNC type with extern linkage (extfunc). */ + +/* { dg-do compile } */ +/* { dg-options "-O0 -gbtf -dA" } */ + +/* { dg-final { scan-assembler-times "btt_info: kind=12, kflag=0, linkage=2" 1 } } */ +/* { dg-final { scan-assembler-times "btt_info: kind=12, kflag=0, linkage=1" 1 } } */ + +extern int extfunc(int a, int b); + +int foo (int x) { + + int y = extfunc (x, x+1); + + return y; +} diff --git a/include/btf.h b/include/btf.h index da621353aa1..2f8f6f68b27 100644 --- a/include/btf.h +++ b/include/btf.h @@ -178,6 +178,15 @@ struct btf_param uint32_t type; /* Type of parameter. */ }; +/* BTF_KIND_FUNC records encode linkage information in the VLEN bits + of the type record. These are the supported values. */ +enum btf_func_linkage +{ + BTF_FUNC_STATIC = 0, + BTF_FUNC_GLOBAL = 1, + BTF_FUNC_EXTERN = 2, +}; + /* BTF_KIND_VAR records encode linkage information in a single trailing struct btf_var. These are the supported values. */ enum btf_var_linkage @@ -195,12 +204,13 @@ struct btf_var }; /* BTF_KIND_DATASEC is followed by VLEN struct btf_var_secinfo entries, - which describe all BTF_KIND_VAR types contained in the section. */ + which describe all BTF_KIND_VAR or extern BTF_KIND_FUNC types contained + in the section. */ struct btf_var_secinfo { - uint32_t type; /* Type of variable. */ - uint32_t offset; /* In-section offset of variable (in bytes). */ - uint32_t size; /* Size (in bytes) of variable. */ + uint32_t type; /* Type of BTF_KIND_VAR or BTF_KIND_FUNC item. */ + uint32_t offset; /* In-section offset (in bytes) of item. */ + uint32_t size; /* Size (in bytes) of item. */ }; /* BTF_KIND_ENUM64 is followed by VLEN struct btf_enum64 entries, -- 2.38.1