gas/Dwarf: record functions
authorJan Beulich <jbeulich@suse.com>
Thu, 7 Apr 2022 06:18:00 +0000 (08:18 +0200)
committerJan Beulich <jbeulich@suse.com>
Thu, 7 Apr 2022 06:18:00 +0000 (08:18 +0200)
To help tools like addr2line looking up function names, in particular
when dealing with e.g. PE/COFF binaries (linked from ELF objects), where
there's no ELF symbol table to fall back to, emit minimalistic
information for functions marked as such and having their size
specified.

Notes regarding the restriction to (pure) ELF:
- I realize this is a layering violation; I don't see how to deal with
  that in a better way.
- S_GET_SIZE(), when OBJ_MAYBE_ELF is defined, looks wrong: Unlike
  S_SET_SIZE() it does not check whether the hook is NULL.
- symbol_get_obj(), when OBJ_MAYBE_ELF is defined, looks unusable, as
  its return type can only ever be one object format's type (and this
  may then not be ELF's).

The new testcases are limited to x86 because I wanted to include the
case where function size can't be determined yet at the time Dwarf2 info
is generated. As .nops gains support by further targets, they could also
be added here then (with, as necessary, expecations suitably relaxed to
cover for insn size differences).

gas/dwarf2dbg.c
gas/testsuite/gas/elf/dwarf-3-func.d [new file with mode: 0644]
gas/testsuite/gas/elf/dwarf-3-func.s [new file with mode: 0644]
gas/testsuite/gas/elf/dwarf-5-func-global.d [new file with mode: 0644]
gas/testsuite/gas/elf/dwarf-5-func-local.d [new file with mode: 0644]
gas/testsuite/gas/elf/dwarf-5-func.d [new file with mode: 0644]
gas/testsuite/gas/elf/elf.exp

index fee56c249e4fd9e300858a47f8470b9b8b3f9692..44d7d9e1331ebd1c87885ca2ff03e434ea87e741 100644 (file)
@@ -2674,14 +2674,55 @@ out_debug_aranges (segT aranges_seg, segT info_seg)
 static void
 out_debug_abbrev (segT abbrev_seg,
                  segT info_seg ATTRIBUTE_UNUSED,
-                 segT line_seg ATTRIBUTE_UNUSED)
+                 segT line_seg ATTRIBUTE_UNUSED,
+                 unsigned char *func_formP)
 {
   int secoff_form;
+  bool have_efunc = false, have_lfunc = false;
+
+  /* Check the symbol table for function symbols which also have their size
+     specified.  */
+  if (symbol_rootP)
+    {
+      symbolS *symp;
+
+      for (symp = symbol_rootP; symp; symp = symbol_next (symp))
+       {
+         /* A warning construct is a warning symbol followed by the
+            symbol warned about.  Skip this and the following symbol.  */
+         if (symbol_get_bfdsym (symp)->flags & BSF_WARNING)
+           {
+             symp = symbol_next (symp);
+             if (!symp)
+               break;
+             continue;
+           }
+
+         if (!S_IS_DEFINED (symp) || !S_IS_FUNCTION (symp))
+           continue;
+
+#if defined (OBJ_ELF) /* || defined (OBJ_MAYBE_ELF) */
+         if (S_GET_SIZE (symp) == 0)
+           {
+             if (!IS_ELF || symbol_get_obj (symp)->size == NULL)
+               continue;
+           }
+#else
+         continue;
+#endif
+
+         if (S_IS_EXTERNAL (symp))
+           have_efunc = true;
+         else
+           have_lfunc = true;
+       }
+    }
+
   subseg_set (abbrev_seg, 0);
 
   out_uleb128 (1);
   out_uleb128 (DW_TAG_compile_unit);
-  out_byte (DW_CHILDREN_no);
+  out_byte (have_efunc || have_lfunc ? DW_CHILDREN_yes : DW_CHILDREN_no);
   if (DWARF2_VERSION < 4)
     {
       if (DWARF2_FORMAT (line_seg) == dwarf2_format_32bit)
@@ -2708,6 +2749,29 @@ out_debug_abbrev (segT abbrev_seg,
   out_abbrev (DW_AT_language, DW_FORM_data2);
   out_abbrev (0, 0);
 
+  if (have_efunc || have_lfunc)
+    {
+      out_uleb128 (2);
+      out_uleb128 (DW_TAG_subprogram);
+      out_byte (DW_CHILDREN_no);
+      out_abbrev (DW_AT_name, DW_FORM_strp);
+      if (have_efunc)
+       {
+         if (have_lfunc || DWARF2_VERSION < 4)
+           *func_formP = DW_FORM_flag;
+         else
+           *func_formP = DW_FORM_flag_present;
+         out_abbrev (DW_AT_external, *func_formP);
+       }
+      else
+       /* Any non-zero value other than DW_FORM_flag will do.  */
+       *func_formP = DW_FORM_block;
+      out_abbrev (DW_AT_low_pc, DW_FORM_addr);
+      out_abbrev (DW_AT_high_pc,
+                 DWARF2_VERSION < 4 ? DW_FORM_addr : DW_FORM_udata);
+      out_abbrev (0, 0);
+    }
+
   /* Terminate the abbreviations for this compilation unit.  */
   out_byte (0);
 }
@@ -2715,9 +2779,10 @@ out_debug_abbrev (segT abbrev_seg,
 /* Emit a description of this compilation unit for .debug_info.  */
 
 static void
-out_debug_info (segT info_seg, segT abbrev_seg, segT line_seg,
+out_debug_info (segT info_seg, segT abbrev_seg, segT line_seg, segT str_seg,
                symbolS *ranges_sym, symbolS *name_sym,
-               symbolS *comp_dir_sym, symbolS *producer_sym)
+               symbolS *comp_dir_sym, symbolS *producer_sym,
+               unsigned char func_form)
 {
   expressionS exp;
   symbolS *info_end;
@@ -2799,6 +2864,81 @@ out_debug_info (segT info_seg, segT abbrev_seg, segT line_seg,
      dwarf2 draft has no standard code for assembler.  */
   out_two (DW_LANG_Mips_Assembler);
 
+  if (func_form)
+    {
+      symbolS *symp;
+
+      for (symp = symbol_rootP; symp; symp = symbol_next (symp))
+       {
+         const char *name;
+         size_t len;
+
+         /* Skip warning constructs (see above).  */
+         if (symbol_get_bfdsym (symp)->flags & BSF_WARNING)
+           {
+             symp = symbol_next (symp);
+             if (!symp)
+               break;
+             continue;
+           }
+
+         if (!S_IS_DEFINED (symp) || !S_IS_FUNCTION (symp))
+           continue;
+
+         subseg_set (str_seg, 0);
+         name_sym = symbol_temp_new_now_octets ();
+         name = S_GET_NAME (symp);
+         len = strlen (name) + 1;
+         memcpy (frag_more (len), name, len);
+
+         subseg_set (info_seg, 0);
+
+         /* DW_TAG_subprogram DIE abbrev */
+         out_uleb128 (2);
+
+         /* DW_AT_name */
+         TC_DWARF2_EMIT_OFFSET (name_sym, sizeof_offset);
+
+         /* DW_AT_external.  */
+         if (func_form == DW_FORM_flag)
+           out_byte (S_IS_EXTERNAL (symp));
+
+         /* DW_AT_low_pc */
+         exp.X_op = O_symbol;
+         exp.X_add_symbol = symp;
+         exp.X_add_number = 0;
+         emit_expr (&exp, sizeof_address);
+
+         /* DW_AT_high_pc */
+         exp.X_op = O_constant;
+#if defined (OBJ_ELF) /* || defined (OBJ_MAYBE_ELF) */
+         exp.X_add_number = S_GET_SIZE (symp);
+         if (exp.X_add_number == 0 && IS_ELF
+             && symbol_get_obj (symp)->size != NULL)
+           {
+             exp.X_op = O_add;
+             exp.X_op_symbol = make_expr_symbol (symbol_get_obj (symp)->size);
+           }
+#else
+         exp.X_add_number = 0;
+#endif
+         if (DWARF2_VERSION < 4)
+           {
+             if (exp.X_op == O_constant)
+               exp.X_op = O_symbol;
+             exp.X_add_symbol = symp;
+             emit_expr (&exp, sizeof_address);
+           }
+         else if (exp.X_op == O_constant)
+           out_uleb128 (exp.X_add_number);
+         else
+           emit_leb128_expr (symbol_get_value_expression (exp.X_op_symbol), 0);
+       }
+
+      /* End of children.  */
+      out_leb128 (0);
+    }
+
   symbol_set_value_now (info_end);
 }
 
@@ -2968,6 +3108,7 @@ dwarf2_finish (void)
       segT aranges_seg;
       segT str_seg;
       symbolS *name_sym, *comp_dir_sym, *producer_sym, *ranges_sym;
+      unsigned char func_form = 0;
 
       gas_assert (all_segs);
 
@@ -3013,10 +3154,11 @@ dwarf2_finish (void)
        }
 
       out_debug_aranges (aranges_seg, info_seg);
-      out_debug_abbrev (abbrev_seg, info_seg, line_seg);
+      out_debug_abbrev (abbrev_seg, info_seg, line_seg, &func_form);
       out_debug_str (str_seg, &name_sym, &comp_dir_sym, &producer_sym);
-      out_debug_info (info_seg, abbrev_seg, line_seg, ranges_sym,
-                     name_sym, comp_dir_sym, producer_sym);
+      out_debug_info (info_seg, abbrev_seg, line_seg, str_seg,
+                     ranges_sym, name_sym, comp_dir_sym, producer_sym,
+                     func_form);
     }
 }
 
diff --git a/gas/testsuite/gas/elf/dwarf-3-func.d b/gas/testsuite/gas/elf/dwarf-3-func.d
new file mode 100644 (file)
index 0000000..0196f69
--- /dev/null
@@ -0,0 +1,48 @@
+#as: --gdwarf-3
+#name: Dwarf3 function debug info
+#readelf: -W -wai
+#target: i?86-*-* x86_64-*-*
+
+Contents of the .debug_info section:
+
+ +Compilation Unit @ offset (0x)?0:
+ +Length: .*
+ +Version: +3
+ +Abbrev Offset: +(0x)?0
+ +Pointer Size: .*
+ <0><[0-9a-f]+>: Abbrev Number: 1 \(DW_TAG_compile_unit\)
+#...
+ <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\)
+ +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): efunc1
+ +<[0-9a-f]+> +DW_AT_external +: \(flag\) 1
+ +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?0
+ +<[0-9a-f]+> +DW_AT_high_pc +: \(addr\) (0x)?2
+ <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\)
+ +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): lfunc1
+ +<[0-9a-f]+> +DW_AT_external +: \(flag\) 0
+ +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?2
+ +<[0-9a-f]+> +DW_AT_high_pc +: \(addr\) (0x)?13
+ <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\)
+ +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): efunc2
+ +<[0-9a-f]+> +DW_AT_external +: \(flag\) 1
+ +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?13
+ +<[0-9a-f]+> +DW_AT_high_pc +: \(addr\) (0x)?35
+ <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\)
+ +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): lfunc2
+ +<[0-9a-f]+> +DW_AT_external +: \(flag\) 0
+ +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?35
+ +<[0-9a-f]+> +DW_AT_high_pc +: \(addr\) (0x)?38
+ <1><[0-9a-f]+>: Abbrev Number: 0
+
+Contents of the .debug_abbrev section:
+
+ +Number TAG \(0x0\)
+ +1 +DW_TAG_compile_unit +\[has children\]
+#...
+ +2 +DW_TAG_subprogram +\[no children\]
+ +DW_AT_name +DW_FORM_strp
+ +DW_AT_external +DW_FORM_flag
+ +DW_AT_low_pc +DW_FORM_addr
+ +DW_AT_high_pc +DW_FORM_addr
+ +DW_AT value: 0 +DW_FORM value: 0
+#pass
diff --git a/gas/testsuite/gas/elf/dwarf-3-func.s b/gas/testsuite/gas/elf/dwarf-3-func.s
new file mode 100644 (file)
index 0000000..4610686
--- /dev/null
@@ -0,0 +1,40 @@
+       .text
+
+       .ifndef LOCAL
+efunc1:
+       .nop
+       .nop
+       .global efunc1
+       .type efunc1, %function
+       .size efunc1, .-efunc1
+       .endif
+
+       .ifndef GLOBAL
+lfunc1:
+       .nops 16
+       .nop
+       .type lfunc1, %function
+       .size lfunc1, .-lfunc1
+       .endif
+
+       .ifndef LOCAL
+efunc2:
+       .nop
+       .nops 32
+       .nop
+       .global efunc2
+       .type efunc2, %function
+       .size efunc2, .-efunc2
+       .endif
+
+       .global efunc3
+       .type efunc3, %function
+
+       .ifndef GLOBAL
+lfunc2:
+       .nop
+       .nop
+       .nop
+       .type lfunc2, %function
+       .size lfunc2, .-lfunc2
+       .endif
diff --git a/gas/testsuite/gas/elf/dwarf-5-func-global.d b/gas/testsuite/gas/elf/dwarf-5-func-global.d
new file mode 100644 (file)
index 0000000..09b10fd
--- /dev/null
@@ -0,0 +1,40 @@
+#as: --gdwarf-5 --defsym GLOBAL=1
+#name: Dwarf5 global function debug info
+#readelf: -W -wai
+#source: dwarf-3-func.s
+#target: i?86-*-* x86_64-*-*
+
+Contents of the .debug_info section:
+
+ +Compilation Unit @ offset (0x)?0:
+ +Length: .*
+ +Version: +5
+ +Unit Type: +DW_UT_compile \(1\)
+ +Abbrev Offset: +(0x)?0
+ +Pointer Size: .*
+ <0><[0-9a-f]+>: Abbrev Number: 1 \(DW_TAG_compile_unit\)
+#...
+ <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\)
+ +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): efunc1
+ +<[0-9a-f]+> +DW_AT_external +: \(flag_present\) 1
+ +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?0
+ +<[0-9a-f]+> +DW_AT_high_pc +: \(udata\) 2
+ <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\)
+ +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): efunc2
+ +<[0-9a-f]+> +DW_AT_external +: \(flag_present\) 1
+ +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?2
+ +<[0-9a-f]+> +DW_AT_high_pc +: \(udata\) 34
+ <1><[0-9a-f]+>: Abbrev Number: 0
+
+Contents of the .debug_abbrev section:
+
+ +Number TAG \(0x0\)
+ +1 +DW_TAG_compile_unit +\[has children\]
+#...
+ +2 +DW_TAG_subprogram +\[no children\]
+ +DW_AT_name +DW_FORM_strp
+ +DW_AT_external +DW_FORM_flag_present
+ +DW_AT_low_pc +DW_FORM_addr
+ +DW_AT_high_pc +DW_FORM_udata
+ +DW_AT value: 0 +DW_FORM value: 0
+#pass
diff --git a/gas/testsuite/gas/elf/dwarf-5-func-local.d b/gas/testsuite/gas/elf/dwarf-5-func-local.d
new file mode 100644 (file)
index 0000000..fd97841
--- /dev/null
@@ -0,0 +1,37 @@
+#as: --gdwarf-5 --defsym LOCAL=1
+#name: Dwarf5 local function debug info
+#readelf: -W -wai
+#source: dwarf-3-func.s
+#target: i?86-*-* x86_64-*-*
+
+Contents of the .debug_info section:
+
+ +Compilation Unit @ offset (0x)?0:
+ +Length: .*
+ +Version: +5
+ +Unit Type: +DW_UT_compile \(1\)
+ +Abbrev Offset: +(0x)?0
+ +Pointer Size: .*
+ <0><[0-9a-f]+>: Abbrev Number: 1 \(DW_TAG_compile_unit\)
+#...
+ <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\)
+ +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): lfunc1
+ +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?0
+ +<[0-9a-f]+> +DW_AT_high_pc +: \(udata\) 17
+ <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\)
+ +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): lfunc2
+ +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?11
+ +<[0-9a-f]+> +DW_AT_high_pc +: \(udata\) 3
+ <1><[0-9a-f]+>: Abbrev Number: 0
+
+Contents of the .debug_abbrev section:
+
+ +Number TAG \(0x0\)
+ +1 +DW_TAG_compile_unit +\[has children\]
+#...
+ +2 +DW_TAG_subprogram +\[no children\]
+ +DW_AT_name +DW_FORM_strp
+ +DW_AT_low_pc +DW_FORM_addr
+ +DW_AT_high_pc +DW_FORM_udata
+ +DW_AT value: 0 +DW_FORM value: 0
+#pass
diff --git a/gas/testsuite/gas/elf/dwarf-5-func.d b/gas/testsuite/gas/elf/dwarf-5-func.d
new file mode 100644 (file)
index 0000000..11a78b2
--- /dev/null
@@ -0,0 +1,50 @@
+#as: --gdwarf-5
+#name: Dwarf5 function debug info
+#readelf: -W -wai
+#source: dwarf-3-func.s
+#target: i?86-*-* x86_64-*-*
+
+Contents of the .debug_info section:
+
+ +Compilation Unit @ offset (0x)?0:
+ +Length: .*
+ +Version: +5
+ +Unit Type: +DW_UT_compile \(1\)
+ +Abbrev Offset: +(0x)?0
+ +Pointer Size: .*
+ <0><[0-9a-f]+>: Abbrev Number: 1 \(DW_TAG_compile_unit\)
+#...
+ <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\)
+ +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): efunc1
+ +<[0-9a-f]+> +DW_AT_external +: \(flag\) 1
+ +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?0
+ +<[0-9a-f]+> +DW_AT_high_pc +: \(udata\) 2
+ <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\)
+ +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): lfunc1
+ +<[0-9a-f]+> +DW_AT_external +: \(flag\) 0
+ +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?2
+ +<[0-9a-f]+> +DW_AT_high_pc +: \(udata\) 17
+ <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\)
+ +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): efunc2
+ +<[0-9a-f]+> +DW_AT_external +: \(flag\) 1
+ +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?13
+ +<[0-9a-f]+> +DW_AT_high_pc +: \(udata\) 34
+ <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\)
+ +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): lfunc2
+ +<[0-9a-f]+> +DW_AT_external +: \(flag\) 0
+ +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?35
+ +<[0-9a-f]+> +DW_AT_high_pc +: \(udata\) 3
+ <1><[0-9a-f]+>: Abbrev Number: 0
+
+Contents of the .debug_abbrev section:
+
+ +Number TAG \(0x0\)
+ +1 +DW_TAG_compile_unit +\[has children\]
+#...
+ +2 +DW_TAG_subprogram +\[no children\]
+ +DW_AT_name +DW_FORM_strp
+ +DW_AT_external +DW_FORM_flag
+ +DW_AT_low_pc +DW_FORM_addr
+ +DW_AT_high_pc +DW_FORM_udata
+ +DW_AT value: 0 +DW_FORM value: 0
+#pass
index 963730a87a554de02c2dcee01ce3fc9ee06d2400..abdc288c2ec763ae4da54602ada9dfe575e94836 100644 (file)
@@ -308,6 +308,10 @@ if { [is_elf_format] } then {
     run_dump_test "dwarf-5-cu" $dump_opts
     run_dump_test "dwarf-5-nop-for-line-table" $dump_opts
     run_dump_test "dwarf-5-irp" $dump_opts
+    run_dump_test "dwarf-3-func" $dump_opts
+    run_dump_test "dwarf-5-func" $dump_opts
+    run_dump_test "dwarf-5-func-global" $dump_opts
+    run_dump_test "dwarf-5-func-local" $dump_opts
     run_dump_test "pr25917"
     run_dump_test "bss"
     run_dump_test "bad-bss"