readelf: Support .gdb_index version 9

Message ID 20231031203312.536219-1-amerey@redhat.com
State Committed
Headers
Series readelf: Support .gdb_index version 9 |

Commit Message

Aaron Merey Oct. 31, 2023, 8:33 p.m. UTC
  Hi,

I'd like to merge this patch before the next release.  Unless anyone objects
I'll merge it by Friday Nov 3.

Commit message:

Version 9 adds a "shortcut table" to the index.  The shortcut table contains
the name and language of the main function, if it exists.

A testcase added in this patch uses an executable written with Fortran.
This is because gdb does not currently populate the shortcut table of
C/C++ programs (see sourceware PR30996).

Signed-off-by: Aaron Merey <amerey@redhat.com>
---
 src/readelf.c                           |  66 +++++++++++++++-
 tests/Makefile.am                       |   3 +-
 tests/run-readelf-gdb_index.sh          |  95 +++++++++++++++++++++++-
 tests/testfilegdbindex9-no-maininfo.bz2 | Bin 0 -> 3502 bytes
 tests/testfilegdbindex9.bz2             | Bin 0 -> 4266 bytes
 5 files changed, 159 insertions(+), 5 deletions(-)
 create mode 100755 tests/testfilegdbindex9-no-maininfo.bz2
 create mode 100755 tests/testfilegdbindex9.bz2

new file mode 100755
index 0000000000000000000000000000000000000000..9a2c18931a21392f396fa05c305f79ec434a851e
GIT binary patch
literal 4266
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
MBvXY63uqC+u-byG*Z=?k

literal 0
HcmV?d00001
  

Comments

Mark Wielaard Nov. 2, 2023, 12:53 p.m. UTC | #1
Hi Aaron,

On Tue, 2023-10-31 at 16:33 -0400, Aaron Merey wrote:
> I'd like to merge this patch before the next release.  Unless anyone objects
> I'll merge it by Friday Nov 3.

Looks good to me. Some small comments below.

> Commit message:

BTW if you add comments like the above after the first "scissors" ---
line, they wont show up in the actually commit message when applying
the email message (that is why the stat output is put there).

> Version 9 adds a "shortcut table" to the index.  The shortcut table contains
> the name and language of the main function, if it exists.
> 
> A testcase added in this patch uses an executable written with Fortran.
> This is because gdb does not currently populate the shortcut table of
> C/C++ programs (see sourceware PR30996).
> 
> Signed-off-by: Aaron Merey <amerey@redhat.com>
> ---
>  src/readelf.c                           |  66 +++++++++++++++-
>  tests/Makefile.am                       |   3 +-
>  tests/run-readelf-gdb_index.sh          |  95 +++++++++++++++++++++++-
>  tests/testfilegdbindex9-no-maininfo.bz2 | Bin 0 -> 3502 bytes
>  tests/testfilegdbindex9.bz2             | Bin 0 -> 4266 bytes
>  5 files changed, 159 insertions(+), 5 deletions(-)
>  create mode 100755 tests/testfilegdbindex9-no-maininfo.bz2
>  create mode 100755 tests/testfilegdbindex9.bz2
> 
> diff --git a/src/readelf.c b/src/readelf.c
> index db31ad09..a28f6236 100644
> --- a/src/readelf.c
> +++ b/src/readelf.c
> @@ -11539,8 +11539,9 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl,
>    // hash used for generating the table.  Version 6 contains symbols
>    // for inlined functions, older versions didn't.  Version 7 adds
>    // symbol kinds.  Version 8 just indicates that it correctly includes
> -  // TUs for symbols.
> -  if (vers < 4 || vers > 8)
> +  // TUs for symbols.  Version 9 adds shortcut table for information
> +  // regarding the main function.
> +  if (vers < 4 || vers > 9)
>      {
>        printf (_("  unknown version, cannot parse section\n"));
>        return;

OK. BTW the description of the gdb_index at the top
https://sourceware.org/gdb/current/onlinedocs/gdb/Index-Section-Format.html
doesn't resolve anymore. It is now 
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Index-Section-Format.html
Maybe we should report that to the gdb project.

> @@ -11578,6 +11579,17 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl,
>    if (unlikely (readp + 4 > dataend))
>      goto invalid_data;
>  
> +  uint32_t shortcut_off = 0;
> +  if (vers >= 9)
> +    {
> +      shortcut_off = read_4ubyte_unaligned (dbg, readp);
> +      printf (_(" shortcut offset: %#" PRIx32 "\n"), shortcut_off);
> +
> +      readp += 4;
> +      if (unlikely (readp + 4 > dataend))
> +	goto invalid_data;
> +    }
> +
>    uint32_t const_off = read_4ubyte_unaligned (dbg, readp);
>    printf (_(" constant offset: %#" PRIx32 "\n"), const_off);

OK

> @@ -11675,8 +11687,19 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl,
>    if (const_off >= data->d_size)
>      goto invalid_data;
>  
> +  const unsigned char *shortcut_start = NULL;
> +  if (vers >= 9)
> +    {
> +      if (shortcut_off >= data->d_size)
> +	goto invalid_data;
> +
> +      shortcut_start = data->d_buf + shortcut_off;
> +      nextp = shortcut_start;
> +    }
> +  else
> +    nextp = const_start;
> +
>    readp = data->d_buf + sym_off;
> -  nextp = const_start;
>    size_t sym_nr = (nextp - readp) / 8;
>  
>    printf (_("\n Symbol table at offset %#" PRIx32

OK

> @@ -11750,6 +11773,43 @@ print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl,
>  	}
>        n++;
>      }
> +
> +  if (vers < 9)
> +    return;
> +
> +  if (unlikely (shortcut_start == NULL))
> +    goto invalid_data;
> +
> +  readp = shortcut_start;
> +  nextp = const_start;
> +  size_t shortcut_nr = (nextp - readp) / 4;
> +
> +  if (unlikely (shortcut_nr != 2))
> +    goto invalid_data;

OK. So you just expect two entries.

> +  printf (_("\nShortcut table at offset %#" PRIx32 " contains %zu slots:\n"),
> +	  shortcut_off, shortcut_nr);

That is a fancy way to print "contains 2 slots" :)

> +  uint32_t lang = read_4ubyte_unaligned (dbg, readp);
> +  readp += 4;
> +
> +  printf (_("Language of main: %s\n"), dwarf_lang_name (lang));

Note that dwarf_lang_name calls string_or_unknown with false for 
print_unknown_num so it might make sense to either flip that to true
(but there is probably a reason it doesn't print the hex num) or to
always add the hex num after the name so the user doesn't just get ???
on an unknown DWARF_LANG constant.

> +  printf (_("Name of main: "));
> +
> +  if (lang != 0)
> +    {
> +      uint32_t name = read_4ubyte_unaligned (dbg, readp);
> +      readp += 4;
> +      const unsigned char *sym = const_start + name;
> +
> +      if (unlikely ((size_t) (dataend - const_start) < name
> +		    || memchr (sym, '\0', dataend - sym) == NULL))
> +	goto invalid_data;

Good end of string check.
BTW. DW_LANG constants are going away with DWARF6:
https://dwarfstd.org/languages-v6.html

> +      printf ("%s\n", sym);
> +    }
> +  else
> +    printf ("<unknown>\n");
>  }
>  
>  /* Returns true and sets split DWARF CU id if there is a split compile

OK

> diff --git a/tests/Makefile.am b/tests/Makefile.am
> index ef5b6bb5..e8bc9058 100644
> --- a/tests/Makefile.am
> +++ b/tests/Makefile.am
> @@ -421,7 +421,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
>  	     run-readelf-Dd.sh \
>  	     testfile-s390x-hash-both.bz2 \
>  	     run-readelf-gdb_index.sh testfilegdbindex5.bz2 \
> -	     testfilegdbindex7.bz2 \
> +	     testfilegdbindex7.bz2 testfilegdbindex9.bz2 \
> +	     testfilegdbindex9-no-maininfo.bz2 \
>  	     run-readelf-s.sh testfilebazdbg.bz2 testfilebazdyn.bz2 \
>  	     testfilebazmin.bz2 testfilebazdbg.debug.bz2 testfilebazmdb.bz2 \
>  	     testfilebaztab.bz2 testfilebasmin.bz2 testfilebaxmin.bz2 \
> diff --git a/tests/run-readelf-gdb_index.sh b/tests/run-readelf-gdb_index.sh
> index fcbc3c57..95367ef8 100755
> --- a/tests/run-readelf-gdb_index.sh
> +++ b/tests/run-readelf-gdb_index.sh
> @@ -63,7 +63,7 @@
>  # (gdb) save gdb-index .
>  # objcopy --add-section .gdb_index=testfilegdbindex7.gdb-index --set-section-flags .gdb_index=readonly testfilegdbindex7 testfilegdbindex7
>  
> -testfiles testfilegdbindex5 testfilegdbindex7
> +testfiles testfilegdbindex5 testfilegdbindex7 testfilegdbindex9 testfilegdbindex9-no-maininfo
>  
>  testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=gdb_index testfilegdbindex5 <<\EOF
>  
> @@ -127,4 +127,97 @@ GDB section [33] '.gdb_index' at offset 0xe76 contains 8399 bytes :
>   [ 754] symbol: int, CUs: 0 (type:S)
>  EOF
>  
> +# testfilegdbindex9-no-maininfo is built the same way as testfilegdbindex7.
> +testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=gdb_index testfilegdbindex9-no-maininfo <<\EOF
> +
> +GDB section [33] '.gdb_index' at offset 0x38e1 contains 8415 bytes :
> + Version:         9
> + CU offset:       0x1c
> + TU offset:       0x3c
> + address offset:  0x54
> + symbol offset:   0x7c
> + shortcut offset: 0x207c
> + constant offset: 0x2084
> +
> + CU list at offset 0x1c contains 2 entries:
> + [   0] start: 0x00004c, length:   220
> + [   1] start: 0x000128, length:   214
> +
> + TU list at offset 0x3c contains 1 entries:
> + [   0] CU offset:     0, type offset:    30, signature: 0x87e03f92cc37cdf0
> +
> + Address list at offset 0x54 contains 2 entries:
> + [   0] 0x0000000000401106 <main>..0x000000000040113b <main+0x35>, CU index:     1
> + [   1] 0x000000000040113c <hello>..0x0000000000401173 <say+0x1c>, CU index:     2
> +
> + Symbol table at offset 0x54 contains 1024 slots:
> + [ 123] symbol: global, CUs: 1 (var:G), 0T (var:G)
> + [ 489] symbol: main, CUs: 1 (func:G)
> + [ 518] symbol: char, CUs: 0 (type:S)
> + [ 661] symbol: foo, CUs: 0 (type:S)
> + [ 741] symbol: hello, CUs: 1 (var:S), 0T (func:S)
> + [ 746] symbol: say, CUs: 0T (func:G)
> + [ 754] symbol: int, CUs: 1 (type:S)
> +
> +Shortcut table at offset 0x207c contains 2 slots:
> +Language of main: ???
> +Name of main: <unknown>
> +EOF

This seems an unfortunate example. Why is the language unknown?
But maybe it is a nice example to show why you should at least print
the hex num of the language?

> +# testfilegdbindex9.f90
> +#
> +# program repro
> +#  type small_stride
> +#     character*40 long_string
> +#     integer      small_pad
> +#  end type small_stride
> +#  type(small_stride), dimension (20), target :: unpleasant
> +#  character*40, pointer, dimension(:):: c40pt
> +#  integer i
> +#  do i = 0,19
> +#     unpleasant(i+1)%small_pad = i+1
> +#     unpleasant(i+1)%long_string = char (ichar('0') + i)
> +#  end do
> +#  c40pt => unpleasant%long_string
> +#  print *, c40pt
> +#end program repro
> +
> +# gfortran -g -o testfilegdbindex9 testfilegdbindex9.f90
> +# gdb-add-index testfilegdbindex9
> +
> +testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=gdb_index testfilegdbindex9 <<\EOF
> +
> +GDB section [35] '.gdb_index' at offset 0x37d9 contains 8395 bytes :
> + Version:         9
> + CU offset:       0x1c
> + TU offset:       0x2c
> + address offset:  0x2c
> + symbol offset:   0x40
> + shortcut offset: 0x2040
> + constant offset: 0x2048
> +
> + CU list at offset 0x1c contains 1 entries:
> + [   0] start: 00000000, length:   307
> +
> + TU list at offset 0x2c contains 0 entries:
> +
> + Address list at offset 0x2c contains 1 entries:
> + [   0] 0x0000000000401166 <MAIN__>..0x00000000004013f0 <main+0x3a>, CU index:     0
> +
> + Symbol table at offset 0x2c contains 1024 slots:
> + [  61] symbol: small_stride, CUs: 0 (type:S)
> + [  71] symbol: integer(kind=8), CUs: 0 (type:S)
> + [ 161] symbol: character(kind=1), CUs: 0 (type:S)
> + [ 397] symbol: unpleasant, CUs: 0 (var:S)
> + [ 489] symbol: main, CUs: 0 (func:G)
> + [ 827] symbol: integer(kind=4), CUs: 0 (type:S)
> + [ 858] symbol: c40pt, CUs: 0 (var:S)
> + [ 965] symbol: repro, CUs: 0 (func:S)
> + [1016] symbol: i, CUs: 0 (var:S)
> +
> +Shortcut table at offset 0x2040 contains 2 slots:
> +Language of main: Fortran08
> +Name of main: repro
> +EOF

Nice.
  
Frank Ch. Eigler Nov. 2, 2023, 2:20 p.m. UTC | #2
Hi -

> OK. BTW the description of the gdb_index at the top
> https://sourceware.org/gdb/current/onlinedocs/gdb/Index-Section-Format.html
> doesn't resolve anymore. It is now 
> https://sourceware.org/gdb/current/onlinedocs/gdb.html/Index-Section-Format.html

Added another httpd redirect to make the first link work too.

- FChE
  
Aaron Merey Nov. 2, 2023, 4:30 p.m. UTC | #3
On Thu, Nov 2, 2023 at 10:20 AM Frank Ch. Eigler <fche@redhat.com> wrote:
>
> > BTW the description of the gdb_index at the top
> > https://sourceware.org/gdb/current/onlinedocs/gdb/Index-Section-Format.html
> > doesn't resolve anymore. It is now
> > https://sourceware.org/gdb/current/onlinedocs/gdb.html/Index-Section-Format.html
>
> Added another httpd redirect to make the first link work too.

Thanks Frank.

On Thu, Nov 2, 2023 at 8:54 AM Mark Wielaard <mark@klomp.org> wrote:
>
> > +  printf (_("\nShortcut table at offset %#" PRIx32 " contains %zu slots:\n"),
> > +       shortcut_off, shortcut_nr);
>
> That is a fancy way to print "contains 2 slots" :)

My intention was to require fewer changes to the code if the shortcut
table was extended in a future version.  But yes at the moment this is
slightly excessive!

>
> > +  uint32_t lang = read_4ubyte_unaligned (dbg, readp);
> > +  readp += 4;
> > +
> > +  printf (_("Language of main: %s\n"), dwarf_lang_name (lang));
>
> Note that dwarf_lang_name calls string_or_unknown with false for
> print_unknown_num so it might make sense to either flip that to true
> (but there is probably a reason it doesn't print the hex num) or to
> always add the hex num after the name so the user doesn't just get ???
> on an unknown DWARF_LANG constant.

Binutils readelf includes the language hex number for unknown languages
and we should too.  I will add the hex number to the output when the
language is unknown.

>
> > +  printf (_("Name of main: "));
> > +
> > +  if (lang != 0)
> > +    {
> > +      uint32_t name = read_4ubyte_unaligned (dbg, readp);
> > +      readp += 4;
> > +      const unsigned char *sym = const_start + name;
> > +
> > +      if (unlikely ((size_t) (dataend - const_start) < name
> > +                 || memchr (sym, '\0', dataend - sym) == NULL))
> > +     goto invalid_data;
>
> Good end of string check.
> BTW. DW_LANG constants are going away with DWARF6:
> https://dwarfstd.org/languages-v6.html

Interesting.  If gdb continues to support .gdb_index then we may end up
extending the shortcut table to include the new DW_AT_language_version.

> > +Shortcut table at offset 0x207c contains 2 slots:
> > +Language of main: ???
> > +Name of main: <unknown>
> > +EOF
>
> This seems an unfortunate example. Why is the language unknown?
> But maybe it is a nice example to show why you should at least print
> the hex num of the language?

Currently gdb only populates the shortcut table when it can find
DW_AT_main_subprogram or DW_AT_calling_convention with value
DW_CC_program.  gcc doesn't emit this for C/C++ programs so their
shortcut tables all have language and name set to 0.
See https://sourceware.org/bugzilla/show_bug.cgi?id=30996

I will change this testcase to include the hex number of the language.

Aaron
  

Patch

diff --git a/src/readelf.c b/src/readelf.c
index db31ad09..a28f6236 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -11539,8 +11539,9 @@  print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl,
   // hash used for generating the table.  Version 6 contains symbols
   // for inlined functions, older versions didn't.  Version 7 adds
   // symbol kinds.  Version 8 just indicates that it correctly includes
-  // TUs for symbols.
-  if (vers < 4 || vers > 8)
+  // TUs for symbols.  Version 9 adds shortcut table for information
+  // regarding the main function.
+  if (vers < 4 || vers > 9)
     {
       printf (_("  unknown version, cannot parse section\n"));
       return;
@@ -11578,6 +11579,17 @@  print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl,
   if (unlikely (readp + 4 > dataend))
     goto invalid_data;
 
+  uint32_t shortcut_off = 0;
+  if (vers >= 9)
+    {
+      shortcut_off = read_4ubyte_unaligned (dbg, readp);
+      printf (_(" shortcut offset: %#" PRIx32 "\n"), shortcut_off);
+
+      readp += 4;
+      if (unlikely (readp + 4 > dataend))
+	goto invalid_data;
+    }
+
   uint32_t const_off = read_4ubyte_unaligned (dbg, readp);
   printf (_(" constant offset: %#" PRIx32 "\n"), const_off);
 
@@ -11675,8 +11687,19 @@  print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl,
   if (const_off >= data->d_size)
     goto invalid_data;
 
+  const unsigned char *shortcut_start = NULL;
+  if (vers >= 9)
+    {
+      if (shortcut_off >= data->d_size)
+	goto invalid_data;
+
+      shortcut_start = data->d_buf + shortcut_off;
+      nextp = shortcut_start;
+    }
+  else
+    nextp = const_start;
+
   readp = data->d_buf + sym_off;
-  nextp = const_start;
   size_t sym_nr = (nextp - readp) / 8;
 
   printf (_("\n Symbol table at offset %#" PRIx32
@@ -11750,6 +11773,43 @@  print_gdb_index_section (Dwfl_Module *dwflmod, Ebl *ebl,
 	}
       n++;
     }
+
+  if (vers < 9)
+    return;
+
+  if (unlikely (shortcut_start == NULL))
+    goto invalid_data;
+
+  readp = shortcut_start;
+  nextp = const_start;
+  size_t shortcut_nr = (nextp - readp) / 4;
+
+  if (unlikely (shortcut_nr != 2))
+    goto invalid_data;
+
+  printf (_("\nShortcut table at offset %#" PRIx32 " contains %zu slots:\n"),
+	  shortcut_off, shortcut_nr);
+
+  uint32_t lang = read_4ubyte_unaligned (dbg, readp);
+  readp += 4;
+
+  printf (_("Language of main: %s\n"), dwarf_lang_name (lang));
+  printf (_("Name of main: "));
+
+  if (lang != 0)
+    {
+      uint32_t name = read_4ubyte_unaligned (dbg, readp);
+      readp += 4;
+      const unsigned char *sym = const_start + name;
+
+      if (unlikely ((size_t) (dataend - const_start) < name
+		    || memchr (sym, '\0', dataend - sym) == NULL))
+	goto invalid_data;
+
+      printf ("%s\n", sym);
+    }
+  else
+    printf ("<unknown>\n");
 }
 
 /* Returns true and sets split DWARF CU id if there is a split compile
diff --git a/tests/Makefile.am b/tests/Makefile.am
index ef5b6bb5..e8bc9058 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -421,7 +421,8 @@  EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \
 	     run-readelf-Dd.sh \
 	     testfile-s390x-hash-both.bz2 \
 	     run-readelf-gdb_index.sh testfilegdbindex5.bz2 \
-	     testfilegdbindex7.bz2 \
+	     testfilegdbindex7.bz2 testfilegdbindex9.bz2 \
+	     testfilegdbindex9-no-maininfo.bz2 \
 	     run-readelf-s.sh testfilebazdbg.bz2 testfilebazdyn.bz2 \
 	     testfilebazmin.bz2 testfilebazdbg.debug.bz2 testfilebazmdb.bz2 \
 	     testfilebaztab.bz2 testfilebasmin.bz2 testfilebaxmin.bz2 \
diff --git a/tests/run-readelf-gdb_index.sh b/tests/run-readelf-gdb_index.sh
index fcbc3c57..95367ef8 100755
--- a/tests/run-readelf-gdb_index.sh
+++ b/tests/run-readelf-gdb_index.sh
@@ -63,7 +63,7 @@ 
 # (gdb) save gdb-index .
 # objcopy --add-section .gdb_index=testfilegdbindex7.gdb-index --set-section-flags .gdb_index=readonly testfilegdbindex7 testfilegdbindex7
 
-testfiles testfilegdbindex5 testfilegdbindex7
+testfiles testfilegdbindex5 testfilegdbindex7 testfilegdbindex9 testfilegdbindex9-no-maininfo
 
 testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=gdb_index testfilegdbindex5 <<\EOF
 
@@ -127,4 +127,97 @@  GDB section [33] '.gdb_index' at offset 0xe76 contains 8399 bytes :
  [ 754] symbol: int, CUs: 0 (type:S)
 EOF
 
+# testfilegdbindex9-no-maininfo is built the same way as testfilegdbindex7.
+testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=gdb_index testfilegdbindex9-no-maininfo <<\EOF
+
+GDB section [33] '.gdb_index' at offset 0x38e1 contains 8415 bytes :
+ Version:         9
+ CU offset:       0x1c
+ TU offset:       0x3c
+ address offset:  0x54
+ symbol offset:   0x7c
+ shortcut offset: 0x207c
+ constant offset: 0x2084
+
+ CU list at offset 0x1c contains 2 entries:
+ [   0] start: 0x00004c, length:   220
+ [   1] start: 0x000128, length:   214
+
+ TU list at offset 0x3c contains 1 entries:
+ [   0] CU offset:     0, type offset:    30, signature: 0x87e03f92cc37cdf0
+
+ Address list at offset 0x54 contains 2 entries:
+ [   0] 0x0000000000401106 <main>..0x000000000040113b <main+0x35>, CU index:     1
+ [   1] 0x000000000040113c <hello>..0x0000000000401173 <say+0x1c>, CU index:     2
+
+ Symbol table at offset 0x54 contains 1024 slots:
+ [ 123] symbol: global, CUs: 1 (var:G), 0T (var:G)
+ [ 489] symbol: main, CUs: 1 (func:G)
+ [ 518] symbol: char, CUs: 0 (type:S)
+ [ 661] symbol: foo, CUs: 0 (type:S)
+ [ 741] symbol: hello, CUs: 1 (var:S), 0T (func:S)
+ [ 746] symbol: say, CUs: 0T (func:G)
+ [ 754] symbol: int, CUs: 1 (type:S)
+
+Shortcut table at offset 0x207c contains 2 slots:
+Language of main: ???
+Name of main: <unknown>
+EOF
+
+# testfilegdbindex9.f90
+#
+# program repro
+#  type small_stride
+#     character*40 long_string
+#     integer      small_pad
+#  end type small_stride
+#  type(small_stride), dimension (20), target :: unpleasant
+#  character*40, pointer, dimension(:):: c40pt
+#  integer i
+#  do i = 0,19
+#     unpleasant(i+1)%small_pad = i+1
+#     unpleasant(i+1)%long_string = char (ichar('0') + i)
+#  end do
+#  c40pt => unpleasant%long_string
+#  print *, c40pt
+#end program repro
+
+# gfortran -g -o testfilegdbindex9 testfilegdbindex9.f90
+# gdb-add-index testfilegdbindex9
+
+testrun_compare ${abs_top_builddir}/src/readelf --debug-dump=gdb_index testfilegdbindex9 <<\EOF
+
+GDB section [35] '.gdb_index' at offset 0x37d9 contains 8395 bytes :
+ Version:         9
+ CU offset:       0x1c
+ TU offset:       0x2c
+ address offset:  0x2c
+ symbol offset:   0x40
+ shortcut offset: 0x2040
+ constant offset: 0x2048
+
+ CU list at offset 0x1c contains 1 entries:
+ [   0] start: 00000000, length:   307
+
+ TU list at offset 0x2c contains 0 entries:
+
+ Address list at offset 0x2c contains 1 entries:
+ [   0] 0x0000000000401166 <MAIN__>..0x00000000004013f0 <main+0x3a>, CU index:     0
+
+ Symbol table at offset 0x2c contains 1024 slots:
+ [  61] symbol: small_stride, CUs: 0 (type:S)
+ [  71] symbol: integer(kind=8), CUs: 0 (type:S)
+ [ 161] symbol: character(kind=1), CUs: 0 (type:S)
+ [ 397] symbol: unpleasant, CUs: 0 (var:S)
+ [ 489] symbol: main, CUs: 0 (func:G)
+ [ 827] symbol: integer(kind=4), CUs: 0 (type:S)
+ [ 858] symbol: c40pt, CUs: 0 (var:S)
+ [ 965] symbol: repro, CUs: 0 (func:S)
+ [1016] symbol: i, CUs: 0 (var:S)
+
+Shortcut table at offset 0x2040 contains 2 slots:
+Language of main: Fortran08
+Name of main: repro
+EOF
+
 exit 0
diff --git a/tests/testfilegdbindex9-no-maininfo.bz2 b/tests/testfilegdbindex9-no-maininfo.bz2
new file mode 100755
index 0000000000000000000000000000000000000000..043b8d38df6c19365c39f2c6d23d1c49ff507d14
GIT binary patch
literal 3502
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

literal 0
HcmV?d00001