https://gcc.gnu.org/g:bf3a264121fe3ba1ed69eda7a11951d10e8c4ae5
commit r16-7846-gbf3a264121fe3ba1ed69eda7a11951d10e8c4ae5 Author: Cupertino Miranda <[email protected]> Date: Wed Feb 25 18:04:56 2026 +0000 bpf: add line_info support to BTF.ext section This patch adds line_info debug information support to .BTF.ext sections. Line info information is used by the BPF verifier to improve error reporting and give more precise source code referenced errors. gcc/ChangeLog: PR target/113453 * config/bpf/bpf-protos.h (bpf_output_call): Change prototype. * config/bpf/bpf.cc (bpf_output_call): Change to adapt operands and return the instruction template instead of immediately emit asm and not allow proper final expected execution flow. (bpf_output_line_info): Add function to introduce line info entries in respective structures (bpf_asm_out_unwind_emit): Add function as hook to TARGET_ASM_UNWIND_EMIT. This hook is called before any instruction is emitted. * config/bpf/bpf.md: Change calls to bpf_output_call. * config/bpf/btfext-out.cc (struct btf_ext_lineinfo): Add fields to struct. (bpf_create_lineinfo, btf_add_line_info_for): Add support function to insert line_info data in respective structures. (output_btfext_line_info): Function to emit line_info data in .BTF.ext section. (btf_ext_output): Call output_btfext_line_info. * config/bpf/btfext-out.h: Add prototype for btf_add_line_info_for. gcc/testsuite/ChangeLog: PR target/113453 * gcc.target/bpf/btfext-funcinfo.c: Adapt test. * gcc.target/bpf/btfext-lineinfo.c: New test. Diff: --- gcc/config/bpf/bpf-protos.h | 2 +- gcc/config/bpf/bpf.cc | 105 +++++++++++++++++++---- gcc/config/bpf/bpf.md | 4 +- gcc/config/bpf/btfext-out.cc | 110 ++++++++++++++++++++++++- gcc/config/bpf/btfext-out.h | 4 + gcc/testsuite/gcc.target/bpf/btfext-funcinfo.c | 3 +- gcc/testsuite/gcc.target/bpf/btfext-lineinfo.c | 51 ++++++++++++ 7 files changed, 258 insertions(+), 21 deletions(-) diff --git a/gcc/config/bpf/bpf-protos.h b/gcc/config/bpf/bpf-protos.h index 4a4799e9bf22..1113cc3e6d17 100644 --- a/gcc/config/bpf/bpf-protos.h +++ b/gcc/config/bpf/bpf-protos.h @@ -23,7 +23,7 @@ along with GCC; see the file COPYING3. If not see /* Routines implemented in bpf.cc. */ extern HOST_WIDE_INT bpf_initial_elimination_offset (int, int); -extern const char *bpf_output_call (rtx); +extern const char *bpf_output_call (const char *templ, rtx *, int target_index); extern void bpf_target_macros (cpp_reader *); extern void bpf_print_operand (FILE *, rtx, int); extern void bpf_print_operand_address (FILE *, rtx); diff --git a/gcc/config/bpf/bpf.cc b/gcc/config/bpf/bpf.cc index d91013ad140b..4773d789d8e3 100644 --- a/gcc/config/bpf/bpf.cc +++ b/gcc/config/bpf/bpf.cc @@ -745,14 +745,12 @@ bpf_output_destructor (rtx symbol, int priority ATTRIBUTE_UNUSED) bpf.md. */ const char * -bpf_output_call (rtx target) +bpf_output_call (const char *templ, rtx *operands, int target_index) { - rtx xops[1]; - + rtx target = operands[target_index]; switch (GET_CODE (target)) { case CONST_INT: - output_asm_insn ("call\t%0", &target); break; case SYMBOL_REF: { @@ -765,26 +763,21 @@ bpf_output_call (rtx target) { tree attr_args = TREE_VALUE (attr); - xops[0] = GEN_INT (TREE_INT_CST_LOW (TREE_VALUE (attr_args))); - output_asm_insn ("call\t%0", xops); + operands[target_index] = + GEN_INT (TREE_INT_CST_LOW (TREE_VALUE (attr_args))); } - else - output_asm_insn ("call\t%0", &target); - break; } default: - if (TARGET_XBPF) - output_asm_insn ("call\t%0", &target); - else + if (!TARGET_XBPF) { error ("indirect call in function, which are not supported by eBPF"); - output_asm_insn ("call 0", NULL); + operands[target_index] = GEN_INT (0); } break; } - return ""; + return templ; } const char * @@ -1145,6 +1138,90 @@ bpf_debug_unwind_info () #undef TARGET_DEBUG_UNWIND_INFO #define TARGET_DEBUG_UNWIND_INFO bpf_debug_unwind_info +/* Create a BTF.ext line_info entry. */ + +static void +bpf_output_line_info (FILE *asm_out_file, rtx_insn *insn) +{ + static unsigned int line_info_label = 1; + static tree cfun_decl = NULL_TREE; + static bool func_start_added = false; + const char *label = NULL; + unsigned int loc = 0; + expanded_location exploc = {NULL, 0, 0, NULL, false}; + static expanded_location prev_exploc = {NULL, 0, 0, NULL, false}; + + if(!btf_debuginfo_p () || flag_building_libgcc) + return; + + gcc_assert (insn != NULL_RTX); + + if (current_function_decl != cfun_decl + && GET_CODE (insn) == NOTE) + { + label = current_function_func_begin_label; + loc = DECL_SOURCE_LOCATION (current_function_decl); + exploc = expand_location (loc); + func_start_added = true; + prev_exploc = exploc; + } + else + { + if (GET_CODE (insn) == NOTE) + return; + + /* Already added a label for this location. This might not be fully + accurate but it is better then adding 2 entries on the same location, + which is incompatible with the verifier expectations. */ + if (func_start_added == true) + { + func_start_added = false; + return; + } + + + if (!INSN_HAS_LOCATION (insn)) + return; + + exploc = insn_location (insn); + + if (exploc.file == NULL || exploc.line == 0 + || (exploc.line == prev_exploc.line + && exploc.column == prev_exploc.column)) + return; + + prev_exploc = exploc; + + char tmp_label[25]; + sprintf (tmp_label, "LI%u", line_info_label); + ASM_OUTPUT_LABEL (asm_out_file, tmp_label); + line_info_label += 1; + label = const_cast<char *> (ggc_strdup (tmp_label)); + } + + cfun_decl = current_function_decl; + + if (exploc.file != NULL && exploc.line != 0) + btf_add_line_info_for (label, exploc.file, exploc.line, exploc.column); +} + + +/* This hook is defined as a way for BPF target to create a label before each + emitted instruction and emit line_info information. This data is later + output in .BTF.ext section. + This approach expects TARGET_EMIT_BEFORE_INSN to be returning TRUE as + this function needs to be called before the instruction is emitted. Current + default behaviour returns TRUE and the hook is left undefined. */ + +static void +bpf_asm_out_unwind_emit (FILE *asm_out_file, rtx_insn *insn) +{ + bpf_output_line_info (asm_out_file, insn); +} + +#undef TARGET_ASM_UNWIND_EMIT +#define TARGET_ASM_UNWIND_EMIT bpf_asm_out_unwind_emit + /* Output assembly directives to assemble data of various sized and alignments. */ diff --git a/gcc/config/bpf/bpf.md b/gcc/config/bpf/bpf.md index 0eec006d3cfe..bc739f2746c0 100644 --- a/gcc/config/bpf/bpf.md +++ b/gcc/config/bpf/bpf.md @@ -545,7 +545,7 @@ ;; operands[2] is next_arg_register ;; operands[3] is struct_value_size_rtx. "" - { return bpf_output_call (operands[0]); } + { return bpf_output_call ("call\t%0", operands, 0); } [(set_attr "type" "jmp")]) (define_expand "call_value" @@ -568,7 +568,7 @@ ;; operands[3] is next_arg_register ;; operands[4] is struct_value_size_rtx. "" - { return bpf_output_call (operands[1]); } + { return bpf_output_call ("call\t%1", operands, 1); } [(set_attr "type" "jmp")]) (define_insn "sibcall" diff --git a/gcc/config/bpf/btfext-out.cc b/gcc/config/bpf/btfext-out.cc index 04bc60d69b0e..f4480d34ed7c 100644 --- a/gcc/config/bpf/btfext-out.cc +++ b/gcc/config/bpf/btfext-out.cc @@ -32,6 +32,7 @@ #include "rtl.h" #include "tree-pretty-print.h" #include "cgraph.h" +#include "toplev.h" /* get_src_pwd */ #include "btfext-out.h" @@ -124,14 +125,19 @@ struct GTY ((chain_next ("%h.next"))) btf_ext_funcinfo /* A lineinfo record, in the .BTF.ext lineinfo section. */ struct GTY ((chain_next ("%h.next"))) btf_ext_lineinfo { - uint32_t insn_off; /* Offset of the instruction. */ + const char *insn_label; /* Instruction label. */ + const char *file_name; /* Source-code file name. */ uint32_t file_name_off; /* Offset of file name in BTF string table. */ uint32_t line_off; /* Offset of source line in BTF string table. */ - uint32_t line_col; /* Line number (bits 31-11) and column (11-0). */ + uint32_t line_col; /* Line number (bits 31-10) and column (9-0). */ struct btf_ext_lineinfo *next; /* Linked list to collect line_info elems. */ }; +#define BTF_LINE_INFO_LINE(line_col) ((line_col) >> 10) +#define BTF_LINE_INFO_COLUMN(line_col) ((line_col) & 0x3ff) +#define BTF_LINE_INFO_LINE_COL(line, column) (line << 10 | (column & 0x3ff)) + /* Internal representation of a BPF CO-RE relocation record. */ struct GTY ((chain_next ("%h.next"))) btf_ext_core_reloc { ctf_dtdef_ref bpfcr_type; /* BTF type involved in relocation. */ @@ -235,6 +241,26 @@ bpf_create_or_find_funcinfo (const char *fnname, const char *sec_name, return *head; } +/* Function to create a lineinfo node in info. */ + +static struct btf_ext_lineinfo * +bpf_create_lineinfo (const char *sec_name, btf_ext_info_sec **in_sec = NULL) +{ + struct btf_ext_info_sec *sec_elem = + btfext_info_sec_find_or_add (sec_name, true); + + if (in_sec != NULL) + *in_sec = sec_elem; + + struct btf_ext_lineinfo **head = + SEARCH_NODE_AND_RETURN(struct btf_ext_lineinfo, + sec_elem->line_info.head, + false); + *head = ggc_cleared_alloc<struct btf_ext_lineinfo> (); + + return *head; +} + /* Function to create a core_reloc node in info. */ static struct btf_ext_core_reloc * @@ -438,6 +464,43 @@ btf_validate_funcinfo (btf_ext_info_sec *sec) } } +#define STR_BUFF_SIZE 256 + +struct btf_ext_lineinfo * +btf_add_line_info_for (const char *label, const char *filename, + unsigned int line, unsigned int column) +{ + const char *sec_name = decl_section_name (current_function_decl); + + if (sec_name == NULL) + sec_name = ".text"; + + struct btf_ext_info_sec *sec = NULL; + struct btf_ext_lineinfo *info = + bpf_create_lineinfo (sec_name, &sec); + + info->insn_label = label; + + if (!IS_DIR_SEPARATOR (filename[0])) + { + char *s = xasprintf ("%s%c%s", get_src_pwd (), + DIR_SEPARATOR, filename); + info->file_name = ggc_strdup (s); + free (s); + } + else + /* Filename is an absolute path. */ + info->file_name = ggc_strdup (filename); + + info->file_name_off = btf_ext_add_string (info->file_name); + info->line_off = 0; + info->line_col = BTF_LINE_INFO_LINE_COL (line, column); + + sec->line_info.num_info += 1; + return info; +} +#undef STR_BUFF_SIZE + /* Compute the section size in section for func_info, line_info and core_info regions of .BTF.ext. */ @@ -545,6 +608,48 @@ output_btfext_func_info (struct btf_ext_info_sec *sec) } } +/* Outputs line_info region on .BTF.ext. */ + +static void +output_btfext_line_info (struct btf_ext_info_sec *sec) +{ + unsigned int str_aux_off = ctfc_get_strtab_len (ctf_get_tu_ctfc (), + CTF_STRTAB); + bool executed = false; + while (sec != NULL) + { + uint32_t count = 0; + if (sec->line_info.num_info > 0) + { + if (executed == false && (executed = true)) + dw2_asm_output_data (4, 16, "LineInfo entry size"); + dw2_asm_output_data (4, sec->sec_name_off + str_aux_off, + "LineInfo section string for %s", + sec->sec_name); + dw2_asm_output_data (4, sec->line_info.num_info, "Number of entries"); + + struct btf_ext_lineinfo *elem = sec->line_info.head; + while (elem != NULL) + { + count += 1; + dw2_asm_output_offset (4, elem->insn_label, NULL, "insn_label"); + + unsigned int file_name_off = btf_ext_add_string (elem->file_name); + dw2_asm_output_data (4, file_name_off + str_aux_off, + "file_name_off"); + dw2_asm_output_data (4, elem->line_off, "line_off"); + dw2_asm_output_data (4, elem->line_col, "(line, col) (%u, %u)", + BTF_LINE_INFO_LINE (elem->line_col), + BTF_LINE_INFO_COLUMN (elem->line_col)); + elem = elem->next; + } + } + + gcc_assert (count == sec->line_info.num_info); + sec = sec->next; + } +} + /* Output all CO-RE relocation sections. */ static void @@ -622,6 +727,7 @@ btf_ext_output (void) output_btfext_header (); output_btfext_func_info (btf_ext); + output_btfext_line_info (btf_ext); if (TARGET_BPF_CORE) output_btfext_core_sections (); diff --git a/gcc/config/bpf/btfext-out.h b/gcc/config/bpf/btfext-out.h index a4c87ffd1f02..fe9b3768643e 100644 --- a/gcc/config/bpf/btfext-out.h +++ b/gcc/config/bpf/btfext-out.h @@ -99,6 +99,10 @@ extern int bpf_core_get_sou_member_index (ctf_container_ref, const tree); struct btf_ext_funcinfo *btf_add_func_info_for (tree decl, const char *label); +struct btf_ext_lineinfo * +btf_add_line_info_for (const char *label, const char *filename, + unsigned int line, unsigned int column); + unsigned int btf_ext_add_string (const char *str); #ifdef __cplusplus diff --git a/gcc/testsuite/gcc.target/bpf/btfext-funcinfo.c b/gcc/testsuite/gcc.target/bpf/btfext-funcinfo.c index fbbefeae68f0..f7f5b44b1ff4 100644 --- a/gcc/testsuite/gcc.target/bpf/btfext-funcinfo.c +++ b/gcc/testsuite/gcc.target/bpf/btfext-funcinfo.c @@ -39,6 +39,5 @@ int bar_func (struct T *t) /* { dg-final { scan-assembler-times "ascii \"bar_sec.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */ /* { dg-final { scan-assembler-times "FuncInfo entry size" 1 } } */ -/* { dg-final { scan-assembler-times ".4byte\t0x1\t# Number of entries" 3 } } */ -/* { dg-final { scan-assembler-times ".4byte\t0x2\t# Number of entries" 1 } } */ +/* { dg-final { scan-assembler-times "FuncInfo section\[^\n\]*\n\[^0\]*0x1\t# Number of entries" 2 } } */ /* { dg-final { scan-assembler-times "Required padding" 1 } } */ diff --git a/gcc/testsuite/gcc.target/bpf/btfext-lineinfo.c b/gcc/testsuite/gcc.target/bpf/btfext-lineinfo.c new file mode 100644 index 000000000000..dc1c297d663e --- /dev/null +++ b/gcc/testsuite/gcc.target/bpf/btfext-lineinfo.c @@ -0,0 +1,51 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -dA -gbtf -masm=normal" } */ + +struct T { + int a; + int b; + struct U { + int c; + struct V { + int d; + int e[4]; + int f; + } v; + } u; +} __attribute__((preserve_access_index)); + +__attribute__((section("foo_sec"), used)) +int foo_func (struct T *t) +{ + t->u.c = 5; + return t->u.v.e[3]; +} + +__attribute__((section("bar_sec"), used)) +int bar_func (struct T *t) +{ + int *x = &(t->u.v.f); + int old = *x; + *x = 4; + return old; +} + +/* { dg-final { scan-assembler-times "LineInfo section string for foo_sec" 1 } } */ +/* { dg-final { scan-assembler-times "LineInfo section string for bar_sec" 1 } } */ +/* { dg-final { scan-assembler-times "label for function foo_func" 1 } } */ +/* { dg-final { scan-assembler-times "label for function bar_func" 1 } } */ + + +/* { dg-final { scan-assembler-times "btfext-lineinfo\.c.0\"\[\t \]+\[^\n\]*btf_aux_string" 1 } } */ +/* { dg-final { scan-assembler-times "4byte\[\t \]+\.LFB.\[\t \]+# insn_label" 2 } } */ +/* { dg-final { scan-assembler-times "4byte\[\t \]+LI.\[\t \]+# insn_label" 6 } } */ + +/* { dg-final { scan-assembler-times "# \\(line, col\\)" 8 } } */ +/* { dg-final { scan-assembler-times "# \\(line, col\\) \\(18, 5\\)" 1 } } */ +/* { dg-final { scan-assembler-times "# \\(line, col\\) \\(20, 10\\)" 1 } } */ +/* { dg-final { scan-assembler-times "# \\(line, col\\) \\(21, 18\\)" 1 } } */ +/* { dg-final { scan-assembler-times "# \\(line, col\\) \\(22, 1\\)" 1 } } */ +/* { dg-final { scan-assembler-times "# \\(line, col\\) \\(25, 5\\)" 1 } } */ +/* { dg-final { scan-assembler-times "# \\(line, col\\) \\(28, 7\\)" 1 } } */ +/* { dg-final { scan-assembler-times "# \\(line, col\\) \\(29, 6\\)" 1 } } */ +/* { dg-final { scan-assembler-times "# \\(line, col\\) \\(31, 1\\)" 1 } } */
