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 } } */

Reply via email to