[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
@@ -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);
}
new file mode 100644
@@ -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));
+}
new file mode 100644
@@ -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;
+}
@@ -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,