Skip to content

Commit

Permalink
btf: correct generation for extern funcs [PR106773]
Browse files Browse the repository at this point in the history
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 (struct btf_var_secinfo): Update comments with notes about
	extern functions.
  • Loading branch information
dafaust authored and ouuleilei-bot committed Dec 7, 2022
1 parent 925d711 commit 0b3d88c
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 46 deletions.
129 changes: 87 additions & 42 deletions gcc/btfout.cc
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,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
Expand All @@ -305,7 +333,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_LINKAGE_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)
{
Expand All @@ -317,28 +398,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_LINKAGE_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;

Expand All @@ -363,6 +429,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
Expand Down Expand Up @@ -461,29 +529,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);
}

Expand Down
28 changes: 28 additions & 0 deletions gcc/testsuite/gcc.dg/debug/btf/btf-datasec-2.c
Original file line number Diff line number Diff line change
@@ -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));
}
19 changes: 19 additions & 0 deletions gcc/testsuite/gcc.dg/debug/btf/btf-function-6.c
Original file line number Diff line number Diff line change
@@ -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;
}
9 changes: 5 additions & 4 deletions include/btf.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,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,
Expand Down

0 comments on commit 0b3d88c

Please sign in to comment.