public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [RFC][PATCH?] fixed some segfaults and bugs in mdebug support
@ 2023-10-22  8:13 Zeck S
  2023-10-23  9:40 ` Andrew Burgess
  0 siblings, 1 reply; 15+ messages in thread
From: Zeck S @ 2023-10-22  8:13 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 3843 bytes --]

First off, I apologize if I'm doing this process wrong. I have sent an
email to assign@gnu.org trying to get the paperwork required for copyright
assignment. I think that's the correct thing to do?

While I wait on that, I'm not sure exactly what is required for these
changes.

Here's what I fixed in mdebug support.

info sym funcName would segfault
The first problem was that no compunit_symtab was set for the global_block
on blockvectors in  new_symtab. This caused a crash in block.c.
initialize_block_iterator called get_block_compunit_symtab and the
assertion gdb_assert (gb->compunit_symtab != NULL); would fail.

info types would segfault
The second problem was memory corruption. struct global_block is a larger
and different type from plain block and blockvector is expected to have
index 0 be a global_block struct. This can be seen done correctly in jit.c
near /* Now add the special blocks */ under if (i == GLOBAL_BLOCK). Failing
to allocate this correctly leads to crashes for me (usually) in
set_compunit_symtab where the assertion  gdb_assert (gb->compunit_symtab ==
NULL); would randomly fail. This fix is also in new_symtab.

info line file:line did not work
The third problem was finding lines never worked because add_line never set
.is_stmt to true, so in symtab.c find_line_common never saw item->is_stmt
as true, do it always went down the /* Ignore non-statements. */ path in
its main loop.

I looked in the gdb/testsuite directory, and I don't see a directory for
mips or mdebug? Unsure how to set up a test for this. To make files with
mdebug symbols, I used the old IRIX IDO compiler running under a kind of
qemu setup used by N64 game reverse engineering projects. (N64 dev is why
I'm interested in this symbol format. I can connect vscode to gdb and gdb
to an n64 emulator with a gdb stub to debug with symbols)

diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
index 4b0a1eb255f..9cb30ce0acd 100644
--- a/gdb/mdebugread.c
+++ b/gdb/mdebugread.c
@@ -239,9 +239,6 @@ enum block_type { FUNCTION_BLOCK, NON_FUNCTION_BLOCK };
 static struct block *new_block (struct objfile *objfile,
  enum block_type, enum language);

-static struct block *new_global_block (struct objfile *objfile,
- enum block_type, enum language);
-
 static struct compunit_symtab *new_symtab (const char *, int, struct
objfile *);

 static struct linetable *new_linetable (int);
@@ -4545,7 +4542,6 @@ add_line (struct linetable *lt, int lineno, CORE_ADDR
adr, int last)
     return lineno;

   lt->item[lt->nitems].line = lineno;
-  lt->item[lt->nitems].is_stmt = 1;
   lt->item[lt->nitems++].set_unrelocated_pc (unrelocated_addr (adr << 2));
   return lineno;
 }
@@ -4638,10 +4634,9 @@ new_symtab (const char *name, int maxlines, struct
objfile *objfile)

   /* All symtabs must have at least two blocks.  */
   bv = new_bvect (2);
-  bv->set_block (GLOBAL_BLOCK, new_global_block (objfile,
NON_FUNCTION_BLOCK, lang));
+  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
lang));
   bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
lang));
   bv->static_block ()->set_superblock (bv->global_block ());
-  bv->global_block ()->set_compunit_symtab(cust);
   cust->set_blockvector (bv);

   cust->set_debugformat ("ECOFF");
@@ -4740,21 +4735,6 @@ new_block (struct objfile *objfile, enum block_type
type,
   return retval;
 }

-static struct block *
-new_global_block (struct objfile *objfile, enum block_type type,
-   enum language language)
-{
-  struct block *retval = new (&objfile->objfile_obstack) global_block;
-
-  if (type == FUNCTION_BLOCK)
-    retval->set_multidict (mdict_create_linear_expandable (language));
-  else
-    retval->set_multidict (mdict_create_hashed_expandable (language));
-
-  return retval;
-}
-
-
 /* Create a new symbol with printname NAME.  */

 static struct symbol *

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH?] fixed some segfaults and bugs in mdebug support
  2023-10-22  8:13 [RFC][PATCH?] fixed some segfaults and bugs in mdebug support Zeck S
@ 2023-10-23  9:40 ` Andrew Burgess
  2023-10-24  0:25   ` Zeck S
  0 siblings, 1 reply; 15+ messages in thread
From: Andrew Burgess @ 2023-10-23  9:40 UTC (permalink / raw)
  To: Zeck S, gdb-patches

Zeck S <zeck654321@gmail.com> writes:

> First off, I apologize if I'm doing this process wrong. I have sent an
> email to assign@gnu.org trying to get the paperwork required for copyright
> assignment. I think that's the correct thing to do?
>
> While I wait on that, I'm not sure exactly what is required for these
> changes.
>
> Here's what I fixed in mdebug support.
>
> info sym funcName would segfault
> The first problem was that no compunit_symtab was set for the global_block
> on blockvectors in  new_symtab. This caused a crash in block.c.
> initialize_block_iterator called get_block_compunit_symtab and the
> assertion gdb_assert (gb->compunit_symtab != NULL); would fail.
>
> info types would segfault
> The second problem was memory corruption. struct global_block is a larger
> and different type from plain block and blockvector is expected to have
> index 0 be a global_block struct. This can be seen done correctly in jit.c
> near /* Now add the special blocks */ under if (i == GLOBAL_BLOCK). Failing
> to allocate this correctly leads to crashes for me (usually) in
> set_compunit_symtab where the assertion  gdb_assert (gb->compunit_symtab ==
> NULL); would randomly fail. This fix is also in new_symtab.
>
> info line file:line did not work
> The third problem was finding lines never worked because add_line never set
> .is_stmt to true, so in symtab.c find_line_common never saw item->is_stmt
> as true, do it always went down the /* Ignore non-statements. */ path in
> its main loop.

I was confused by this description as the only change I see is you
removing this line 'lt->item[lt->nitems].is_stmt = 1;' , but I suspect
you generated your diff the wrong way round.

You should consider creating your diff as a git commit, then use 'git
send-email' to send out patches, I found this site
https://git-send-email.io/ a pretty useful guide for setting up git &
email sending.

>
> I looked in the gdb/testsuite directory, and I don't see a directory for
> mips or mdebug? Unsure how to set up a test for this. To make files with
> mdebug symbols, I used the old IRIX IDO compiler running under a kind of
> qemu setup used by N64 game reverse engineering projects. (N64 dev is why
> I'm interested in this symbol format. I can connect vscode to gdb and gdb
> to an n64 emulator with a gdb stub to debug with symbols)

You might not need to add any new tests at all, IF you can identify some
existing tests that are fixed by your changes.

Most tests are not separated based on which compiler or environment is
used, though clearly there are exceptions, e.g. gdb.arch/*.exp does
contain some architecture specific tests.  Instead most tests are
written based on the GDB feature being tested.  For example,
gdb.base/infoline.exp tests the 'info line' command.

The expectation is that if someone has a more niche compiler or
environment then they will perform their own regression testing using
their setup.

So, hopefully, if you can get the GDB tests running using your
toolchain, then without your patch you'll see some failures in (maybe)
gdb.base/infoline.exp, and after your patch some of the failures would
be resolved, you'd then mention some (or all) of these improvements in
your commit message.

Of course, if your particular situation isn't covered by an existing
test then you might need to extend an existing test -- or create a new
test -- whatever seems most appropriate.

>
> diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
> index 4b0a1eb255f..9cb30ce0acd 100644
> --- a/gdb/mdebugread.c
> +++ b/gdb/mdebugread.c
> @@ -239,9 +239,6 @@ enum block_type { FUNCTION_BLOCK, NON_FUNCTION_BLOCK };
>  static struct block *new_block (struct objfile *objfile,
>   enum block_type, enum language);
>
> -static struct block *new_global_block (struct objfile *objfile,
> - enum block_type, enum language);
> -
>  static struct compunit_symtab *new_symtab (const char *, int, struct
> objfile *);
>
>  static struct linetable *new_linetable (int);
> @@ -4545,7 +4542,6 @@ add_line (struct linetable *lt, int lineno, CORE_ADDR
> adr, int last)
>      return lineno;
>
>    lt->item[lt->nitems].line = lineno;
> -  lt->item[lt->nitems].is_stmt = 1;
>    lt->item[lt->nitems++].set_unrelocated_pc (unrelocated_addr (adr << 2));
>    return lineno;
>  }
> @@ -4638,10 +4634,9 @@ new_symtab (const char *name, int maxlines, struct
> objfile *objfile)
>
>    /* All symtabs must have at least two blocks.  */
>    bv = new_bvect (2);
> -  bv->set_block (GLOBAL_BLOCK, new_global_block (objfile,
> NON_FUNCTION_BLOCK, lang));
> +  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
> lang));
>    bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
> lang));
>    bv->static_block ()->set_superblock (bv->global_block ());
> -  bv->global_block ()->set_compunit_symtab(cust);
>    cust->set_blockvector (bv);
>
>    cust->set_debugformat ("ECOFF");
> @@ -4740,21 +4735,6 @@ new_block (struct objfile *objfile, enum block_type
> type,
>    return retval;
>  }
>
> -static struct block *
> -new_global_block (struct objfile *objfile, enum block_type type,
> -   enum language language)

Static functions should have a comment before them.  In this case
something as simple as:

  /* Like new_block, but create a global_block.  */

Though I wonder if we could/should just give new_block an extra
parameter so its declaration becomes:

  static struct block *new_block (struct objfile *objfile,
                                  enum block_type, enum language,
                                  bool global_block = false);

Hopefully it's obvious how the new parameter would be used :)

Thanks,
Andrew


> -{
> -  struct block *retval = new (&objfile->objfile_obstack) global_block;
> -
> -  if (type == FUNCTION_BLOCK)
> -    retval->set_multidict (mdict_create_linear_expandable (language));
> -  else
> -    retval->set_multidict (mdict_create_hashed_expandable (language));
> -
> -  return retval;
> -}
> -
> -
>  /* Create a new symbol with printname NAME.  */
>
>  static struct symbol *


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH?] fixed some segfaults and bugs in mdebug support
  2023-10-23  9:40 ` Andrew Burgess
@ 2023-10-24  0:25   ` Zeck S
  2023-11-11  3:07     ` Zeck S
  0 siblings, 1 reply; 15+ messages in thread
From: Zeck S @ 2023-10-24  0:25 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 6803 bytes --]

Oh, I messed up the diff. Spoiled by github, never done it this way.
The testing makes sense now. Might be the weekend before I can make much
progress on that.
Thanks for the fast reply!

On Mon, Oct 23, 2023 at 4:40 AM Andrew Burgess <aburgess@redhat.com> wrote:

> Zeck S <zeck654321@gmail.com> writes:
>
> > First off, I apologize if I'm doing this process wrong. I have sent an
> > email to assign@gnu.org trying to get the paperwork required for
> copyright
> > assignment. I think that's the correct thing to do?
> >
> > While I wait on that, I'm not sure exactly what is required for these
> > changes.
> >
> > Here's what I fixed in mdebug support.
> >
> > info sym funcName would segfault
> > The first problem was that no compunit_symtab was set for the
> global_block
> > on blockvectors in  new_symtab. This caused a crash in block.c.
> > initialize_block_iterator called get_block_compunit_symtab and the
> > assertion gdb_assert (gb->compunit_symtab != NULL); would fail.
> >
> > info types would segfault
> > The second problem was memory corruption. struct global_block is a larger
> > and different type from plain block and blockvector is expected to have
> > index 0 be a global_block struct. This can be seen done correctly in
> jit.c
> > near /* Now add the special blocks */ under if (i == GLOBAL_BLOCK).
> Failing
> > to allocate this correctly leads to crashes for me (usually) in
> > set_compunit_symtab where the assertion  gdb_assert (gb->compunit_symtab
> ==
> > NULL); would randomly fail. This fix is also in new_symtab.
> >
> > info line file:line did not work
> > The third problem was finding lines never worked because add_line never
> set
> > .is_stmt to true, so in symtab.c find_line_common never saw item->is_stmt
> > as true, do it always went down the /* Ignore non-statements. */ path in
> > its main loop.
>
> I was confused by this description as the only change I see is you
> removing this line 'lt->item[lt->nitems].is_stmt = 1;' , but I suspect
> you generated your diff the wrong way round.
>
> You should consider creating your diff as a git commit, then use 'git
> send-email' to send out patches, I found this site
> https://git-send-email.io/ a pretty useful guide for setting up git &
> email sending.
>
> >
> > I looked in the gdb/testsuite directory, and I don't see a directory for
> > mips or mdebug? Unsure how to set up a test for this. To make files with
> > mdebug symbols, I used the old IRIX IDO compiler running under a kind of
> > qemu setup used by N64 game reverse engineering projects. (N64 dev is why
> > I'm interested in this symbol format. I can connect vscode to gdb and gdb
> > to an n64 emulator with a gdb stub to debug with symbols)
>
> You might not need to add any new tests at all, IF you can identify some
> existing tests that are fixed by your changes.
>
> Most tests are not separated based on which compiler or environment is
> used, though clearly there are exceptions, e.g. gdb.arch/*.exp does
> contain some architecture specific tests.  Instead most tests are
> written based on the GDB feature being tested.  For example,
> gdb.base/infoline.exp tests the 'info line' command.
>
> The expectation is that if someone has a more niche compiler or
> environment then they will perform their own regression testing using
> their setup.
>
> So, hopefully, if you can get the GDB tests running using your
> toolchain, then without your patch you'll see some failures in (maybe)
> gdb.base/infoline.exp, and after your patch some of the failures would
> be resolved, you'd then mention some (or all) of these improvements in
> your commit message.
>
> Of course, if your particular situation isn't covered by an existing
> test then you might need to extend an existing test -- or create a new
> test -- whatever seems most appropriate.
>
> >
> > diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
> > index 4b0a1eb255f..9cb30ce0acd 100644
> > --- a/gdb/mdebugread.c
> > +++ b/gdb/mdebugread.c
> > @@ -239,9 +239,6 @@ enum block_type { FUNCTION_BLOCK, NON_FUNCTION_BLOCK
> };
> >  static struct block *new_block (struct objfile *objfile,
> >   enum block_type, enum language);
> >
> > -static struct block *new_global_block (struct objfile *objfile,
> > - enum block_type, enum language);
> > -
> >  static struct compunit_symtab *new_symtab (const char *, int, struct
> > objfile *);
> >
> >  static struct linetable *new_linetable (int);
> > @@ -4545,7 +4542,6 @@ add_line (struct linetable *lt, int lineno,
> CORE_ADDR
> > adr, int last)
> >      return lineno;
> >
> >    lt->item[lt->nitems].line = lineno;
> > -  lt->item[lt->nitems].is_stmt = 1;
> >    lt->item[lt->nitems++].set_unrelocated_pc (unrelocated_addr (adr <<
> 2));
> >    return lineno;
> >  }
> > @@ -4638,10 +4634,9 @@ new_symtab (const char *name, int maxlines, struct
> > objfile *objfile)
> >
> >    /* All symtabs must have at least two blocks.  */
> >    bv = new_bvect (2);
> > -  bv->set_block (GLOBAL_BLOCK, new_global_block (objfile,
> > NON_FUNCTION_BLOCK, lang));
> > +  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
> > lang));
> >    bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
> > lang));
> >    bv->static_block ()->set_superblock (bv->global_block ());
> > -  bv->global_block ()->set_compunit_symtab(cust);
> >    cust->set_blockvector (bv);
> >
> >    cust->set_debugformat ("ECOFF");
> > @@ -4740,21 +4735,6 @@ new_block (struct objfile *objfile, enum
> block_type
> > type,
> >    return retval;
> >  }
> >
> > -static struct block *
> > -new_global_block (struct objfile *objfile, enum block_type type,
> > -   enum language language)
>
> Static functions should have a comment before them.  In this case
> something as simple as:
>
>   /* Like new_block, but create a global_block.  */
>
> Though I wonder if we could/should just give new_block an extra
> parameter so its declaration becomes:
>
>   static struct block *new_block (struct objfile *objfile,
>                                   enum block_type, enum language,
>                                   bool global_block = false);
>
> Hopefully it's obvious how the new parameter would be used :)
>
> Thanks,
> Andrew
>
>
> > -{
> > -  struct block *retval = new (&objfile->objfile_obstack) global_block;
> > -
> > -  if (type == FUNCTION_BLOCK)
> > -    retval->set_multidict (mdict_create_linear_expandable (language));
> > -  else
> > -    retval->set_multidict (mdict_create_hashed_expandable (language));
> > -
> > -  return retval;
> > -}
> > -
> > -
> >  /* Create a new symbol with printname NAME.  */
> >
> >  static struct symbol *
>
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH?] fixed some segfaults and bugs in mdebug support
  2023-10-24  0:25   ` Zeck S
@ 2023-11-11  3:07     ` Zeck S
  2023-12-04  3:36       ` Zeck S
  0 siblings, 1 reply; 15+ messages in thread
From: Zeck S @ 2023-11-11  3:07 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 7505 bytes --]

I'm still working on the testing. Been setting up QEMU stuff so I can run
linux mips code. Seems like the best option.
I think I should be able to get the test suite to call the IDO compiler and
use the linux mips linker.

Also, sent the signed paperwork to the FSF for copyright assignment, and am
waiting to hear back.

Been a few weeks, so, figured I'd post an update. Fixed one more small bug
I found as well.

On Mon, Oct 23, 2023 at 7:25 PM Zeck S <zeck654321@gmail.com> wrote:

> Oh, I messed up the diff. Spoiled by github, never done it this way.
> The testing makes sense now. Might be the weekend before I can make much
> progress on that.
> Thanks for the fast reply!
>
> On Mon, Oct 23, 2023 at 4:40 AM Andrew Burgess <aburgess@redhat.com>
> wrote:
>
>> Zeck S <zeck654321@gmail.com> writes:
>>
>> > First off, I apologize if I'm doing this process wrong. I have sent an
>> > email to assign@gnu.org trying to get the paperwork required for
>> copyright
>> > assignment. I think that's the correct thing to do?
>> >
>> > While I wait on that, I'm not sure exactly what is required for these
>> > changes.
>> >
>> > Here's what I fixed in mdebug support.
>> >
>> > info sym funcName would segfault
>> > The first problem was that no compunit_symtab was set for the
>> global_block
>> > on blockvectors in  new_symtab. This caused a crash in block.c.
>> > initialize_block_iterator called get_block_compunit_symtab and the
>> > assertion gdb_assert (gb->compunit_symtab != NULL); would fail.
>> >
>> > info types would segfault
>> > The second problem was memory corruption. struct global_block is a
>> larger
>> > and different type from plain block and blockvector is expected to have
>> > index 0 be a global_block struct. This can be seen done correctly in
>> jit.c
>> > near /* Now add the special blocks */ under if (i == GLOBAL_BLOCK).
>> Failing
>> > to allocate this correctly leads to crashes for me (usually) in
>> > set_compunit_symtab where the assertion  gdb_assert
>> (gb->compunit_symtab ==
>> > NULL); would randomly fail. This fix is also in new_symtab.
>> >
>> > info line file:line did not work
>> > The third problem was finding lines never worked because add_line never
>> set
>> > .is_stmt to true, so in symtab.c find_line_common never saw
>> item->is_stmt
>> > as true, do it always went down the /* Ignore non-statements. */ path in
>> > its main loop.
>>
>> I was confused by this description as the only change I see is you
>> removing this line 'lt->item[lt->nitems].is_stmt = 1;' , but I suspect
>> you generated your diff the wrong way round.
>>
>> You should consider creating your diff as a git commit, then use 'git
>> send-email' to send out patches, I found this site
>> https://git-send-email.io/ a pretty useful guide for setting up git &
>> email sending.
>>
>> >
>> > I looked in the gdb/testsuite directory, and I don't see a directory for
>> > mips or mdebug? Unsure how to set up a test for this. To make files with
>> > mdebug symbols, I used the old IRIX IDO compiler running under a kind of
>> > qemu setup used by N64 game reverse engineering projects. (N64 dev is
>> why
>> > I'm interested in this symbol format. I can connect vscode to gdb and
>> gdb
>> > to an n64 emulator with a gdb stub to debug with symbols)
>>
>> You might not need to add any new tests at all, IF you can identify some
>> existing tests that are fixed by your changes.
>>
>> Most tests are not separated based on which compiler or environment is
>> used, though clearly there are exceptions, e.g. gdb.arch/*.exp does
>> contain some architecture specific tests.  Instead most tests are
>> written based on the GDB feature being tested.  For example,
>> gdb.base/infoline.exp tests the 'info line' command.
>>
>> The expectation is that if someone has a more niche compiler or
>> environment then they will perform their own regression testing using
>> their setup.
>>
>> So, hopefully, if you can get the GDB tests running using your
>> toolchain, then without your patch you'll see some failures in (maybe)
>> gdb.base/infoline.exp, and after your patch some of the failures would
>> be resolved, you'd then mention some (or all) of these improvements in
>> your commit message.
>>
>> Of course, if your particular situation isn't covered by an existing
>> test then you might need to extend an existing test -- or create a new
>> test -- whatever seems most appropriate.
>>
>> >
>> > diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
>> > index 4b0a1eb255f..9cb30ce0acd 100644
>> > --- a/gdb/mdebugread.c
>> > +++ b/gdb/mdebugread.c
>> > @@ -239,9 +239,6 @@ enum block_type { FUNCTION_BLOCK,
>> NON_FUNCTION_BLOCK };
>> >  static struct block *new_block (struct objfile *objfile,
>> >   enum block_type, enum language);
>> >
>> > -static struct block *new_global_block (struct objfile *objfile,
>> > - enum block_type, enum language);
>> > -
>> >  static struct compunit_symtab *new_symtab (const char *, int, struct
>> > objfile *);
>> >
>> >  static struct linetable *new_linetable (int);
>> > @@ -4545,7 +4542,6 @@ add_line (struct linetable *lt, int lineno,
>> CORE_ADDR
>> > adr, int last)
>> >      return lineno;
>> >
>> >    lt->item[lt->nitems].line = lineno;
>> > -  lt->item[lt->nitems].is_stmt = 1;
>> >    lt->item[lt->nitems++].set_unrelocated_pc (unrelocated_addr (adr <<
>> 2));
>> >    return lineno;
>> >  }
>> > @@ -4638,10 +4634,9 @@ new_symtab (const char *name, int maxlines,
>> struct
>> > objfile *objfile)
>> >
>> >    /* All symtabs must have at least two blocks.  */
>> >    bv = new_bvect (2);
>> > -  bv->set_block (GLOBAL_BLOCK, new_global_block (objfile,
>> > NON_FUNCTION_BLOCK, lang));
>> > +  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
>> > lang));
>> >    bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
>> > lang));
>> >    bv->static_block ()->set_superblock (bv->global_block ());
>> > -  bv->global_block ()->set_compunit_symtab(cust);
>> >    cust->set_blockvector (bv);
>> >
>> >    cust->set_debugformat ("ECOFF");
>> > @@ -4740,21 +4735,6 @@ new_block (struct objfile *objfile, enum
>> block_type
>> > type,
>> >    return retval;
>> >  }
>> >
>> > -static struct block *
>> > -new_global_block (struct objfile *objfile, enum block_type type,
>> > -   enum language language)
>>
>> Static functions should have a comment before them.  In this case
>> something as simple as:
>>
>>   /* Like new_block, but create a global_block.  */
>>
>> Though I wonder if we could/should just give new_block an extra
>> parameter so its declaration becomes:
>>
>>   static struct block *new_block (struct objfile *objfile,
>>                                   enum block_type, enum language,
>>                                   bool global_block = false);
>>
>> Hopefully it's obvious how the new parameter would be used :)
>>
>> Thanks,
>> Andrew
>>
>>
>> > -{
>> > -  struct block *retval = new (&objfile->objfile_obstack) global_block;
>> > -
>> > -  if (type == FUNCTION_BLOCK)
>> > -    retval->set_multidict (mdict_create_linear_expandable (language));
>> > -  else
>> > -    retval->set_multidict (mdict_create_hashed_expandable (language));
>> > -
>> > -  return retval;
>> > -}
>> > -
>> > -
>> >  /* Create a new symbol with printname NAME.  */
>> >
>> >  static struct symbol *
>>
>>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH?] fixed some segfaults and bugs in mdebug support
  2023-11-11  3:07     ` Zeck S
@ 2023-12-04  3:36       ` Zeck S
  2023-12-11 11:42         ` Zeck S
  0 siblings, 1 reply; 15+ messages in thread
From: Zeck S @ 2023-12-04  3:36 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 8368 bytes --]

Not sure what etiquette is correct, but just so this thread isn't left
hanging without an update, after nearly a month:
I have created a QEMU user mode emulation board configuration, so that I
can test this patch.
https://sourceware.org/pipermail/gdb-patches/2023-December/204760.html

After that is resolved (or I am told of a better way),
I should be able to get some tests running by wrapping the compiler in a
script as suggested
at the bottom of this page
https://sourceware.org/gdb/wiki/Running%20the%20test%20suite%20with%20a%20non-GCC%20compiler

On Fri, Nov 10, 2023 at 9:07 PM Zeck S <zeck654321@gmail.com> wrote:

> I'm still working on the testing. Been setting up QEMU stuff so I can run
> linux mips code. Seems like the best option.
> I think I should be able to get the test suite to call the IDO compiler
> and use the linux mips linker.
>
> Also, sent the signed paperwork to the FSF for copyright assignment, and
> am waiting to hear back.
>
> Been a few weeks, so, figured I'd post an update. Fixed one more small bug
> I found as well.
>
> On Mon, Oct 23, 2023 at 7:25 PM Zeck S <zeck654321@gmail.com> wrote:
>
>> Oh, I messed up the diff. Spoiled by github, never done it this way.
>> The testing makes sense now. Might be the weekend before I can make much
>> progress on that.
>> Thanks for the fast reply!
>>
>> On Mon, Oct 23, 2023 at 4:40 AM Andrew Burgess <aburgess@redhat.com>
>> wrote:
>>
>>> Zeck S <zeck654321@gmail.com> writes:
>>>
>>> > First off, I apologize if I'm doing this process wrong. I have sent an
>>> > email to assign@gnu.org trying to get the paperwork required for
>>> copyright
>>> > assignment. I think that's the correct thing to do?
>>> >
>>> > While I wait on that, I'm not sure exactly what is required for these
>>> > changes.
>>> >
>>> > Here's what I fixed in mdebug support.
>>> >
>>> > info sym funcName would segfault
>>> > The first problem was that no compunit_symtab was set for the
>>> global_block
>>> > on blockvectors in  new_symtab. This caused a crash in block.c.
>>> > initialize_block_iterator called get_block_compunit_symtab and the
>>> > assertion gdb_assert (gb->compunit_symtab != NULL); would fail.
>>> >
>>> > info types would segfault
>>> > The second problem was memory corruption. struct global_block is a
>>> larger
>>> > and different type from plain block and blockvector is expected to have
>>> > index 0 be a global_block struct. This can be seen done correctly in
>>> jit.c
>>> > near /* Now add the special blocks */ under if (i == GLOBAL_BLOCK).
>>> Failing
>>> > to allocate this correctly leads to crashes for me (usually) in
>>> > set_compunit_symtab where the assertion  gdb_assert
>>> (gb->compunit_symtab ==
>>> > NULL); would randomly fail. This fix is also in new_symtab.
>>> >
>>> > info line file:line did not work
>>> > The third problem was finding lines never worked because add_line
>>> never set
>>> > .is_stmt to true, so in symtab.c find_line_common never saw
>>> item->is_stmt
>>> > as true, do it always went down the /* Ignore non-statements. */ path
>>> in
>>> > its main loop.
>>>
>>> I was confused by this description as the only change I see is you
>>> removing this line 'lt->item[lt->nitems].is_stmt = 1;' , but I suspect
>>> you generated your diff the wrong way round.
>>>
>>> You should consider creating your diff as a git commit, then use 'git
>>> send-email' to send out patches, I found this site
>>> https://git-send-email.io/ a pretty useful guide for setting up git &
>>> email sending.
>>>
>>> >
>>> > I looked in the gdb/testsuite directory, and I don't see a directory
>>> for
>>> > mips or mdebug? Unsure how to set up a test for this. To make files
>>> with
>>> > mdebug symbols, I used the old IRIX IDO compiler running under a kind
>>> of
>>> > qemu setup used by N64 game reverse engineering projects. (N64 dev is
>>> why
>>> > I'm interested in this symbol format. I can connect vscode to gdb and
>>> gdb
>>> > to an n64 emulator with a gdb stub to debug with symbols)
>>>
>>> You might not need to add any new tests at all, IF you can identify some
>>> existing tests that are fixed by your changes.
>>>
>>> Most tests are not separated based on which compiler or environment is
>>> used, though clearly there are exceptions, e.g. gdb.arch/*.exp does
>>> contain some architecture specific tests.  Instead most tests are
>>> written based on the GDB feature being tested.  For example,
>>> gdb.base/infoline.exp tests the 'info line' command.
>>>
>>> The expectation is that if someone has a more niche compiler or
>>> environment then they will perform their own regression testing using
>>> their setup.
>>>
>>> So, hopefully, if you can get the GDB tests running using your
>>> toolchain, then without your patch you'll see some failures in (maybe)
>>> gdb.base/infoline.exp, and after your patch some of the failures would
>>> be resolved, you'd then mention some (or all) of these improvements in
>>> your commit message.
>>>
>>> Of course, if your particular situation isn't covered by an existing
>>> test then you might need to extend an existing test -- or create a new
>>> test -- whatever seems most appropriate.
>>>
>>> >
>>> > diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
>>> > index 4b0a1eb255f..9cb30ce0acd 100644
>>> > --- a/gdb/mdebugread.c
>>> > +++ b/gdb/mdebugread.c
>>> > @@ -239,9 +239,6 @@ enum block_type { FUNCTION_BLOCK,
>>> NON_FUNCTION_BLOCK };
>>> >  static struct block *new_block (struct objfile *objfile,
>>> >   enum block_type, enum language);
>>> >
>>> > -static struct block *new_global_block (struct objfile *objfile,
>>> > - enum block_type, enum language);
>>> > -
>>> >  static struct compunit_symtab *new_symtab (const char *, int, struct
>>> > objfile *);
>>> >
>>> >  static struct linetable *new_linetable (int);
>>> > @@ -4545,7 +4542,6 @@ add_line (struct linetable *lt, int lineno,
>>> CORE_ADDR
>>> > adr, int last)
>>> >      return lineno;
>>> >
>>> >    lt->item[lt->nitems].line = lineno;
>>> > -  lt->item[lt->nitems].is_stmt = 1;
>>> >    lt->item[lt->nitems++].set_unrelocated_pc (unrelocated_addr (adr <<
>>> 2));
>>> >    return lineno;
>>> >  }
>>> > @@ -4638,10 +4634,9 @@ new_symtab (const char *name, int maxlines,
>>> struct
>>> > objfile *objfile)
>>> >
>>> >    /* All symtabs must have at least two blocks.  */
>>> >    bv = new_bvect (2);
>>> > -  bv->set_block (GLOBAL_BLOCK, new_global_block (objfile,
>>> > NON_FUNCTION_BLOCK, lang));
>>> > +  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
>>> > lang));
>>> >    bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
>>> > lang));
>>> >    bv->static_block ()->set_superblock (bv->global_block ());
>>> > -  bv->global_block ()->set_compunit_symtab(cust);
>>> >    cust->set_blockvector (bv);
>>> >
>>> >    cust->set_debugformat ("ECOFF");
>>> > @@ -4740,21 +4735,6 @@ new_block (struct objfile *objfile, enum
>>> block_type
>>> > type,
>>> >    return retval;
>>> >  }
>>> >
>>> > -static struct block *
>>> > -new_global_block (struct objfile *objfile, enum block_type type,
>>> > -   enum language language)
>>>
>>> Static functions should have a comment before them.  In this case
>>> something as simple as:
>>>
>>>   /* Like new_block, but create a global_block.  */
>>>
>>> Though I wonder if we could/should just give new_block an extra
>>> parameter so its declaration becomes:
>>>
>>>   static struct block *new_block (struct objfile *objfile,
>>>                                   enum block_type, enum language,
>>>                                   bool global_block = false);
>>>
>>> Hopefully it's obvious how the new parameter would be used :)
>>>
>>> Thanks,
>>> Andrew
>>>
>>>
>>> > -{
>>> > -  struct block *retval = new (&objfile->objfile_obstack) global_block;
>>> > -
>>> > -  if (type == FUNCTION_BLOCK)
>>> > -    retval->set_multidict (mdict_create_linear_expandable (language));
>>> > -  else
>>> > -    retval->set_multidict (mdict_create_hashed_expandable (language));
>>> > -
>>> > -  return retval;
>>> > -}
>>> > -
>>> > -
>>> >  /* Create a new symbol with printname NAME.  */
>>> >
>>> >  static struct symbol *
>>>
>>>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [RFC][PATCH?] fixed some segfaults and bugs in mdebug support
  2023-12-04  3:36       ` Zeck S
@ 2023-12-11 11:42         ` Zeck S
  2023-12-11 14:03           ` Andrew Burgess
  0 siblings, 1 reply; 15+ messages in thread
From: Zeck S @ 2023-12-11 11:42 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zeck S

I'm not quite sure what to do.
I have done some testing using the qemu-mips board I made.
There are a lot of limitations I'm running into.
I can just run as much of the testsuite as I can and see what happens.


Here are the main problems:
1. IDO only supports ansi c. This is a problem for some headers in glibc which use c99.
2. Test code sometimes has GCC features in them like asm()
3. Test expectations sometimes assume features from nicer formats like DWARF.

I knew I probably couldn't get standard library stuff working.
Even if I hack past the C99 issues (variadic macros),
there's also problems
with compiler specific files like stddef.h.

I started testing by just running gdb.base/info-types-c.exp alone,
since I knew I fixed bugs in that area, and the test didn't use stdio.h.
Found that the .c file used asm() which IDO doesn't support.
Commented that code out, ran the test. Failed.
Fixed a segfault that had been introduced since I started this project.
Still failed.
Fixed some typedef handling to get closer to the expected output.
However, the expected output includes line numbers for types.
mdebug format doesn't include line information for types.
There are also differences in output stemming from
mdebug assuming the debugger knows size information etc about "basic"
types, unlike dwarf, which includes information about them.

So at this point, I realize, I've modified the .c file for the test
which already seems not great, and I realize due to limitations of mdebug,
I can't ever make the test pass.
Not sure how to demonstrate this as progress, but uh, it feels like progress?


For the curious, this is the bash script I wrapped IDO with.
stderr is directed to dev null, because qemu-irix puts some non fatal
errors there, which confuses the test runner.
Put the script in a directory in PATH.
Passing an executable path as CC_FOR_TARGET does not work.
It must be the name of a file in PATH.

#!/bin/sh

echo "$@" >> theargs

for arg do
  if [ "$arg" = "-static" ]
  then
    link=1
  fi
done

if [ "$link" = 1 ]
then
  mips-unknown-linux-gnu-gcc "$@"
else
for arg do
  shift
  [ "$arg" = "-fdiagnostics-color=never" ] && continue
  [ "$arg" = "-g" ] && continue
  set -- "$@" "$arg"
done

/path/to/qemu-irix -L /path/to/ido/ido7.1_compiler /path/to/ido/ido7.1_compiler/usr/bin/cc -Xcpluscomm -nostdlib -nostdinc -mips2 -g2 "$@" 2>/dev/null
fi


---
 gdb/mdebugread.c | 89 ++++++++++++++++++++++++++++--------------------
 1 file changed, 52 insertions(+), 37 deletions(-)

diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
index cd6638224e7..191932e91cd 100644
--- a/gdb/mdebugread.c
+++ b/gdb/mdebugread.c
@@ -237,7 +237,7 @@ static struct type *new_type (char *);
 enum block_type { FUNCTION_BLOCK, NON_FUNCTION_BLOCK };
 
 static struct block *new_block (struct objfile *objfile,
-				enum block_type, enum language);
+				enum block_type, enum language, bool isGlobal);
 
 static struct compunit_symtab *new_symtab (const char *, int, struct objfile *);
 
@@ -246,7 +246,7 @@ static struct linetable *new_linetable (int);
 static struct blockvector *new_bvect (int);
 
 static struct type *parse_type (int, union aux_ext *, unsigned int, int *,
-				int, const char *);
+				int, const char *, bool);
 
 static struct symbol *mylookup_symbol (const char *, const struct block *,
 				       domain_enum, enum address_class);
@@ -572,7 +572,7 @@ add_data_symbol (SYMR *sh, union aux_ext *ax, int bigend,
       || sh->sc == scNil || sh->index == indexNil)
     s->set_type (builtin_type (objfile)->nodebug_data_symbol);
   else
-    s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name));
+    s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name, false));
   /* Value of a data symbol is its memory address.  */
 }
 
@@ -705,7 +705,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	  break;
 	}
       s->set_value_longest (svalue);
-      s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name));
+      s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name, false));
       add_symbol (s, top_stack->cur_st, top_stack->cur_block);
       break;
 
@@ -761,7 +761,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	t = builtin_type (objfile)->builtin_int;
       else
 	{
-	  t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name);
+	  t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name, false);
 	  if (strcmp (name, "malloc") == 0
 	      && t->code () == TYPE_CODE_VOID)
 	    {
@@ -805,7 +805,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	s->type ()->set_is_prototyped (true);
 
       /* Create and enter a new lexical context.  */
-      b = new_block (objfile, FUNCTION_BLOCK, s->language ());
+      b = new_block (objfile, FUNCTION_BLOCK, s->language (), false);
       s->set_value_block (b);
       b->set_function (s);
       b->set_start (sh->value);
@@ -1135,7 +1135,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	}
 
       top_stack->blocktype = stBlock;
-      b = new_block (objfile, NON_FUNCTION_BLOCK, psymtab_language);
+      b = new_block (objfile, NON_FUNCTION_BLOCK, psymtab_language, false);
       b->set_start (sh->value + top_stack->procadr);
       b->set_superblock (top_stack->cur_block);
       top_stack->cur_block = b;
@@ -1247,7 +1247,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	f->set_loc_bitpos (sh->value);
 	bitsize = 0;
 	f->set_type (parse_type (cur_fd, ax, sh->index, &bitsize, bigend,
-				 name));
+				 name, false));
 	f->set_bitsize (bitsize);
       }
       break;
@@ -1269,7 +1269,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
       pend = is_pending_symbol (cur_fdr, ext_sh);
       if (pend == NULL)
 	{
-	  t = parse_type (cur_fd, ax, sh->index, NULL, bigend, name);
+	  t = parse_type (cur_fd, ax, sh->index, NULL, bigend, name, true);
 	  add_pending (cur_fdr, ext_sh, t);
 	}
       else
@@ -1382,7 +1382,7 @@ basic_type (int bt, struct objfile *objfile)
   if (map_bt[bt])
     return map_bt[bt];
 
-  type_allocator alloc (objfile, get_current_subfile ()->language);
+  type_allocator alloc (objfile, psymtab_language);
 
   switch (bt)
     {
@@ -1514,7 +1514,7 @@ basic_type (int bt, struct objfile *objfile)
 
 static struct type *
 parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
-	    int bigend, const char *sym_name)
+	    int bigend, const char *sym_name, bool isStTypedef)
 {
   TIR t[1];
   struct type *tp = 0;
@@ -1571,7 +1571,7 @@ parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
 	}
     }
 
-  type_allocator alloc (mdebugread_objfile, get_current_subfile ()->language);
+  type_allocator alloc (mdebugread_objfile, psymtab_language);
 
   /* Move on to next aux.  */
   ax++;
@@ -1628,7 +1628,7 @@ parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
       xref_fh = get_rfd (fd, rf);
       xref_fd = xref_fh - debug_info->fdr;
       tp = parse_type (xref_fd, debug_info->external_aux + xref_fh->iauxBase,
-		    rn->index, NULL, xref_fh->fBigendian, sym_name);
+		    rn->index, NULL, xref_fh->fBigendian, sym_name, false);
     }
 
   /* All these types really point to some (common) MIPS type
@@ -1785,6 +1785,13 @@ parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
   if (t->continued)
     complaint (_("illegal TIR continued for %s"), sym_name);
 
+  if (isStTypedef)
+  {
+	struct type *wrap = alloc.new_type (TYPE_CODE_TYPEDEF, 0, sym_name);
+	wrap->set_target_type(tp);
+	tp = wrap;
+  }
+
   return tp;
 }
 
@@ -1839,7 +1846,7 @@ upgrade_type (int fd, struct type **tpp, int tq, union aux_ext *ax, int bigend,
 
       indx = parse_type (fh - debug_info->fdr,
 			 debug_info->external_aux + fh->iauxBase,
-			 id, NULL, bigend, sym_name);
+			 id, NULL, bigend, sym_name, false);
 
       /* The bounds type should be an integer type, but might be anything
 	 else due to corrupt aux entries.  */
@@ -2154,8 +2161,7 @@ parse_external (EXTR *es, int bigend, const section_offsets &section_offsets,
    with that and do not need to reorder our linetables.  */
 
 static void
-parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines,
-	     CORE_ADDR lowest_pdr_addr)
+parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines)
 {
   unsigned char *base;
   int j, k;
@@ -2169,7 +2175,6 @@ parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines,
   for (j = 0; j < fh->cpd; j++, pr++)
     {
       CORE_ADDR l;
-      CORE_ADDR adr;
       unsigned char *halt;
 
       /* No code for this one.  */
@@ -2186,9 +2191,7 @@ parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines,
 	halt = base + fh->cbLine;
       base += pr->cbLineOffset;
 
-      adr = pr->adr - lowest_pdr_addr;
-
-      l = adr >> 2;		/* in words */
+      l = pr->adr >> 2;		/* in words */
       for (lineno = pr->lnLow; base < halt;)
 	{
 	  count = *base & 0x0f;
@@ -4053,6 +4056,14 @@ mdebug_expand_psymtab (legacy_psymtab *pst, struct objfile *objfile)
 	    {
 	      (*swap_pdr_in) (cur_bfd, pdr_ptr, pdr_in);
 
+	      char *sym_ptr = (char *)(debug_info->external_sym) +
+	        (fh->isymBase + pdr_in->isym) * external_sym_size;
+
+	      SYMR sh;
+	      (*swap_sym_in) (cur_bfd, sym_ptr, &sh);
+
+	      pdr_in->adr = sh.value;
+
 	      /* Determine lowest PDR address, the PDRs are not always
 		 sorted.  */
 	      if (pdr_in == pr_block.data ())
@@ -4154,16 +4165,16 @@ mdebug_expand_psymtab (legacy_psymtab *pst, struct objfile *objfile)
 		{
 		  (*swap_pdr_in) (cur_bfd, pdr_ptr, pdr_in);
 
-		  /* Determine lowest PDR address, the PDRs are not always
-		     sorted.  */
-		  if (pdr_in == pr_block.data ())
-		    lowest_pdr_addr = pdr_in->adr;
-		  else if (pdr_in->adr < lowest_pdr_addr)
-		    lowest_pdr_addr = pdr_in->adr;
+                  sym_ptr = (char *)(debug_info->external_sym) +
+                    (fh->isymBase + pdr_in->isym) * external_sym_size;
+
+                  SYMR sh;
+                  (*swap_sym_in) (cur_bfd, sym_ptr, &sh);
+
+                  pdr_in->adr = sh.value;
 		}
 
-	      parse_lines (fh, pr_block.data (), lines, maxlines,
-			   lowest_pdr_addr);
+	      parse_lines (fh, pr_block.data (), lines, maxlines);
 	      if (lines->nitems < fh->cline)
 		lines = shrink_linetable (lines);
 
@@ -4291,7 +4302,7 @@ cross_ref (int fd, union aux_ext *ax, struct type **tpp,
       rf = rn->rfd;
     }
 
-  type_allocator alloc (mdebugread_objfile, get_current_subfile ()->language);
+  type_allocator alloc (mdebugread_objfile, psymtab_language);
 
   /* mips cc uses a rf of -1 for opaque struct definitions.
      Set TYPE_STUB for these types so that check_typedef will
@@ -4412,7 +4423,8 @@ cross_ref (int fd, union aux_ext *ax, struct type **tpp,
 				 sh.index,
 				 NULL,
 				 fh->fBigendian,
-				 debug_info->ss + fh->issBase + sh.iss);
+				 debug_info->ss + fh->issBase + sh.iss,
+				 sh.st == stTypedef);
 	      add_pending (fh, esh, *tpp);
 	      break;
 
@@ -4438,7 +4450,8 @@ cross_ref (int fd, union aux_ext *ax, struct type **tpp,
 			     sh.index,
 			     NULL,
 			     fh->fBigendian,
-			     debug_info->ss + fh->issBase + sh.iss);
+			     debug_info->ss + fh->issBase + sh.iss,
+			     true);
 	}
       else
 	{
@@ -4542,6 +4555,7 @@ add_line (struct linetable *lt, int lineno, CORE_ADDR adr, int last)
     return lineno;
 
   lt->item[lt->nitems].line = lineno;
+  lt->item[lt->nitems].is_stmt = 1;
   lt->item[lt->nitems++].set_unrelocated_pc (unrelocated_addr (adr << 2));
   return lineno;
 }
@@ -4634,9 +4648,10 @@ new_symtab (const char *name, int maxlines, struct objfile *objfile)
 
   /* All symtabs must have at least two blocks.  */
   bv = new_bvect (2);
-  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang));
-  bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang));
+  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang, true));
+  bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang, false));
   bv->static_block ()->set_superblock (bv->global_block ());
+  bv->global_block ()->set_compunit_symtab(cust);
   cust->set_blockvector (bv);
 
   cust->set_debugformat ("ECOFF");
@@ -4723,9 +4738,10 @@ new_bvect (int nblocks)
 
 static struct block *
 new_block (struct objfile *objfile, enum block_type type,
-	   enum language language)
+	   enum language language, bool isGlobal)
 {
-  struct block *retval = new (&objfile->objfile_obstack) block;
+  struct block *retval = isGlobal ? new (&objfile->objfile_obstack) global_block
+    : new (&objfile->objfile_obstack) block;
 
   if (type == FUNCTION_BLOCK)
     retval->set_multidict (mdict_create_linear_expandable (language));
@@ -4754,8 +4770,7 @@ new_type (char *name)
 {
   struct type *t;
 
-  t = type_allocator (mdebugread_objfile,
-		      get_current_subfile ()->language).new_type ();
+  t = type_allocator (mdebugread_objfile, psymtab_language).new_type ();
   t->set_name (name);
   INIT_CPLUS_SPECIFIC (t);
   return t;
-- 
2.41.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH?] fixed some segfaults and bugs in mdebug support
  2023-12-11 11:42         ` Zeck S
@ 2023-12-11 14:03           ` Andrew Burgess
  2023-12-11 14:48             ` Zeck S
  2023-12-15 19:27             ` Tom Tromey
  0 siblings, 2 replies; 15+ messages in thread
From: Andrew Burgess @ 2023-12-11 14:03 UTC (permalink / raw)
  To: Zeck S, gdb-patches; +Cc: Zeck S

Zeck S <zeck654321@gmail.com> writes:

> I'm not quite sure what to do.
> I have done some testing using the qemu-mips board I made.
> There are a lot of limitations I'm running into.
> I can just run as much of the testsuite as I can and see what happens.
>
>
> Here are the main problems:
> 1. IDO only supports ansi c. This is a problem for some headers in glibc which use c99.
> 2. Test code sometimes has GCC features in them like asm()
> 3. Test expectations sometimes assume features from nicer formats like DWARF.
>
> I knew I probably couldn't get standard library stuff working.
> Even if I hack past the C99 issues (variadic macros),
> there's also problems
> with compiler specific files like stddef.h.
>
> I started testing by just running gdb.base/info-types-c.exp alone,
> since I knew I fixed bugs in that area, and the test didn't use stdio.h.
> Found that the .c file used asm() which IDO doesn't support.
> Commented that code out, ran the test. Failed.
> Fixed a segfault that had been introduced since I started this project.
> Still failed.
> Fixed some typedef handling to get closer to the expected output.
> However, the expected output includes line numbers for types.
> mdebug format doesn't include line information for types.
> There are also differences in output stemming from
> mdebug assuming the debugger knows size information etc about "basic"
> types, unlike dwarf, which includes information about them.
>
> So at this point, I realize, I've modified the .c file for the test
> which already seems not great, and I realize due to limitations of mdebug,
> I can't ever make the test pass.
> Not sure how to demonstrate this as progress, but uh, it feels like
> progress?

So, I know nothing about targets that make use of ECOFF / mdebug format
stuff, and I don't really have time to try setting up a test
environment.

I guess my expectation for at least some of these fixes would be that
you could write a small sample program that does compile with your
compiler, and then a .exp file which triggers the right GDB commands to
expose the bug.

From what you've said, it doesn't sound like the existing test framework
actually has problems invoking the compiler and running the test, it's
just that the test has too many DWARF/ISO-C assumptions within.

Personally, I'm a huge fan of writing tests, I think it is worth the
effort in the long run, so I think it would be worth your time to try
and write some new tests for these issues.

That said, given the nature of these fixes and the difficulties you're
experiencing, if you don't feel that's possible, I'm not going to oppose
merging these fixes without tests...

... but I do have a couple of style issues that would need fixing first,
see inline below:

>
>
> For the curious, this is the bash script I wrapped IDO with.
> stderr is directed to dev null, because qemu-irix puts some non fatal
> errors there, which confuses the test runner.
> Put the script in a directory in PATH.
> Passing an executable path as CC_FOR_TARGET does not work.
> It must be the name of a file in PATH.
>
> #!/bin/sh
>
> echo "$@" >> theargs
>
> for arg do
>   if [ "$arg" = "-static" ]
>   then
>     link=1
>   fi
> done
>
> if [ "$link" = 1 ]
> then
>   mips-unknown-linux-gnu-gcc "$@"
> else
> for arg do
>   shift
>   [ "$arg" = "-fdiagnostics-color=never" ] && continue
>   [ "$arg" = "-g" ] && continue
>   set -- "$@" "$arg"
> done
>
> /path/to/qemu-irix -L /path/to/ido/ido7.1_compiler /path/to/ido/ido7.1_compiler/usr/bin/cc -Xcpluscomm -nostdlib -nostdinc -mips2 -g2 "$@" 2>/dev/null
> fi
>
>
> ---
>  gdb/mdebugread.c | 89 ++++++++++++++++++++++++++++--------------------
>  1 file changed, 52 insertions(+), 37 deletions(-)
>
> diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
> index cd6638224e7..191932e91cd 100644
> --- a/gdb/mdebugread.c
> +++ b/gdb/mdebugread.c
> @@ -237,7 +237,7 @@ static struct type *new_type (char *);
>  enum block_type { FUNCTION_BLOCK, NON_FUNCTION_BLOCK };
>  
>  static struct block *new_block (struct objfile *objfile,
> -				enum block_type, enum language);
> +				enum block_type, enum language, bool isGlobal);

GDB style is for snake_case rather than camelCase, so:
s/isGlobal/is_global/ throughout.

>  
>  static struct compunit_symtab *new_symtab (const char *, int, struct objfile *);
>  
> @@ -246,7 +246,7 @@ static struct linetable *new_linetable (int);
>  static struct blockvector *new_bvect (int);
>  
>  static struct type *parse_type (int, union aux_ext *, unsigned int, int *,
> -				int, const char *);
> +				int, const char *, bool);
>  
>  static struct symbol *mylookup_symbol (const char *, const struct block *,
>  				       domain_enum, enum address_class);
> @@ -572,7 +572,7 @@ add_data_symbol (SYMR *sh, union aux_ext *ax, int bigend,
>        || sh->sc == scNil || sh->index == indexNil)
>      s->set_type (builtin_type (objfile)->nodebug_data_symbol);
>    else
> -    s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name));
> +    s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name, false));
>    /* Value of a data symbol is its memory address.  */
>  }
>  
> @@ -705,7 +705,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
>  	  break;
>  	}
>        s->set_value_longest (svalue);
> -      s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name));
> +      s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name, false));
>        add_symbol (s, top_stack->cur_st, top_stack->cur_block);
>        break;
>  
> @@ -761,7 +761,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
>  	t = builtin_type (objfile)->builtin_int;
>        else
>  	{
> -	  t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name);
> +	  t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name, false);
>  	  if (strcmp (name, "malloc") == 0
>  	      && t->code () == TYPE_CODE_VOID)
>  	    {
> @@ -805,7 +805,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
>  	s->type ()->set_is_prototyped (true);
>  
>        /* Create and enter a new lexical context.  */
> -      b = new_block (objfile, FUNCTION_BLOCK, s->language ());
> +      b = new_block (objfile, FUNCTION_BLOCK, s->language (), false);
>        s->set_value_block (b);
>        b->set_function (s);
>        b->set_start (sh->value);
> @@ -1135,7 +1135,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
>  	}
>  
>        top_stack->blocktype = stBlock;
> -      b = new_block (objfile, NON_FUNCTION_BLOCK, psymtab_language);
> +      b = new_block (objfile, NON_FUNCTION_BLOCK, psymtab_language, false);
>        b->set_start (sh->value + top_stack->procadr);
>        b->set_superblock (top_stack->cur_block);
>        top_stack->cur_block = b;
> @@ -1247,7 +1247,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
>  	f->set_loc_bitpos (sh->value);
>  	bitsize = 0;
>  	f->set_type (parse_type (cur_fd, ax, sh->index, &bitsize, bigend,
> -				 name));
> +				 name, false));
>  	f->set_bitsize (bitsize);
>        }
>        break;
> @@ -1269,7 +1269,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
>        pend = is_pending_symbol (cur_fdr, ext_sh);
>        if (pend == NULL)
>  	{
> -	  t = parse_type (cur_fd, ax, sh->index, NULL, bigend, name);
> +	  t = parse_type (cur_fd, ax, sh->index, NULL, bigend, name, true);
>  	  add_pending (cur_fdr, ext_sh, t);
>  	}
>        else
> @@ -1382,7 +1382,7 @@ basic_type (int bt, struct objfile *objfile)
>    if (map_bt[bt])
>      return map_bt[bt];
>  
> -  type_allocator alloc (objfile, get_current_subfile ()->language);
> +  type_allocator alloc (objfile, psymtab_language);

With fixes like this, it can (I think) be useful to mention the commit
that is being fixed, in this case 76fc0f62138e.  Wouldn't be much, I'd
just say:

  Commit 76fc0f62138e added calls to 'get_current_subfile ()->language',
  but at the time this was known to be a bit of a guess, and untested.
  We should actually have used 'psymtab_language' because ....

Then, if in the future, someone looks at your patch and wonders why was
this change made?  There's a super quick way that can find the history
of this code, and understand the original change, and your update.

>  
>    switch (bt)
>      {
> @@ -1514,7 +1514,7 @@ basic_type (int bt, struct objfile *objfile)
>  
>  static struct type *
>  parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
> -	    int bigend, const char *sym_name)
> +	    int bigend, const char *sym_name, bool isStTypedef)

Switch to snake_case for isStTypedef.

>  {
>    TIR t[1];
>    struct type *tp = 0;
> @@ -1571,7 +1571,7 @@ parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
>  	}
>      }
>  
> -  type_allocator alloc (mdebugread_objfile, get_current_subfile ()->language);
> +  type_allocator alloc (mdebugread_objfile, psymtab_language);
>  
>    /* Move on to next aux.  */
>    ax++;
> @@ -1628,7 +1628,7 @@ parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
>        xref_fh = get_rfd (fd, rf);
>        xref_fd = xref_fh - debug_info->fdr;
>        tp = parse_type (xref_fd, debug_info->external_aux + xref_fh->iauxBase,
> -		    rn->index, NULL, xref_fh->fBigendian, sym_name);
> +		    rn->index, NULL, xref_fh->fBigendian, sym_name, false);
>      }
>  
>    /* All these types really point to some (common) MIPS type
> @@ -1785,6 +1785,13 @@ parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
>    if (t->continued)
>      complaint (_("illegal TIR continued for %s"), sym_name);
>  
> +  if (isStTypedef)
> +  {
> +	struct type *wrap = alloc.new_type (TYPE_CODE_TYPEDEF, 0, sym_name);
> +	wrap->set_target_type(tp);
> +	tp = wrap;
> +  }

GNU style indents the '{' and '}', this should look like:

  if (isStTypedef)
    {
      struct type *wrap = alloc.new_type (TYPE_CODE_TYPEDEF, 0, sym_name);
      wrap->set_target_type(tp);
      tp = wrap;
    }

> +
>    return tp;
>  }
>  
> @@ -1839,7 +1846,7 @@ upgrade_type (int fd, struct type **tpp, int tq, union aux_ext *ax, int bigend,
>  
>        indx = parse_type (fh - debug_info->fdr,
>  			 debug_info->external_aux + fh->iauxBase,
> -			 id, NULL, bigend, sym_name);
> +			 id, NULL, bigend, sym_name, false);
>  
>        /* The bounds type should be an integer type, but might be anything
>  	 else due to corrupt aux entries.  */
> @@ -2154,8 +2161,7 @@ parse_external (EXTR *es, int bigend, const section_offsets &section_offsets,
>     with that and do not need to reorder our linetables.  */
>  
>  static void
> -parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines,
> -	     CORE_ADDR lowest_pdr_addr)
> +parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines)

As above, this seems to be reverting parts of 1acc9dca423f78e4, it would
be great to more of an explanation for why that commit was wrong.

Remember, someone thought that commit was correct once.  You're looking
at their code and thinking they got it wrong.  What's to stop someone
looking at your code and thinking _you_ got it wrong?

Hopefully, what stops that is that you add more details in the commit
message, along with a link to the previous commit.

>  {
>    unsigned char *base;
>    int j, k;
> @@ -2169,7 +2175,6 @@ parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines,
>    for (j = 0; j < fh->cpd; j++, pr++)
>      {
>        CORE_ADDR l;
> -      CORE_ADDR adr;
>        unsigned char *halt;
>  
>        /* No code for this one.  */
> @@ -2186,9 +2191,7 @@ parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines,
>  	halt = base + fh->cbLine;
>        base += pr->cbLineOffset;
>  
> -      adr = pr->adr - lowest_pdr_addr;
> -
> -      l = adr >> 2;		/* in words */
> +      l = pr->adr >> 2;		/* in words */
>        for (lineno = pr->lnLow; base < halt;)
>  	{
>  	  count = *base & 0x0f;
> @@ -4053,6 +4056,14 @@ mdebug_expand_psymtab (legacy_psymtab *pst, struct objfile *objfile)
>  	    {
>  	      (*swap_pdr_in) (cur_bfd, pdr_ptr, pdr_in);
>  
> +	      char *sym_ptr = (char *)(debug_info->external_sym) +
> +	        (fh->isymBase + pdr_in->isym) * external_sym_size;

The layout seems wrong here.  There should for sure be a single space
after '(char *)', and the '+' should be at the start of the new line
rather than the end of the previous line.

Additionally, we usually wrap expressions like this within '( ... )' to
force editors to align things better.  Turns out that this pattern of
code is repeated lots in this file.  Search for: '* external_sym_size'
and you'll find lots of example for how to align this code.  I'd go
with:

	      char *sym_ptr = ((char *) debug_info->external_sym
			       + (fh->isymBase + pdr_in->isym)
			       * external_sym_size);

maybe?

> +
> +	      SYMR sh;
> +	      (*swap_sym_in) (cur_bfd, sym_ptr, &sh);
> +
> +	      pdr_in->adr = sh.value;
> +
>  	      /* Determine lowest PDR address, the PDRs are not always
>  		 sorted.  */
>  	      if (pdr_in == pr_block.data ())
> @@ -4154,16 +4165,16 @@ mdebug_expand_psymtab (legacy_psymtab *pst, struct objfile *objfile)
>  		{
>  		  (*swap_pdr_in) (cur_bfd, pdr_ptr, pdr_in);
>  
> -		  /* Determine lowest PDR address, the PDRs are not always
> -		     sorted.  */
> -		  if (pdr_in == pr_block.data ())
> -		    lowest_pdr_addr = pdr_in->adr;
> -		  else if (pdr_in->adr < lowest_pdr_addr)
> -		    lowest_pdr_addr = pdr_in->adr;
> +                  sym_ptr = (char *)(debug_info->external_sym) +
> +                    (fh->isymBase + pdr_in->isym) * external_sym_size;

Similar layout issues to above.

> +
> +                  SYMR sh;
> +                  (*swap_sym_in) (cur_bfd, sym_ptr, &sh);
> +
> +                  pdr_in->adr = sh.value;

These lines are all indented using only spaces.  GDB (unfortunately)
uses a hybrid tab and space approach for indentation.  If you 'git diff'
and look at this file, git _should_ highlight the whitespace issues for
you -- we have a .gitattributes file in the binutils-gdb root that asks
for whitespace issues to be highlighted.

>  		}
>  
> -	      parse_lines (fh, pr_block.data (), lines, maxlines,
> -			   lowest_pdr_addr);
> +	      parse_lines (fh, pr_block.data (), lines, maxlines);
>  	      if (lines->nitems < fh->cline)
>  		lines = shrink_linetable (lines);
>  
> @@ -4291,7 +4302,7 @@ cross_ref (int fd, union aux_ext *ax, struct type **tpp,
>        rf = rn->rfd;
>      }
>  
> -  type_allocator alloc (mdebugread_objfile, get_current_subfile ()->language);
> +  type_allocator alloc (mdebugread_objfile, psymtab_language);
>  
>    /* mips cc uses a rf of -1 for opaque struct definitions.
>       Set TYPE_STUB for these types so that check_typedef will
> @@ -4412,7 +4423,8 @@ cross_ref (int fd, union aux_ext *ax, struct type **tpp,
>  				 sh.index,
>  				 NULL,
>  				 fh->fBigendian,
> -				 debug_info->ss + fh->issBase + sh.iss);
> +				 debug_info->ss + fh->issBase + sh.iss,
> +				 sh.st == stTypedef);
>  	      add_pending (fh, esh, *tpp);
>  	      break;
>  
> @@ -4438,7 +4450,8 @@ cross_ref (int fd, union aux_ext *ax, struct type **tpp,
>  			     sh.index,
>  			     NULL,
>  			     fh->fBigendian,
> -			     debug_info->ss + fh->issBase + sh.iss);
> +			     debug_info->ss + fh->issBase + sh.iss,
> +			     true);
>  	}
>        else
>  	{
> @@ -4542,6 +4555,7 @@ add_line (struct linetable *lt, int lineno, CORE_ADDR adr, int last)
>      return lineno;
>  
>    lt->item[lt->nitems].line = lineno;
> +  lt->item[lt->nitems].is_stmt = 1;
>    lt->item[lt->nitems++].set_unrelocated_pc (unrelocated_addr (adr << 2));
>    return lineno;
>  }
> @@ -4634,9 +4648,10 @@ new_symtab (const char *name, int maxlines, struct objfile *objfile)
>  
>    /* All symtabs must have at least two blocks.  */
>    bv = new_bvect (2);
> -  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang));
> -  bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang));
> +  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang, true));
> +  bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang, false));
>    bv->static_block ()->set_superblock (bv->global_block ());
> +  bv->global_block ()->set_compunit_symtab(cust);
>    cust->set_blockvector (bv);
>  
>    cust->set_debugformat ("ECOFF");
> @@ -4723,9 +4738,10 @@ new_bvect (int nblocks)
>  
>  static struct block *
>  new_block (struct objfile *objfile, enum block_type type,
> -	   enum language language)
> +	   enum language language, bool isGlobal)
>  {
> -  struct block *retval = new (&objfile->objfile_obstack) block;
> +  struct block *retval = isGlobal ? new (&objfile->objfile_obstack) global_block
> +    : new (&objfile->objfile_obstack) block;

By the time you've s/isGlobal/is_global/ I think you should format this
as:

  struct block *retval = (is_global
			  ? new (&objfile->objfile_obstack) global_block
			  : new (&objfile->objfile_obstack) block);


Thanks,
Andrew


>  
>    if (type == FUNCTION_BLOCK)
>      retval->set_multidict (mdict_create_linear_expandable (language));
> @@ -4754,8 +4770,7 @@ new_type (char *name)
>  {
>    struct type *t;
>  
> -  t = type_allocator (mdebugread_objfile,
> -		      get_current_subfile ()->language).new_type ();
> +  t = type_allocator (mdebugread_objfile, psymtab_language).new_type ();
>    t->set_name (name);
>    INIT_CPLUS_SPECIFIC (t);
>    return t;
> -- 
> 2.41.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH?] fixed some segfaults and bugs in mdebug support
  2023-12-11 14:03           ` Andrew Burgess
@ 2023-12-11 14:48             ` Zeck S
  2023-12-15 19:26               ` Tom Tromey
  2023-12-18 15:50               ` Andrew Burgess
  2023-12-15 19:27             ` Tom Tromey
  1 sibling, 2 replies; 15+ messages in thread
From: Zeck S @ 2023-12-11 14:48 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

[-- Attachment #1: Type: text/plain, Size: 21045 bytes --]

I definitely would like to add some tests, or tweak some existing tests!
Just was not sure how normal that was, or if it would almost be viewed
"cheating" or something.
I really wanted to get the test suite running, which is why I made the
qemu-user board.
And it has been very useful, but legitimately some of the tests just can't
pass (or sometimes even build) with this nasty old compiler and symbol
format combination.
If I can make a few new tests I can work around all of this with relative
ease.

I'll address everything else as well!

I'm tempted to make some kind of container for making running tests with
this setup easier.
It's all a bit ridiculous, wrapping an old compiler and a modern linker in
a bash script, then the compiler runs in an emulator, the built executable
runs in another emulator...
If I do that I'll make it public somewhere.

Thanks for the feedback. I know this is a weird old format, but it is
actually becoming slightly more relevant again with the N64 game
decompilation projects.

On Mon, Dec 11, 2023 at 8:03 AM Andrew Burgess <aburgess@redhat.com> wrote:

> Zeck S <zeck654321@gmail.com> writes:
>
> > I'm not quite sure what to do.
> > I have done some testing using the qemu-mips board I made.
> > There are a lot of limitations I'm running into.
> > I can just run as much of the testsuite as I can and see what happens.
> >
> >
> > Here are the main problems:
> > 1. IDO only supports ansi c. This is a problem for some headers in glibc
> which use c99.
> > 2. Test code sometimes has GCC features in them like asm()
> > 3. Test expectations sometimes assume features from nicer formats like
> DWARF.
> >
> > I knew I probably couldn't get standard library stuff working.
> > Even if I hack past the C99 issues (variadic macros),
> > there's also problems
> > with compiler specific files like stddef.h.
> >
> > I started testing by just running gdb.base/info-types-c.exp alone,
> > since I knew I fixed bugs in that area, and the test didn't use stdio.h.
> > Found that the .c file used asm() which IDO doesn't support.
> > Commented that code out, ran the test. Failed.
> > Fixed a segfault that had been introduced since I started this project.
> > Still failed.
> > Fixed some typedef handling to get closer to the expected output.
> > However, the expected output includes line numbers for types.
> > mdebug format doesn't include line information for types.
> > There are also differences in output stemming from
> > mdebug assuming the debugger knows size information etc about "basic"
> > types, unlike dwarf, which includes information about them.
> >
> > So at this point, I realize, I've modified the .c file for the test
> > which already seems not great, and I realize due to limitations of
> mdebug,
> > I can't ever make the test pass.
> > Not sure how to demonstrate this as progress, but uh, it feels like
> > progress?
>
> So, I know nothing about targets that make use of ECOFF / mdebug format
> stuff, and I don't really have time to try setting up a test
> environment.
>
> I guess my expectation for at least some of these fixes would be that
> you could write a small sample program that does compile with your
> compiler, and then a .exp file which triggers the right GDB commands to
> expose the bug.
>
> From what you've said, it doesn't sound like the existing test framework
> actually has problems invoking the compiler and running the test, it's
> just that the test has too many DWARF/ISO-C assumptions within.
>
> Personally, I'm a huge fan of writing tests, I think it is worth the
> effort in the long run, so I think it would be worth your time to try
> and write some new tests for these issues.
>
> That said, given the nature of these fixes and the difficulties you're
> experiencing, if you don't feel that's possible, I'm not going to oppose
> merging these fixes without tests...
>
> ... but I do have a couple of style issues that would need fixing first,
> see inline below:
>
> >
> >
> > For the curious, this is the bash script I wrapped IDO with.
> > stderr is directed to dev null, because qemu-irix puts some non fatal
> > errors there, which confuses the test runner.
> > Put the script in a directory in PATH.
> > Passing an executable path as CC_FOR_TARGET does not work.
> > It must be the name of a file in PATH.
> >
> > #!/bin/sh
> >
> > echo "$@" >> theargs
> >
> > for arg do
> >   if [ "$arg" = "-static" ]
> >   then
> >     link=1
> >   fi
> > done
> >
> > if [ "$link" = 1 ]
> > then
> >   mips-unknown-linux-gnu-gcc "$@"
> > else
> > for arg do
> >   shift
> >   [ "$arg" = "-fdiagnostics-color=never" ] && continue
> >   [ "$arg" = "-g" ] && continue
> >   set -- "$@" "$arg"
> > done
> >
> > /path/to/qemu-irix -L /path/to/ido/ido7.1_compiler
> /path/to/ido/ido7.1_compiler/usr/bin/cc -Xcpluscomm -nostdlib -nostdinc
> -mips2 -g2 "$@" 2>/dev/null
> > fi
> >
> >
> > ---
> >  gdb/mdebugread.c | 89 ++++++++++++++++++++++++++++--------------------
> >  1 file changed, 52 insertions(+), 37 deletions(-)
> >
> > diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
> > index cd6638224e7..191932e91cd 100644
> > --- a/gdb/mdebugread.c
> > +++ b/gdb/mdebugread.c
> > @@ -237,7 +237,7 @@ static struct type *new_type (char *);
> >  enum block_type { FUNCTION_BLOCK, NON_FUNCTION_BLOCK };
> >
> >  static struct block *new_block (struct objfile *objfile,
> > -                             enum block_type, enum language);
> > +                             enum block_type, enum language, bool
> isGlobal);
>
> GDB style is for snake_case rather than camelCase, so:
> s/isGlobal/is_global/ throughout.
>
> >
> >  static struct compunit_symtab *new_symtab (const char *, int, struct
> objfile *);
> >
> > @@ -246,7 +246,7 @@ static struct linetable *new_linetable (int);
> >  static struct blockvector *new_bvect (int);
> >
> >  static struct type *parse_type (int, union aux_ext *, unsigned int, int
> *,
> > -                             int, const char *);
> > +                             int, const char *, bool);
> >
> >  static struct symbol *mylookup_symbol (const char *, const struct block
> *,
> >                                      domain_enum, enum address_class);
> > @@ -572,7 +572,7 @@ add_data_symbol (SYMR *sh, union aux_ext *ax, int
> bigend,
> >        || sh->sc == scNil || sh->index == indexNil)
> >      s->set_type (builtin_type (objfile)->nodebug_data_symbol);
> >    else
> > -    s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name));
> > +    s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name,
> false));
> >    /* Value of a data symbol is its memory address.  */
> >  }
> >
> > @@ -705,7 +705,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> *ext_sh, int bigend,
> >         break;
> >       }
> >        s->set_value_longest (svalue);
> > -      s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name));
> > +      s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name,
> false));
> >        add_symbol (s, top_stack->cur_st, top_stack->cur_block);
> >        break;
> >
> > @@ -761,7 +761,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> *ext_sh, int bigend,
> >       t = builtin_type (objfile)->builtin_int;
> >        else
> >       {
> > -       t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name);
> > +       t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name,
> false);
> >         if (strcmp (name, "malloc") == 0
> >             && t->code () == TYPE_CODE_VOID)
> >           {
> > @@ -805,7 +805,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> *ext_sh, int bigend,
> >       s->type ()->set_is_prototyped (true);
> >
> >        /* Create and enter a new lexical context.  */
> > -      b = new_block (objfile, FUNCTION_BLOCK, s->language ());
> > +      b = new_block (objfile, FUNCTION_BLOCK, s->language (), false);
> >        s->set_value_block (b);
> >        b->set_function (s);
> >        b->set_start (sh->value);
> > @@ -1135,7 +1135,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> *ext_sh, int bigend,
> >       }
> >
> >        top_stack->blocktype = stBlock;
> > -      b = new_block (objfile, NON_FUNCTION_BLOCK, psymtab_language);
> > +      b = new_block (objfile, NON_FUNCTION_BLOCK, psymtab_language,
> false);
> >        b->set_start (sh->value + top_stack->procadr);
> >        b->set_superblock (top_stack->cur_block);
> >        top_stack->cur_block = b;
> > @@ -1247,7 +1247,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> *ext_sh, int bigend,
> >       f->set_loc_bitpos (sh->value);
> >       bitsize = 0;
> >       f->set_type (parse_type (cur_fd, ax, sh->index, &bitsize, bigend,
> > -                              name));
> > +                              name, false));
> >       f->set_bitsize (bitsize);
> >        }
> >        break;
> > @@ -1269,7 +1269,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> *ext_sh, int bigend,
> >        pend = is_pending_symbol (cur_fdr, ext_sh);
> >        if (pend == NULL)
> >       {
> > -       t = parse_type (cur_fd, ax, sh->index, NULL, bigend, name);
> > +       t = parse_type (cur_fd, ax, sh->index, NULL, bigend, name, true);
> >         add_pending (cur_fdr, ext_sh, t);
> >       }
> >        else
> > @@ -1382,7 +1382,7 @@ basic_type (int bt, struct objfile *objfile)
> >    if (map_bt[bt])
> >      return map_bt[bt];
> >
> > -  type_allocator alloc (objfile, get_current_subfile ()->language);
> > +  type_allocator alloc (objfile, psymtab_language);
>
> With fixes like this, it can (I think) be useful to mention the commit
> that is being fixed, in this case 76fc0f62138e.  Wouldn't be much, I'd
> just say:
>
>   Commit 76fc0f62138e added calls to 'get_current_subfile ()->language',
>   but at the time this was known to be a bit of a guess, and untested.
>   We should actually have used 'psymtab_language' because ....
>
> Then, if in the future, someone looks at your patch and wonders why was
> this change made?  There's a super quick way that can find the history
> of this code, and understand the original change, and your update.
>
> >
> >    switch (bt)
> >      {
> > @@ -1514,7 +1514,7 @@ basic_type (int bt, struct objfile *objfile)
> >
> >  static struct type *
> >  parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
> > -         int bigend, const char *sym_name)
> > +         int bigend, const char *sym_name, bool isStTypedef)
>
> Switch to snake_case for isStTypedef.
>
> >  {
> >    TIR t[1];
> >    struct type *tp = 0;
> > @@ -1571,7 +1571,7 @@ parse_type (int fd, union aux_ext *ax, unsigned
> int aux_index, int *bs,
> >       }
> >      }
> >
> > -  type_allocator alloc (mdebugread_objfile, get_current_subfile
> ()->language);
> > +  type_allocator alloc (mdebugread_objfile, psymtab_language);
> >
> >    /* Move on to next aux.  */
> >    ax++;
> > @@ -1628,7 +1628,7 @@ parse_type (int fd, union aux_ext *ax, unsigned
> int aux_index, int *bs,
> >        xref_fh = get_rfd (fd, rf);
> >        xref_fd = xref_fh - debug_info->fdr;
> >        tp = parse_type (xref_fd, debug_info->external_aux +
> xref_fh->iauxBase,
> > -                 rn->index, NULL, xref_fh->fBigendian, sym_name);
> > +                 rn->index, NULL, xref_fh->fBigendian, sym_name, false);
> >      }
> >
> >    /* All these types really point to some (common) MIPS type
> > @@ -1785,6 +1785,13 @@ parse_type (int fd, union aux_ext *ax, unsigned
> int aux_index, int *bs,
> >    if (t->continued)
> >      complaint (_("illegal TIR continued for %s"), sym_name);
> >
> > +  if (isStTypedef)
> > +  {
> > +     struct type *wrap = alloc.new_type (TYPE_CODE_TYPEDEF, 0,
> sym_name);
> > +     wrap->set_target_type(tp);
> > +     tp = wrap;
> > +  }
>
> GNU style indents the '{' and '}', this should look like:
>
>   if (isStTypedef)
>     {
>       struct type *wrap = alloc.new_type (TYPE_CODE_TYPEDEF, 0, sym_name);
>       wrap->set_target_type(tp);
>       tp = wrap;
>     }
>
> > +
> >    return tp;
> >  }
> >
> > @@ -1839,7 +1846,7 @@ upgrade_type (int fd, struct type **tpp, int tq,
> union aux_ext *ax, int bigend,
> >
> >        indx = parse_type (fh - debug_info->fdr,
> >                        debug_info->external_aux + fh->iauxBase,
> > -                      id, NULL, bigend, sym_name);
> > +                      id, NULL, bigend, sym_name, false);
> >
> >        /* The bounds type should be an integer type, but might be
> anything
> >        else due to corrupt aux entries.  */
> > @@ -2154,8 +2161,7 @@ parse_external (EXTR *es, int bigend, const
> section_offsets &section_offsets,
> >     with that and do not need to reorder our linetables.  */
> >
> >  static void
> > -parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines,
> > -          CORE_ADDR lowest_pdr_addr)
> > +parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines)
>
> As above, this seems to be reverting parts of 1acc9dca423f78e4, it would
> be great to more of an explanation for why that commit was wrong.
>
> Remember, someone thought that commit was correct once.  You're looking
> at their code and thinking they got it wrong.  What's to stop someone
> looking at your code and thinking _you_ got it wrong?
>
> Hopefully, what stops that is that you add more details in the commit
> message, along with a link to the previous commit.
>
> >  {
> >    unsigned char *base;
> >    int j, k;
> > @@ -2169,7 +2175,6 @@ parse_lines (FDR *fh, PDR *pr, struct linetable
> *lt, int maxlines,
> >    for (j = 0; j < fh->cpd; j++, pr++)
> >      {
> >        CORE_ADDR l;
> > -      CORE_ADDR adr;
> >        unsigned char *halt;
> >
> >        /* No code for this one.  */
> > @@ -2186,9 +2191,7 @@ parse_lines (FDR *fh, PDR *pr, struct linetable
> *lt, int maxlines,
> >       halt = base + fh->cbLine;
> >        base += pr->cbLineOffset;
> >
> > -      adr = pr->adr - lowest_pdr_addr;
> > -
> > -      l = adr >> 2;          /* in words */
> > +      l = pr->adr >> 2;              /* in words */
> >        for (lineno = pr->lnLow; base < halt;)
> >       {
> >         count = *base & 0x0f;
> > @@ -4053,6 +4056,14 @@ mdebug_expand_psymtab (legacy_psymtab *pst,
> struct objfile *objfile)
> >           {
> >             (*swap_pdr_in) (cur_bfd, pdr_ptr, pdr_in);
> >
> > +           char *sym_ptr = (char *)(debug_info->external_sym) +
> > +             (fh->isymBase + pdr_in->isym) * external_sym_size;
>
> The layout seems wrong here.  There should for sure be a single space
> after '(char *)', and the '+' should be at the start of the new line
> rather than the end of the previous line.
>
> Additionally, we usually wrap expressions like this within '( ... )' to
> force editors to align things better.  Turns out that this pattern of
> code is repeated lots in this file.  Search for: '* external_sym_size'
> and you'll find lots of example for how to align this code.  I'd go
> with:
>
>               char *sym_ptr = ((char *) debug_info->external_sym
>                                + (fh->isymBase + pdr_in->isym)
>                                * external_sym_size);
>
> maybe?
>
> > +
> > +           SYMR sh;
> > +           (*swap_sym_in) (cur_bfd, sym_ptr, &sh);
> > +
> > +           pdr_in->adr = sh.value;
> > +
> >             /* Determine lowest PDR address, the PDRs are not always
> >                sorted.  */
> >             if (pdr_in == pr_block.data ())
> > @@ -4154,16 +4165,16 @@ mdebug_expand_psymtab (legacy_psymtab *pst,
> struct objfile *objfile)
> >               {
> >                 (*swap_pdr_in) (cur_bfd, pdr_ptr, pdr_in);
> >
> > -               /* Determine lowest PDR address, the PDRs are not always
> > -                  sorted.  */
> > -               if (pdr_in == pr_block.data ())
> > -                 lowest_pdr_addr = pdr_in->adr;
> > -               else if (pdr_in->adr < lowest_pdr_addr)
> > -                 lowest_pdr_addr = pdr_in->adr;
> > +                  sym_ptr = (char *)(debug_info->external_sym) +
> > +                    (fh->isymBase + pdr_in->isym) * external_sym_size;
>
> Similar layout issues to above.
>
> > +
> > +                  SYMR sh;
> > +                  (*swap_sym_in) (cur_bfd, sym_ptr, &sh);
> > +
> > +                  pdr_in->adr = sh.value;
>
> These lines are all indented using only spaces.  GDB (unfortunately)
> uses a hybrid tab and space approach for indentation.  If you 'git diff'
> and look at this file, git _should_ highlight the whitespace issues for
> you -- we have a .gitattributes file in the binutils-gdb root that asks
> for whitespace issues to be highlighted.
>
> >               }
> >
> > -           parse_lines (fh, pr_block.data (), lines, maxlines,
> > -                        lowest_pdr_addr);
> > +           parse_lines (fh, pr_block.data (), lines, maxlines);
> >             if (lines->nitems < fh->cline)
> >               lines = shrink_linetable (lines);
> >
> > @@ -4291,7 +4302,7 @@ cross_ref (int fd, union aux_ext *ax, struct type
> **tpp,
> >        rf = rn->rfd;
> >      }
> >
> > -  type_allocator alloc (mdebugread_objfile, get_current_subfile
> ()->language);
> > +  type_allocator alloc (mdebugread_objfile, psymtab_language);
> >
> >    /* mips cc uses a rf of -1 for opaque struct definitions.
> >       Set TYPE_STUB for these types so that check_typedef will
> > @@ -4412,7 +4423,8 @@ cross_ref (int fd, union aux_ext *ax, struct type
> **tpp,
> >                                sh.index,
> >                                NULL,
> >                                fh->fBigendian,
> > -                              debug_info->ss + fh->issBase + sh.iss);
> > +                              debug_info->ss + fh->issBase + sh.iss,
> > +                              sh.st == stTypedef);
> >             add_pending (fh, esh, *tpp);
> >             break;
> >
> > @@ -4438,7 +4450,8 @@ cross_ref (int fd, union aux_ext *ax, struct type
> **tpp,
> >                            sh.index,
> >                            NULL,
> >                            fh->fBigendian,
> > -                          debug_info->ss + fh->issBase + sh.iss);
> > +                          debug_info->ss + fh->issBase + sh.iss,
> > +                          true);
> >       }
> >        else
> >       {
> > @@ -4542,6 +4555,7 @@ add_line (struct linetable *lt, int lineno,
> CORE_ADDR adr, int last)
> >      return lineno;
> >
> >    lt->item[lt->nitems].line = lineno;
> > +  lt->item[lt->nitems].is_stmt = 1;
> >    lt->item[lt->nitems++].set_unrelocated_pc (unrelocated_addr (adr <<
> 2));
> >    return lineno;
> >  }
> > @@ -4634,9 +4648,10 @@ new_symtab (const char *name, int maxlines,
> struct objfile *objfile)
> >
> >    /* All symtabs must have at least two blocks.  */
> >    bv = new_bvect (2);
> > -  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
> lang));
> > -  bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
> lang));
> > +  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
> lang, true));
> > +  bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
> lang, false));
> >    bv->static_block ()->set_superblock (bv->global_block ());
> > +  bv->global_block ()->set_compunit_symtab(cust);
> >    cust->set_blockvector (bv);
> >
> >    cust->set_debugformat ("ECOFF");
> > @@ -4723,9 +4738,10 @@ new_bvect (int nblocks)
> >
> >  static struct block *
> >  new_block (struct objfile *objfile, enum block_type type,
> > -        enum language language)
> > +        enum language language, bool isGlobal)
> >  {
> > -  struct block *retval = new (&objfile->objfile_obstack) block;
> > +  struct block *retval = isGlobal ? new (&objfile->objfile_obstack)
> global_block
> > +    : new (&objfile->objfile_obstack) block;
>
> By the time you've s/isGlobal/is_global/ I think you should format this
> as:
>
>   struct block *retval = (is_global
>                           ? new (&objfile->objfile_obstack) global_block
>                           : new (&objfile->objfile_obstack) block);
>
>
> Thanks,
> Andrew
>
>
> >
> >    if (type == FUNCTION_BLOCK)
> >      retval->set_multidict (mdict_create_linear_expandable (language));
> > @@ -4754,8 +4770,7 @@ new_type (char *name)
> >  {
> >    struct type *t;
> >
> > -  t = type_allocator (mdebugread_objfile,
> > -                   get_current_subfile ()->language).new_type ();
> > +  t = type_allocator (mdebugread_objfile, psymtab_language).new_type ();
> >    t->set_name (name);
> >    INIT_CPLUS_SPECIFIC (t);
> >    return t;
> > --
> > 2.41.0
>
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH?] fixed some segfaults and bugs in mdebug support
  2023-12-11 14:48             ` Zeck S
@ 2023-12-15 19:26               ` Tom Tromey
  2023-12-18 15:50               ` Andrew Burgess
  1 sibling, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2023-12-15 19:26 UTC (permalink / raw)
  To: Zeck S; +Cc: Andrew Burgess, gdb-patches

>>>>> "Zeck" == Zeck S <zeck654321@gmail.com> writes:

Zeck> And it has been very useful, but legitimately some of the tests
Zeck> just can't pass (or sometimes even build) with this nasty old
Zeck> compiler and symbol format combination.

Depending on how much work you want to do on this, it's also possible to
arrange for tests to be skipped for your target.  E.g., many of the
gdb.dwarf2 tests start with:

    require dwarf2_support

dwarf2_support is just a proc in lib/dwarf.exp... if it returns 0, this
entire test will be skipped on your target.

Possibly some tests are missing some 'require' line.  And if your
compiler is old enough, probably a lot of C++ tests and the like will
either not compile or will generate unusable debuginfo.  These could be
patched to depend on a compiler or C++ version perhaps.

Tom

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH?] fixed some segfaults and bugs in mdebug support
  2023-12-11 14:03           ` Andrew Burgess
  2023-12-11 14:48             ` Zeck S
@ 2023-12-15 19:27             ` Tom Tromey
  1 sibling, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2023-12-15 19:27 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Zeck S, gdb-patches

>>>>> "Andrew" == Andrew Burgess <aburgess@redhat.com> writes:

Andrew> That said, given the nature of these fixes and the difficulties you're
Andrew> experiencing, if you don't feel that's possible, I'm not going to oppose
Andrew> merging these fixes without tests...

I tend to assume that essentially nobody else is using mdebugread at
all.  I was hoping to delete it, but if someone is using it, I think
it's also fine to keep it around on a "best effort" basis.

Tom

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH?] fixed some segfaults and bugs in mdebug support
  2023-12-11 14:48             ` Zeck S
  2023-12-15 19:26               ` Tom Tromey
@ 2023-12-18 15:50               ` Andrew Burgess
  2023-12-25  5:42                 ` Zeck S
  1 sibling, 1 reply; 15+ messages in thread
From: Andrew Burgess @ 2023-12-18 15:50 UTC (permalink / raw)
  To: Zeck S, gdb-patches

Zeck S <zeck654321@gmail.com> writes:

> I definitely would like to add some tests, or tweak some existing tests!
> Just was not sure how normal that was, or if it would almost be viewed
> "cheating" or something.

I don't think it would be considered cheating.  I'd just make sure to
place a comment at the head of the file explaining that the test is
deliberately simple in order to compile with XXX compiler and that
changes should be made with care.  This will pretty much guarantee
nobody else will dare change the test's source code :)

As Tom said, the mdebug stuff is not used much, so if you posted an
updated patch without any tests, I'd be happy to approve it.  It's your
choice.

Thanks,
Andrew


> I really wanted to get the test suite running, which is why I made the
> qemu-user board.
> And it has been very useful, but legitimately some of the tests just can't
> pass (or sometimes even build) with this nasty old compiler and symbol
> format combination.
> If I can make a few new tests I can work around all of this with relative
> ease.
>
> I'll address everything else as well!
>
> I'm tempted to make some kind of container for making running tests with
> this setup easier.
> It's all a bit ridiculous, wrapping an old compiler and a modern linker in
> a bash script, then the compiler runs in an emulator, the built executable
> runs in another emulator...
> If I do that I'll make it public somewhere.
>
> Thanks for the feedback. I know this is a weird old format, but it is
> actually becoming slightly more relevant again with the N64 game
> decompilation projects.
>
> On Mon, Dec 11, 2023 at 8:03 AM Andrew Burgess <aburgess@redhat.com> wrote:
>
>> Zeck S <zeck654321@gmail.com> writes:
>>
>> > I'm not quite sure what to do.
>> > I have done some testing using the qemu-mips board I made.
>> > There are a lot of limitations I'm running into.
>> > I can just run as much of the testsuite as I can and see what happens.
>> >
>> >
>> > Here are the main problems:
>> > 1. IDO only supports ansi c. This is a problem for some headers in glibc
>> which use c99.
>> > 2. Test code sometimes has GCC features in them like asm()
>> > 3. Test expectations sometimes assume features from nicer formats like
>> DWARF.
>> >
>> > I knew I probably couldn't get standard library stuff working.
>> > Even if I hack past the C99 issues (variadic macros),
>> > there's also problems
>> > with compiler specific files like stddef.h.
>> >
>> > I started testing by just running gdb.base/info-types-c.exp alone,
>> > since I knew I fixed bugs in that area, and the test didn't use stdio.h.
>> > Found that the .c file used asm() which IDO doesn't support.
>> > Commented that code out, ran the test. Failed.
>> > Fixed a segfault that had been introduced since I started this project.
>> > Still failed.
>> > Fixed some typedef handling to get closer to the expected output.
>> > However, the expected output includes line numbers for types.
>> > mdebug format doesn't include line information for types.
>> > There are also differences in output stemming from
>> > mdebug assuming the debugger knows size information etc about "basic"
>> > types, unlike dwarf, which includes information about them.
>> >
>> > So at this point, I realize, I've modified the .c file for the test
>> > which already seems not great, and I realize due to limitations of
>> mdebug,
>> > I can't ever make the test pass.
>> > Not sure how to demonstrate this as progress, but uh, it feels like
>> > progress?
>>
>> So, I know nothing about targets that make use of ECOFF / mdebug format
>> stuff, and I don't really have time to try setting up a test
>> environment.
>>
>> I guess my expectation for at least some of these fixes would be that
>> you could write a small sample program that does compile with your
>> compiler, and then a .exp file which triggers the right GDB commands to
>> expose the bug.
>>
>> From what you've said, it doesn't sound like the existing test framework
>> actually has problems invoking the compiler and running the test, it's
>> just that the test has too many DWARF/ISO-C assumptions within.
>>
>> Personally, I'm a huge fan of writing tests, I think it is worth the
>> effort in the long run, so I think it would be worth your time to try
>> and write some new tests for these issues.
>>
>> That said, given the nature of these fixes and the difficulties you're
>> experiencing, if you don't feel that's possible, I'm not going to oppose
>> merging these fixes without tests...
>>
>> ... but I do have a couple of style issues that would need fixing first,
>> see inline below:
>>
>> >
>> >
>> > For the curious, this is the bash script I wrapped IDO with.
>> > stderr is directed to dev null, because qemu-irix puts some non fatal
>> > errors there, which confuses the test runner.
>> > Put the script in a directory in PATH.
>> > Passing an executable path as CC_FOR_TARGET does not work.
>> > It must be the name of a file in PATH.
>> >
>> > #!/bin/sh
>> >
>> > echo "$@" >> theargs
>> >
>> > for arg do
>> >   if [ "$arg" = "-static" ]
>> >   then
>> >     link=1
>> >   fi
>> > done
>> >
>> > if [ "$link" = 1 ]
>> > then
>> >   mips-unknown-linux-gnu-gcc "$@"
>> > else
>> > for arg do
>> >   shift
>> >   [ "$arg" = "-fdiagnostics-color=never" ] && continue
>> >   [ "$arg" = "-g" ] && continue
>> >   set -- "$@" "$arg"
>> > done
>> >
>> > /path/to/qemu-irix -L /path/to/ido/ido7.1_compiler
>> /path/to/ido/ido7.1_compiler/usr/bin/cc -Xcpluscomm -nostdlib -nostdinc
>> -mips2 -g2 "$@" 2>/dev/null
>> > fi
>> >
>> >
>> > ---
>> >  gdb/mdebugread.c | 89 ++++++++++++++++++++++++++++--------------------
>> >  1 file changed, 52 insertions(+), 37 deletions(-)
>> >
>> > diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
>> > index cd6638224e7..191932e91cd 100644
>> > --- a/gdb/mdebugread.c
>> > +++ b/gdb/mdebugread.c
>> > @@ -237,7 +237,7 @@ static struct type *new_type (char *);
>> >  enum block_type { FUNCTION_BLOCK, NON_FUNCTION_BLOCK };
>> >
>> >  static struct block *new_block (struct objfile *objfile,
>> > -                             enum block_type, enum language);
>> > +                             enum block_type, enum language, bool
>> isGlobal);
>>
>> GDB style is for snake_case rather than camelCase, so:
>> s/isGlobal/is_global/ throughout.
>>
>> >
>> >  static struct compunit_symtab *new_symtab (const char *, int, struct
>> objfile *);
>> >
>> > @@ -246,7 +246,7 @@ static struct linetable *new_linetable (int);
>> >  static struct blockvector *new_bvect (int);
>> >
>> >  static struct type *parse_type (int, union aux_ext *, unsigned int, int
>> *,
>> > -                             int, const char *);
>> > +                             int, const char *, bool);
>> >
>> >  static struct symbol *mylookup_symbol (const char *, const struct block
>> *,
>> >                                      domain_enum, enum address_class);
>> > @@ -572,7 +572,7 @@ add_data_symbol (SYMR *sh, union aux_ext *ax, int
>> bigend,
>> >        || sh->sc == scNil || sh->index == indexNil)
>> >      s->set_type (builtin_type (objfile)->nodebug_data_symbol);
>> >    else
>> > -    s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name));
>> > +    s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name,
>> false));
>> >    /* Value of a data symbol is its memory address.  */
>> >  }
>> >
>> > @@ -705,7 +705,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
>> *ext_sh, int bigend,
>> >         break;
>> >       }
>> >        s->set_value_longest (svalue);
>> > -      s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name));
>> > +      s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name,
>> false));
>> >        add_symbol (s, top_stack->cur_st, top_stack->cur_block);
>> >        break;
>> >
>> > @@ -761,7 +761,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
>> *ext_sh, int bigend,
>> >       t = builtin_type (objfile)->builtin_int;
>> >        else
>> >       {
>> > -       t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name);
>> > +       t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name,
>> false);
>> >         if (strcmp (name, "malloc") == 0
>> >             && t->code () == TYPE_CODE_VOID)
>> >           {
>> > @@ -805,7 +805,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
>> *ext_sh, int bigend,
>> >       s->type ()->set_is_prototyped (true);
>> >
>> >        /* Create and enter a new lexical context.  */
>> > -      b = new_block (objfile, FUNCTION_BLOCK, s->language ());
>> > +      b = new_block (objfile, FUNCTION_BLOCK, s->language (), false);
>> >        s->set_value_block (b);
>> >        b->set_function (s);
>> >        b->set_start (sh->value);
>> > @@ -1135,7 +1135,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
>> *ext_sh, int bigend,
>> >       }
>> >
>> >        top_stack->blocktype = stBlock;
>> > -      b = new_block (objfile, NON_FUNCTION_BLOCK, psymtab_language);
>> > +      b = new_block (objfile, NON_FUNCTION_BLOCK, psymtab_language,
>> false);
>> >        b->set_start (sh->value + top_stack->procadr);
>> >        b->set_superblock (top_stack->cur_block);
>> >        top_stack->cur_block = b;
>> > @@ -1247,7 +1247,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
>> *ext_sh, int bigend,
>> >       f->set_loc_bitpos (sh->value);
>> >       bitsize = 0;
>> >       f->set_type (parse_type (cur_fd, ax, sh->index, &bitsize, bigend,
>> > -                              name));
>> > +                              name, false));
>> >       f->set_bitsize (bitsize);
>> >        }
>> >        break;
>> > @@ -1269,7 +1269,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
>> *ext_sh, int bigend,
>> >        pend = is_pending_symbol (cur_fdr, ext_sh);
>> >        if (pend == NULL)
>> >       {
>> > -       t = parse_type (cur_fd, ax, sh->index, NULL, bigend, name);
>> > +       t = parse_type (cur_fd, ax, sh->index, NULL, bigend, name, true);
>> >         add_pending (cur_fdr, ext_sh, t);
>> >       }
>> >        else
>> > @@ -1382,7 +1382,7 @@ basic_type (int bt, struct objfile *objfile)
>> >    if (map_bt[bt])
>> >      return map_bt[bt];
>> >
>> > -  type_allocator alloc (objfile, get_current_subfile ()->language);
>> > +  type_allocator alloc (objfile, psymtab_language);
>>
>> With fixes like this, it can (I think) be useful to mention the commit
>> that is being fixed, in this case 76fc0f62138e.  Wouldn't be much, I'd
>> just say:
>>
>>   Commit 76fc0f62138e added calls to 'get_current_subfile ()->language',
>>   but at the time this was known to be a bit of a guess, and untested.
>>   We should actually have used 'psymtab_language' because ....
>>
>> Then, if in the future, someone looks at your patch and wonders why was
>> this change made?  There's a super quick way that can find the history
>> of this code, and understand the original change, and your update.
>>
>> >
>> >    switch (bt)
>> >      {
>> > @@ -1514,7 +1514,7 @@ basic_type (int bt, struct objfile *objfile)
>> >
>> >  static struct type *
>> >  parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
>> > -         int bigend, const char *sym_name)
>> > +         int bigend, const char *sym_name, bool isStTypedef)
>>
>> Switch to snake_case for isStTypedef.
>>
>> >  {
>> >    TIR t[1];
>> >    struct type *tp = 0;
>> > @@ -1571,7 +1571,7 @@ parse_type (int fd, union aux_ext *ax, unsigned
>> int aux_index, int *bs,
>> >       }
>> >      }
>> >
>> > -  type_allocator alloc (mdebugread_objfile, get_current_subfile
>> ()->language);
>> > +  type_allocator alloc (mdebugread_objfile, psymtab_language);
>> >
>> >    /* Move on to next aux.  */
>> >    ax++;
>> > @@ -1628,7 +1628,7 @@ parse_type (int fd, union aux_ext *ax, unsigned
>> int aux_index, int *bs,
>> >        xref_fh = get_rfd (fd, rf);
>> >        xref_fd = xref_fh - debug_info->fdr;
>> >        tp = parse_type (xref_fd, debug_info->external_aux +
>> xref_fh->iauxBase,
>> > -                 rn->index, NULL, xref_fh->fBigendian, sym_name);
>> > +                 rn->index, NULL, xref_fh->fBigendian, sym_name, false);
>> >      }
>> >
>> >    /* All these types really point to some (common) MIPS type
>> > @@ -1785,6 +1785,13 @@ parse_type (int fd, union aux_ext *ax, unsigned
>> int aux_index, int *bs,
>> >    if (t->continued)
>> >      complaint (_("illegal TIR continued for %s"), sym_name);
>> >
>> > +  if (isStTypedef)
>> > +  {
>> > +     struct type *wrap = alloc.new_type (TYPE_CODE_TYPEDEF, 0,
>> sym_name);
>> > +     wrap->set_target_type(tp);
>> > +     tp = wrap;
>> > +  }
>>
>> GNU style indents the '{' and '}', this should look like:
>>
>>   if (isStTypedef)
>>     {
>>       struct type *wrap = alloc.new_type (TYPE_CODE_TYPEDEF, 0, sym_name);
>>       wrap->set_target_type(tp);
>>       tp = wrap;
>>     }
>>
>> > +
>> >    return tp;
>> >  }
>> >
>> > @@ -1839,7 +1846,7 @@ upgrade_type (int fd, struct type **tpp, int tq,
>> union aux_ext *ax, int bigend,
>> >
>> >        indx = parse_type (fh - debug_info->fdr,
>> >                        debug_info->external_aux + fh->iauxBase,
>> > -                      id, NULL, bigend, sym_name);
>> > +                      id, NULL, bigend, sym_name, false);
>> >
>> >        /* The bounds type should be an integer type, but might be
>> anything
>> >        else due to corrupt aux entries.  */
>> > @@ -2154,8 +2161,7 @@ parse_external (EXTR *es, int bigend, const
>> section_offsets &section_offsets,
>> >     with that and do not need to reorder our linetables.  */
>> >
>> >  static void
>> > -parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines,
>> > -          CORE_ADDR lowest_pdr_addr)
>> > +parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines)
>>
>> As above, this seems to be reverting parts of 1acc9dca423f78e4, it would
>> be great to more of an explanation for why that commit was wrong.
>>
>> Remember, someone thought that commit was correct once.  You're looking
>> at their code and thinking they got it wrong.  What's to stop someone
>> looking at your code and thinking _you_ got it wrong?
>>
>> Hopefully, what stops that is that you add more details in the commit
>> message, along with a link to the previous commit.
>>
>> >  {
>> >    unsigned char *base;
>> >    int j, k;
>> > @@ -2169,7 +2175,6 @@ parse_lines (FDR *fh, PDR *pr, struct linetable
>> *lt, int maxlines,
>> >    for (j = 0; j < fh->cpd; j++, pr++)
>> >      {
>> >        CORE_ADDR l;
>> > -      CORE_ADDR adr;
>> >        unsigned char *halt;
>> >
>> >        /* No code for this one.  */
>> > @@ -2186,9 +2191,7 @@ parse_lines (FDR *fh, PDR *pr, struct linetable
>> *lt, int maxlines,
>> >       halt = base + fh->cbLine;
>> >        base += pr->cbLineOffset;
>> >
>> > -      adr = pr->adr - lowest_pdr_addr;
>> > -
>> > -      l = adr >> 2;          /* in words */
>> > +      l = pr->adr >> 2;              /* in words */
>> >        for (lineno = pr->lnLow; base < halt;)
>> >       {
>> >         count = *base & 0x0f;
>> > @@ -4053,6 +4056,14 @@ mdebug_expand_psymtab (legacy_psymtab *pst,
>> struct objfile *objfile)
>> >           {
>> >             (*swap_pdr_in) (cur_bfd, pdr_ptr, pdr_in);
>> >
>> > +           char *sym_ptr = (char *)(debug_info->external_sym) +
>> > +             (fh->isymBase + pdr_in->isym) * external_sym_size;
>>
>> The layout seems wrong here.  There should for sure be a single space
>> after '(char *)', and the '+' should be at the start of the new line
>> rather than the end of the previous line.
>>
>> Additionally, we usually wrap expressions like this within '( ... )' to
>> force editors to align things better.  Turns out that this pattern of
>> code is repeated lots in this file.  Search for: '* external_sym_size'
>> and you'll find lots of example for how to align this code.  I'd go
>> with:
>>
>>               char *sym_ptr = ((char *) debug_info->external_sym
>>                                + (fh->isymBase + pdr_in->isym)
>>                                * external_sym_size);
>>
>> maybe?
>>
>> > +
>> > +           SYMR sh;
>> > +           (*swap_sym_in) (cur_bfd, sym_ptr, &sh);
>> > +
>> > +           pdr_in->adr = sh.value;
>> > +
>> >             /* Determine lowest PDR address, the PDRs are not always
>> >                sorted.  */
>> >             if (pdr_in == pr_block.data ())
>> > @@ -4154,16 +4165,16 @@ mdebug_expand_psymtab (legacy_psymtab *pst,
>> struct objfile *objfile)
>> >               {
>> >                 (*swap_pdr_in) (cur_bfd, pdr_ptr, pdr_in);
>> >
>> > -               /* Determine lowest PDR address, the PDRs are not always
>> > -                  sorted.  */
>> > -               if (pdr_in == pr_block.data ())
>> > -                 lowest_pdr_addr = pdr_in->adr;
>> > -               else if (pdr_in->adr < lowest_pdr_addr)
>> > -                 lowest_pdr_addr = pdr_in->adr;
>> > +                  sym_ptr = (char *)(debug_info->external_sym) +
>> > +                    (fh->isymBase + pdr_in->isym) * external_sym_size;
>>
>> Similar layout issues to above.
>>
>> > +
>> > +                  SYMR sh;
>> > +                  (*swap_sym_in) (cur_bfd, sym_ptr, &sh);
>> > +
>> > +                  pdr_in->adr = sh.value;
>>
>> These lines are all indented using only spaces.  GDB (unfortunately)
>> uses a hybrid tab and space approach for indentation.  If you 'git diff'
>> and look at this file, git _should_ highlight the whitespace issues for
>> you -- we have a .gitattributes file in the binutils-gdb root that asks
>> for whitespace issues to be highlighted.
>>
>> >               }
>> >
>> > -           parse_lines (fh, pr_block.data (), lines, maxlines,
>> > -                        lowest_pdr_addr);
>> > +           parse_lines (fh, pr_block.data (), lines, maxlines);
>> >             if (lines->nitems < fh->cline)
>> >               lines = shrink_linetable (lines);
>> >
>> > @@ -4291,7 +4302,7 @@ cross_ref (int fd, union aux_ext *ax, struct type
>> **tpp,
>> >        rf = rn->rfd;
>> >      }
>> >
>> > -  type_allocator alloc (mdebugread_objfile, get_current_subfile
>> ()->language);
>> > +  type_allocator alloc (mdebugread_objfile, psymtab_language);
>> >
>> >    /* mips cc uses a rf of -1 for opaque struct definitions.
>> >       Set TYPE_STUB for these types so that check_typedef will
>> > @@ -4412,7 +4423,8 @@ cross_ref (int fd, union aux_ext *ax, struct type
>> **tpp,
>> >                                sh.index,
>> >                                NULL,
>> >                                fh->fBigendian,
>> > -                              debug_info->ss + fh->issBase + sh.iss);
>> > +                              debug_info->ss + fh->issBase + sh.iss,
>> > +                              sh.st == stTypedef);
>> >             add_pending (fh, esh, *tpp);
>> >             break;
>> >
>> > @@ -4438,7 +4450,8 @@ cross_ref (int fd, union aux_ext *ax, struct type
>> **tpp,
>> >                            sh.index,
>> >                            NULL,
>> >                            fh->fBigendian,
>> > -                          debug_info->ss + fh->issBase + sh.iss);
>> > +                          debug_info->ss + fh->issBase + sh.iss,
>> > +                          true);
>> >       }
>> >        else
>> >       {
>> > @@ -4542,6 +4555,7 @@ add_line (struct linetable *lt, int lineno,
>> CORE_ADDR adr, int last)
>> >      return lineno;
>> >
>> >    lt->item[lt->nitems].line = lineno;
>> > +  lt->item[lt->nitems].is_stmt = 1;
>> >    lt->item[lt->nitems++].set_unrelocated_pc (unrelocated_addr (adr <<
>> 2));
>> >    return lineno;
>> >  }
>> > @@ -4634,9 +4648,10 @@ new_symtab (const char *name, int maxlines,
>> struct objfile *objfile)
>> >
>> >    /* All symtabs must have at least two blocks.  */
>> >    bv = new_bvect (2);
>> > -  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
>> lang));
>> > -  bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
>> lang));
>> > +  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
>> lang, true));
>> > +  bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
>> lang, false));
>> >    bv->static_block ()->set_superblock (bv->global_block ());
>> > +  bv->global_block ()->set_compunit_symtab(cust);
>> >    cust->set_blockvector (bv);
>> >
>> >    cust->set_debugformat ("ECOFF");
>> > @@ -4723,9 +4738,10 @@ new_bvect (int nblocks)
>> >
>> >  static struct block *
>> >  new_block (struct objfile *objfile, enum block_type type,
>> > -        enum language language)
>> > +        enum language language, bool isGlobal)
>> >  {
>> > -  struct block *retval = new (&objfile->objfile_obstack) block;
>> > +  struct block *retval = isGlobal ? new (&objfile->objfile_obstack)
>> global_block
>> > +    : new (&objfile->objfile_obstack) block;
>>
>> By the time you've s/isGlobal/is_global/ I think you should format this
>> as:
>>
>>   struct block *retval = (is_global
>>                           ? new (&objfile->objfile_obstack) global_block
>>                           : new (&objfile->objfile_obstack) block);
>>
>>
>> Thanks,
>> Andrew
>>
>>
>> >
>> >    if (type == FUNCTION_BLOCK)
>> >      retval->set_multidict (mdict_create_linear_expandable (language));
>> > @@ -4754,8 +4770,7 @@ new_type (char *name)
>> >  {
>> >    struct type *t;
>> >
>> > -  t = type_allocator (mdebugread_objfile,
>> > -                   get_current_subfile ()->language).new_type ();
>> > +  t = type_allocator (mdebugread_objfile, psymtab_language).new_type ();
>> >    t->set_name (name);
>> >    INIT_CPLUS_SPECIFIC (t);
>> >    return t;
>> > --
>> > 2.41.0
>>
>>


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH?] fixed some segfaults and bugs in mdebug support
  2023-12-18 15:50               ` Andrew Burgess
@ 2023-12-25  5:42                 ` Zeck S
  2024-02-07 13:33                   ` [PATCH] mdebug fix Zeck S
  2024-02-16  2:45                   ` [RFC][PATCH?] fixed some segfaults and bugs in mdebug support Zeck S
  0 siblings, 2 replies; 15+ messages in thread
From: Zeck S @ 2023-12-25  5:42 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 24901 bytes --]

Wanted to say, still working on this, would like to get some tests in.
Occupied a bit because of the time of year. Hoping to get something
together in January.

Also, really really appreciate that you maintainers are willing to keep
this code around at all, and have up to this point. I know it's very niche.
There are N64 (and PS2) reverse engineering projects, that either use this
part of GDB fairly directly, by gdb stubs in the emulator,
or referenced its code when trying to parse symbols either mistakenly
shipped with old games, or produced from old compilers when trying to make
matching binaries.
I'm not particularly involved in the scene, beyond observing, but I gather
some other people have patched things in this code privately, or are using
old GDB versions that worked.
Started working on this project because I was doing some N64 reversing.
Seemed like a waste to not just go the extra mile to get the fixes shared,
if possible.

For an example of its use, the Ares N64 emulator just got gdb stub support
this November, and it's possible to use an ELF file from a build of the
Super Mario 64 decompilation project to debug the original ROM, essentially
perfectly, with views of threads and everything else, after patching GDB.
When I started on this, I'd just finished writing a very basic stub for the
Mupen64plus emulator (turned out to be redundant, I'd somehow missed some
developer forks that had one).


On Mon, Dec 18, 2023 at 10:50 AM Andrew Burgess <aburgess@redhat.com> wrote:

> Zeck S <zeck654321@gmail.com> writes:
>
> > I definitely would like to add some tests, or tweak some existing tests!
> > Just was not sure how normal that was, or if it would almost be viewed
> > "cheating" or something.
>
> I don't think it would be considered cheating.  I'd just make sure to
> place a comment at the head of the file explaining that the test is
> deliberately simple in order to compile with XXX compiler and that
> changes should be made with care.  This will pretty much guarantee
> nobody else will dare change the test's source code :)
>
> As Tom said, the mdebug stuff is not used much, so if you posted an
> updated patch without any tests, I'd be happy to approve it.  It's your
> choice.
>
> Thanks,
> Andrew
>
>
> > I really wanted to get the test suite running, which is why I made the
> > qemu-user board.
> > And it has been very useful, but legitimately some of the tests just
> can't
> > pass (or sometimes even build) with this nasty old compiler and symbol
> > format combination.
> > If I can make a few new tests I can work around all of this with relative
> > ease.
> >
> > I'll address everything else as well!
> >
> > I'm tempted to make some kind of container for making running tests with
> > this setup easier.
> > It's all a bit ridiculous, wrapping an old compiler and a modern linker
> in
> > a bash script, then the compiler runs in an emulator, the built
> executable
> > runs in another emulator...
> > If I do that I'll make it public somewhere.
> >
> > Thanks for the feedback. I know this is a weird old format, but it is
> > actually becoming slightly more relevant again with the N64 game
> > decompilation projects.
> >
> > On Mon, Dec 11, 2023 at 8:03 AM Andrew Burgess <aburgess@redhat.com>
> wrote:
> >
> >> Zeck S <zeck654321@gmail.com> writes:
> >>
> >> > I'm not quite sure what to do.
> >> > I have done some testing using the qemu-mips board I made.
> >> > There are a lot of limitations I'm running into.
> >> > I can just run as much of the testsuite as I can and see what happens.
> >> >
> >> >
> >> > Here are the main problems:
> >> > 1. IDO only supports ansi c. This is a problem for some headers in
> glibc
> >> which use c99.
> >> > 2. Test code sometimes has GCC features in them like asm()
> >> > 3. Test expectations sometimes assume features from nicer formats like
> >> DWARF.
> >> >
> >> > I knew I probably couldn't get standard library stuff working.
> >> > Even if I hack past the C99 issues (variadic macros),
> >> > there's also problems
> >> > with compiler specific files like stddef.h.
> >> >
> >> > I started testing by just running gdb.base/info-types-c.exp alone,
> >> > since I knew I fixed bugs in that area, and the test didn't use
> stdio.h.
> >> > Found that the .c file used asm() which IDO doesn't support.
> >> > Commented that code out, ran the test. Failed.
> >> > Fixed a segfault that had been introduced since I started this
> project.
> >> > Still failed.
> >> > Fixed some typedef handling to get closer to the expected output.
> >> > However, the expected output includes line numbers for types.
> >> > mdebug format doesn't include line information for types.
> >> > There are also differences in output stemming from
> >> > mdebug assuming the debugger knows size information etc about "basic"
> >> > types, unlike dwarf, which includes information about them.
> >> >
> >> > So at this point, I realize, I've modified the .c file for the test
> >> > which already seems not great, and I realize due to limitations of
> >> mdebug,
> >> > I can't ever make the test pass.
> >> > Not sure how to demonstrate this as progress, but uh, it feels like
> >> > progress?
> >>
> >> So, I know nothing about targets that make use of ECOFF / mdebug format
> >> stuff, and I don't really have time to try setting up a test
> >> environment.
> >>
> >> I guess my expectation for at least some of these fixes would be that
> >> you could write a small sample program that does compile with your
> >> compiler, and then a .exp file which triggers the right GDB commands to
> >> expose the bug.
> >>
> >> From what you've said, it doesn't sound like the existing test framework
> >> actually has problems invoking the compiler and running the test, it's
> >> just that the test has too many DWARF/ISO-C assumptions within.
> >>
> >> Personally, I'm a huge fan of writing tests, I think it is worth the
> >> effort in the long run, so I think it would be worth your time to try
> >> and write some new tests for these issues.
> >>
> >> That said, given the nature of these fixes and the difficulties you're
> >> experiencing, if you don't feel that's possible, I'm not going to oppose
> >> merging these fixes without tests...
> >>
> >> ... but I do have a couple of style issues that would need fixing first,
> >> see inline below:
> >>
> >> >
> >> >
> >> > For the curious, this is the bash script I wrapped IDO with.
> >> > stderr is directed to dev null, because qemu-irix puts some non fatal
> >> > errors there, which confuses the test runner.
> >> > Put the script in a directory in PATH.
> >> > Passing an executable path as CC_FOR_TARGET does not work.
> >> > It must be the name of a file in PATH.
> >> >
> >> > #!/bin/sh
> >> >
> >> > echo "$@" >> theargs
> >> >
> >> > for arg do
> >> >   if [ "$arg" = "-static" ]
> >> >   then
> >> >     link=1
> >> >   fi
> >> > done
> >> >
> >> > if [ "$link" = 1 ]
> >> > then
> >> >   mips-unknown-linux-gnu-gcc "$@"
> >> > else
> >> > for arg do
> >> >   shift
> >> >   [ "$arg" = "-fdiagnostics-color=never" ] && continue
> >> >   [ "$arg" = "-g" ] && continue
> >> >   set -- "$@" "$arg"
> >> > done
> >> >
> >> > /path/to/qemu-irix -L /path/to/ido/ido7.1_compiler
> >> /path/to/ido/ido7.1_compiler/usr/bin/cc -Xcpluscomm -nostdlib -nostdinc
> >> -mips2 -g2 "$@" 2>/dev/null
> >> > fi
> >> >
> >> >
> >> > ---
> >> >  gdb/mdebugread.c | 89
> ++++++++++++++++++++++++++++--------------------
> >> >  1 file changed, 52 insertions(+), 37 deletions(-)
> >> >
> >> > diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
> >> > index cd6638224e7..191932e91cd 100644
> >> > --- a/gdb/mdebugread.c
> >> > +++ b/gdb/mdebugread.c
> >> > @@ -237,7 +237,7 @@ static struct type *new_type (char *);
> >> >  enum block_type { FUNCTION_BLOCK, NON_FUNCTION_BLOCK };
> >> >
> >> >  static struct block *new_block (struct objfile *objfile,
> >> > -                             enum block_type, enum language);
> >> > +                             enum block_type, enum language, bool
> >> isGlobal);
> >>
> >> GDB style is for snake_case rather than camelCase, so:
> >> s/isGlobal/is_global/ throughout.
> >>
> >> >
> >> >  static struct compunit_symtab *new_symtab (const char *, int, struct
> >> objfile *);
> >> >
> >> > @@ -246,7 +246,7 @@ static struct linetable *new_linetable (int);
> >> >  static struct blockvector *new_bvect (int);
> >> >
> >> >  static struct type *parse_type (int, union aux_ext *, unsigned int,
> int
> >> *,
> >> > -                             int, const char *);
> >> > +                             int, const char *, bool);
> >> >
> >> >  static struct symbol *mylookup_symbol (const char *, const struct
> block
> >> *,
> >> >                                      domain_enum, enum address_class);
> >> > @@ -572,7 +572,7 @@ add_data_symbol (SYMR *sh, union aux_ext *ax, int
> >> bigend,
> >> >        || sh->sc == scNil || sh->index == indexNil)
> >> >      s->set_type (builtin_type (objfile)->nodebug_data_symbol);
> >> >    else
> >> > -    s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend,
> name));
> >> > +    s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name,
> >> false));
> >> >    /* Value of a data symbol is its memory address.  */
> >> >  }
> >> >
> >> > @@ -705,7 +705,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> >> *ext_sh, int bigend,
> >> >         break;
> >> >       }
> >> >        s->set_value_longest (svalue);
> >> > -      s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend,
> name));
> >> > +      s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend,
> name,
> >> false));
> >> >        add_symbol (s, top_stack->cur_st, top_stack->cur_block);
> >> >        break;
> >> >
> >> > @@ -761,7 +761,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> >> *ext_sh, int bigend,
> >> >       t = builtin_type (objfile)->builtin_int;
> >> >        else
> >> >       {
> >> > -       t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name);
> >> > +       t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name,
> >> false);
> >> >         if (strcmp (name, "malloc") == 0
> >> >             && t->code () == TYPE_CODE_VOID)
> >> >           {
> >> > @@ -805,7 +805,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> >> *ext_sh, int bigend,
> >> >       s->type ()->set_is_prototyped (true);
> >> >
> >> >        /* Create and enter a new lexical context.  */
> >> > -      b = new_block (objfile, FUNCTION_BLOCK, s->language ());
> >> > +      b = new_block (objfile, FUNCTION_BLOCK, s->language (), false);
> >> >        s->set_value_block (b);
> >> >        b->set_function (s);
> >> >        b->set_start (sh->value);
> >> > @@ -1135,7 +1135,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> >> *ext_sh, int bigend,
> >> >       }
> >> >
> >> >        top_stack->blocktype = stBlock;
> >> > -      b = new_block (objfile, NON_FUNCTION_BLOCK, psymtab_language);
> >> > +      b = new_block (objfile, NON_FUNCTION_BLOCK, psymtab_language,
> >> false);
> >> >        b->set_start (sh->value + top_stack->procadr);
> >> >        b->set_superblock (top_stack->cur_block);
> >> >        top_stack->cur_block = b;
> >> > @@ -1247,7 +1247,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> >> *ext_sh, int bigend,
> >> >       f->set_loc_bitpos (sh->value);
> >> >       bitsize = 0;
> >> >       f->set_type (parse_type (cur_fd, ax, sh->index, &bitsize,
> bigend,
> >> > -                              name));
> >> > +                              name, false));
> >> >       f->set_bitsize (bitsize);
> >> >        }
> >> >        break;
> >> > @@ -1269,7 +1269,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> >> *ext_sh, int bigend,
> >> >        pend = is_pending_symbol (cur_fdr, ext_sh);
> >> >        if (pend == NULL)
> >> >       {
> >> > -       t = parse_type (cur_fd, ax, sh->index, NULL, bigend, name);
> >> > +       t = parse_type (cur_fd, ax, sh->index, NULL, bigend, name,
> true);
> >> >         add_pending (cur_fdr, ext_sh, t);
> >> >       }
> >> >        else
> >> > @@ -1382,7 +1382,7 @@ basic_type (int bt, struct objfile *objfile)
> >> >    if (map_bt[bt])
> >> >      return map_bt[bt];
> >> >
> >> > -  type_allocator alloc (objfile, get_current_subfile ()->language);
> >> > +  type_allocator alloc (objfile, psymtab_language);
> >>
> >> With fixes like this, it can (I think) be useful to mention the commit
> >> that is being fixed, in this case 76fc0f62138e.  Wouldn't be much, I'd
> >> just say:
> >>
> >>   Commit 76fc0f62138e added calls to 'get_current_subfile ()->language',
> >>   but at the time this was known to be a bit of a guess, and untested.
> >>   We should actually have used 'psymtab_language' because ....
> >>
> >> Then, if in the future, someone looks at your patch and wonders why was
> >> this change made?  There's a super quick way that can find the history
> >> of this code, and understand the original change, and your update.
> >>
> >> >
> >> >    switch (bt)
> >> >      {
> >> > @@ -1514,7 +1514,7 @@ basic_type (int bt, struct objfile *objfile)
> >> >
> >> >  static struct type *
> >> >  parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int
> *bs,
> >> > -         int bigend, const char *sym_name)
> >> > +         int bigend, const char *sym_name, bool isStTypedef)
> >>
> >> Switch to snake_case for isStTypedef.
> >>
> >> >  {
> >> >    TIR t[1];
> >> >    struct type *tp = 0;
> >> > @@ -1571,7 +1571,7 @@ parse_type (int fd, union aux_ext *ax, unsigned
> >> int aux_index, int *bs,
> >> >       }
> >> >      }
> >> >
> >> > -  type_allocator alloc (mdebugread_objfile, get_current_subfile
> >> ()->language);
> >> > +  type_allocator alloc (mdebugread_objfile, psymtab_language);
> >> >
> >> >    /* Move on to next aux.  */
> >> >    ax++;
> >> > @@ -1628,7 +1628,7 @@ parse_type (int fd, union aux_ext *ax, unsigned
> >> int aux_index, int *bs,
> >> >        xref_fh = get_rfd (fd, rf);
> >> >        xref_fd = xref_fh - debug_info->fdr;
> >> >        tp = parse_type (xref_fd, debug_info->external_aux +
> >> xref_fh->iauxBase,
> >> > -                 rn->index, NULL, xref_fh->fBigendian, sym_name);
> >> > +                 rn->index, NULL, xref_fh->fBigendian, sym_name,
> false);
> >> >      }
> >> >
> >> >    /* All these types really point to some (common) MIPS type
> >> > @@ -1785,6 +1785,13 @@ parse_type (int fd, union aux_ext *ax, unsigned
> >> int aux_index, int *bs,
> >> >    if (t->continued)
> >> >      complaint (_("illegal TIR continued for %s"), sym_name);
> >> >
> >> > +  if (isStTypedef)
> >> > +  {
> >> > +     struct type *wrap = alloc.new_type (TYPE_CODE_TYPEDEF, 0,
> >> sym_name);
> >> > +     wrap->set_target_type(tp);
> >> > +     tp = wrap;
> >> > +  }
> >>
> >> GNU style indents the '{' and '}', this should look like:
> >>
> >>   if (isStTypedef)
> >>     {
> >>       struct type *wrap = alloc.new_type (TYPE_CODE_TYPEDEF, 0,
> sym_name);
> >>       wrap->set_target_type(tp);
> >>       tp = wrap;
> >>     }
> >>
> >> > +
> >> >    return tp;
> >> >  }
> >> >
> >> > @@ -1839,7 +1846,7 @@ upgrade_type (int fd, struct type **tpp, int tq,
> >> union aux_ext *ax, int bigend,
> >> >
> >> >        indx = parse_type (fh - debug_info->fdr,
> >> >                        debug_info->external_aux + fh->iauxBase,
> >> > -                      id, NULL, bigend, sym_name);
> >> > +                      id, NULL, bigend, sym_name, false);
> >> >
> >> >        /* The bounds type should be an integer type, but might be
> >> anything
> >> >        else due to corrupt aux entries.  */
> >> > @@ -2154,8 +2161,7 @@ parse_external (EXTR *es, int bigend, const
> >> section_offsets &section_offsets,
> >> >     with that and do not need to reorder our linetables.  */
> >> >
> >> >  static void
> >> > -parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines,
> >> > -          CORE_ADDR lowest_pdr_addr)
> >> > +parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines)
> >>
> >> As above, this seems to be reverting parts of 1acc9dca423f78e4, it would
> >> be great to more of an explanation for why that commit was wrong.
> >>
> >> Remember, someone thought that commit was correct once.  You're looking
> >> at their code and thinking they got it wrong.  What's to stop someone
> >> looking at your code and thinking _you_ got it wrong?
> >>
> >> Hopefully, what stops that is that you add more details in the commit
> >> message, along with a link to the previous commit.
> >>
> >> >  {
> >> >    unsigned char *base;
> >> >    int j, k;
> >> > @@ -2169,7 +2175,6 @@ parse_lines (FDR *fh, PDR *pr, struct linetable
> >> *lt, int maxlines,
> >> >    for (j = 0; j < fh->cpd; j++, pr++)
> >> >      {
> >> >        CORE_ADDR l;
> >> > -      CORE_ADDR adr;
> >> >        unsigned char *halt;
> >> >
> >> >        /* No code for this one.  */
> >> > @@ -2186,9 +2191,7 @@ parse_lines (FDR *fh, PDR *pr, struct linetable
> >> *lt, int maxlines,
> >> >       halt = base + fh->cbLine;
> >> >        base += pr->cbLineOffset;
> >> >
> >> > -      adr = pr->adr - lowest_pdr_addr;
> >> > -
> >> > -      l = adr >> 2;          /* in words */
> >> > +      l = pr->adr >> 2;              /* in words */
> >> >        for (lineno = pr->lnLow; base < halt;)
> >> >       {
> >> >         count = *base & 0x0f;
> >> > @@ -4053,6 +4056,14 @@ mdebug_expand_psymtab (legacy_psymtab *pst,
> >> struct objfile *objfile)
> >> >           {
> >> >             (*swap_pdr_in) (cur_bfd, pdr_ptr, pdr_in);
> >> >
> >> > +           char *sym_ptr = (char *)(debug_info->external_sym) +
> >> > +             (fh->isymBase + pdr_in->isym) * external_sym_size;
> >>
> >> The layout seems wrong here.  There should for sure be a single space
> >> after '(char *)', and the '+' should be at the start of the new line
> >> rather than the end of the previous line.
> >>
> >> Additionally, we usually wrap expressions like this within '( ... )' to
> >> force editors to align things better.  Turns out that this pattern of
> >> code is repeated lots in this file.  Search for: '* external_sym_size'
> >> and you'll find lots of example for how to align this code.  I'd go
> >> with:
> >>
> >>               char *sym_ptr = ((char *) debug_info->external_sym
> >>                                + (fh->isymBase + pdr_in->isym)
> >>                                * external_sym_size);
> >>
> >> maybe?
> >>
> >> > +
> >> > +           SYMR sh;
> >> > +           (*swap_sym_in) (cur_bfd, sym_ptr, &sh);
> >> > +
> >> > +           pdr_in->adr = sh.value;
> >> > +
> >> >             /* Determine lowest PDR address, the PDRs are not always
> >> >                sorted.  */
> >> >             if (pdr_in == pr_block.data ())
> >> > @@ -4154,16 +4165,16 @@ mdebug_expand_psymtab (legacy_psymtab *pst,
> >> struct objfile *objfile)
> >> >               {
> >> >                 (*swap_pdr_in) (cur_bfd, pdr_ptr, pdr_in);
> >> >
> >> > -               /* Determine lowest PDR address, the PDRs are not
> always
> >> > -                  sorted.  */
> >> > -               if (pdr_in == pr_block.data ())
> >> > -                 lowest_pdr_addr = pdr_in->adr;
> >> > -               else if (pdr_in->adr < lowest_pdr_addr)
> >> > -                 lowest_pdr_addr = pdr_in->adr;
> >> > +                  sym_ptr = (char *)(debug_info->external_sym) +
> >> > +                    (fh->isymBase + pdr_in->isym) *
> external_sym_size;
> >>
> >> Similar layout issues to above.
> >>
> >> > +
> >> > +                  SYMR sh;
> >> > +                  (*swap_sym_in) (cur_bfd, sym_ptr, &sh);
> >> > +
> >> > +                  pdr_in->adr = sh.value;
> >>
> >> These lines are all indented using only spaces.  GDB (unfortunately)
> >> uses a hybrid tab and space approach for indentation.  If you 'git diff'
> >> and look at this file, git _should_ highlight the whitespace issues for
> >> you -- we have a .gitattributes file in the binutils-gdb root that asks
> >> for whitespace issues to be highlighted.
> >>
> >> >               }
> >> >
> >> > -           parse_lines (fh, pr_block.data (), lines, maxlines,
> >> > -                        lowest_pdr_addr);
> >> > +           parse_lines (fh, pr_block.data (), lines, maxlines);
> >> >             if (lines->nitems < fh->cline)
> >> >               lines = shrink_linetable (lines);
> >> >
> >> > @@ -4291,7 +4302,7 @@ cross_ref (int fd, union aux_ext *ax, struct
> type
> >> **tpp,
> >> >        rf = rn->rfd;
> >> >      }
> >> >
> >> > -  type_allocator alloc (mdebugread_objfile, get_current_subfile
> >> ()->language);
> >> > +  type_allocator alloc (mdebugread_objfile, psymtab_language);
> >> >
> >> >    /* mips cc uses a rf of -1 for opaque struct definitions.
> >> >       Set TYPE_STUB for these types so that check_typedef will
> >> > @@ -4412,7 +4423,8 @@ cross_ref (int fd, union aux_ext *ax, struct
> type
> >> **tpp,
> >> >                                sh.index,
> >> >                                NULL,
> >> >                                fh->fBigendian,
> >> > -                              debug_info->ss + fh->issBase + sh.iss);
> >> > +                              debug_info->ss + fh->issBase + sh.iss,
> >> > +                              sh.st == stTypedef);
> >> >             add_pending (fh, esh, *tpp);
> >> >             break;
> >> >
> >> > @@ -4438,7 +4450,8 @@ cross_ref (int fd, union aux_ext *ax, struct
> type
> >> **tpp,
> >> >                            sh.index,
> >> >                            NULL,
> >> >                            fh->fBigendian,
> >> > -                          debug_info->ss + fh->issBase + sh.iss);
> >> > +                          debug_info->ss + fh->issBase + sh.iss,
> >> > +                          true);
> >> >       }
> >> >        else
> >> >       {
> >> > @@ -4542,6 +4555,7 @@ add_line (struct linetable *lt, int lineno,
> >> CORE_ADDR adr, int last)
> >> >      return lineno;
> >> >
> >> >    lt->item[lt->nitems].line = lineno;
> >> > +  lt->item[lt->nitems].is_stmt = 1;
> >> >    lt->item[lt->nitems++].set_unrelocated_pc (unrelocated_addr (adr <<
> >> 2));
> >> >    return lineno;
> >> >  }
> >> > @@ -4634,9 +4648,10 @@ new_symtab (const char *name, int maxlines,
> >> struct objfile *objfile)
> >> >
> >> >    /* All symtabs must have at least two blocks.  */
> >> >    bv = new_bvect (2);
> >> > -  bv->set_block (GLOBAL_BLOCK, new_block (objfile,
> NON_FUNCTION_BLOCK,
> >> lang));
> >> > -  bv->set_block (STATIC_BLOCK, new_block (objfile,
> NON_FUNCTION_BLOCK,
> >> lang));
> >> > +  bv->set_block (GLOBAL_BLOCK, new_block (objfile,
> NON_FUNCTION_BLOCK,
> >> lang, true));
> >> > +  bv->set_block (STATIC_BLOCK, new_block (objfile,
> NON_FUNCTION_BLOCK,
> >> lang, false));
> >> >    bv->static_block ()->set_superblock (bv->global_block ());
> >> > +  bv->global_block ()->set_compunit_symtab(cust);
> >> >    cust->set_blockvector (bv);
> >> >
> >> >    cust->set_debugformat ("ECOFF");
> >> > @@ -4723,9 +4738,10 @@ new_bvect (int nblocks)
> >> >
> >> >  static struct block *
> >> >  new_block (struct objfile *objfile, enum block_type type,
> >> > -        enum language language)
> >> > +        enum language language, bool isGlobal)
> >> >  {
> >> > -  struct block *retval = new (&objfile->objfile_obstack) block;
> >> > +  struct block *retval = isGlobal ? new (&objfile->objfile_obstack)
> >> global_block
> >> > +    : new (&objfile->objfile_obstack) block;
> >>
> >> By the time you've s/isGlobal/is_global/ I think you should format this
> >> as:
> >>
> >>   struct block *retval = (is_global
> >>                           ? new (&objfile->objfile_obstack) global_block
> >>                           : new (&objfile->objfile_obstack) block);
> >>
> >>
> >> Thanks,
> >> Andrew
> >>
> >>
> >> >
> >> >    if (type == FUNCTION_BLOCK)
> >> >      retval->set_multidict (mdict_create_linear_expandable
> (language));
> >> > @@ -4754,8 +4770,7 @@ new_type (char *name)
> >> >  {
> >> >    struct type *t;
> >> >
> >> > -  t = type_allocator (mdebugread_objfile,
> >> > -                   get_current_subfile ()->language).new_type ();
> >> > +  t = type_allocator (mdebugread_objfile, psymtab_language).new_type
> ();
> >> >    t->set_name (name);
> >> >    INIT_CPLUS_SPECIFIC (t);
> >> >    return t;
> >> > --
> >> > 2.41.0
> >>
> >>
>
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH] mdebug fix
  2023-12-25  5:42                 ` Zeck S
@ 2024-02-07 13:33                   ` Zeck S
  2024-02-16  2:45                   ` [RFC][PATCH?] fixed some segfaults and bugs in mdebug support Zeck S
  1 sibling, 0 replies; 15+ messages in thread
From: Zeck S @ 2024-02-07 13:33 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zeck S

Addressed comments, adapted some core tests, fixed more bugs.
In addition to the modified tests, I specifically also got
gdb.base/relocate.exp working to sort out some problems.

As always, glad to address feedback, and thanks for the patience.

I reverted some of my code around parse_lines addresses.
The Procedure Descriptor Record format is weird.
The File Descriptor Record has the final correct address
of the LOWEST PDR, only. This is used as a base address
for a kind of janky fixup/relocation system.
I suspect originally the PDR addresses were directly useful in simpler
formats.

Based on comments in bfd ecofflink and documentation about something
similar in
"MIPS Mdebug Debugging Information" by David Anderson
that describes HDRR offsets as offsets from the beginning of the a.out
as a "design mistake" I think some compilers
set the PDR address to a location in an object file.

This means there's basically a garbage offset added to all the PDRs.
So all PDRs have their addresses subtracted from the lowest to kind
treat it as 0, and then the lowest PDRs REAL correct address
is used from the FDR, as an offset.

IDO actually always outputs the lowest PDR's address as 0
so subtracting it out does nothing, but hurts nothing.

I can find no real documentation about this.

I did find precedent for what my reverted code was doing in
section 5.3.4.1 of ""Tru64 UNIX Object File and Symbol Table Format Specification"
but after I figured out what the original code was doing I
reverted those changes to distrub the code less.

---
 gdb/mdebugread.c                          | 230 ++++--
 gdb/testsuite/boards/qemu-user.exp        | 267 +++++++
 gdb/testsuite/gdb.mdebug/README           |  67 ++
 gdb/testsuite/gdb.mdebug/break.c          |  92 +++
 gdb/testsuite/gdb.mdebug/break.exp        | 921 ++++++++++++++++++++++
 gdb/testsuite/gdb.mdebug/break1.c         |  59 ++
 gdb/testsuite/gdb.mdebug/info-types-c.exp |  67 ++
 gdb/testsuite/gdb.mdebug/info-types.c     | 116 +++
 8 files changed, 1770 insertions(+), 49 deletions(-)
 create mode 100644 gdb/testsuite/boards/qemu-user.exp
 create mode 100644 gdb/testsuite/gdb.mdebug/README
 create mode 100644 gdb/testsuite/gdb.mdebug/break.c
 create mode 100644 gdb/testsuite/gdb.mdebug/break.exp
 create mode 100644 gdb/testsuite/gdb.mdebug/break1.c
 create mode 100644 gdb/testsuite/gdb.mdebug/info-types-c.exp
 create mode 100644 gdb/testsuite/gdb.mdebug/info-types.c

diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
index cd6638224e7..0118507a8f3 100644
--- a/gdb/mdebugread.c
+++ b/gdb/mdebugread.c
@@ -237,7 +237,7 @@ static struct type *new_type (char *);
 enum block_type { FUNCTION_BLOCK, NON_FUNCTION_BLOCK };
 
 static struct block *new_block (struct objfile *objfile,
-				enum block_type, enum language);
+				enum block_type, enum language, bool is_global);
 
 static struct compunit_symtab *new_symtab (const char *, int, struct objfile *);
 
@@ -246,7 +246,7 @@ static struct linetable *new_linetable (int);
 static struct blockvector *new_bvect (int);
 
 static struct type *parse_type (int, union aux_ext *, unsigned int, int *,
-				int, const char *);
+				int, const char *, bool);
 
 static struct symbol *mylookup_symbol (const char *, const struct block *,
 				       domain_enum, enum address_class);
@@ -572,7 +572,7 @@ add_data_symbol (SYMR *sh, union aux_ext *ax, int bigend,
       || sh->sc == scNil || sh->index == indexNil)
     s->set_type (builtin_type (objfile)->nodebug_data_symbol);
   else
-    s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name));
+    s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name, false));
   /* Value of a data symbol is its memory address.  */
 }
 
@@ -624,8 +624,17 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
       break;
     }
 
-  if (section_index != -1)
-    sh->value += section_offsets[section_index];
+  if (section_index != -1) {
+	int offset = section_offsets[section_index];
+    if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+    {
+	  /* This is attempting to detect .o files.
+	     Their sections are relocated in symfile.c default_symfile_offsets
+	     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+	  offset += objfile->sections_start[section_index].the_bfd_section->vma;
+    }
+	sh->value += offset;
+  }
 
   switch (sh->st)
     {
@@ -705,7 +714,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	  break;
 	}
       s->set_value_longest (svalue);
-      s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name));
+      s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name, false));
       add_symbol (s, top_stack->cur_st, top_stack->cur_block);
       break;
 
@@ -761,7 +770,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	t = builtin_type (objfile)->builtin_int;
       else
 	{
-	  t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name);
+	  t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name, false);
 	  if (strcmp (name, "malloc") == 0
 	      && t->code () == TYPE_CODE_VOID)
 	    {
@@ -805,7 +814,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	s->type ()->set_is_prototyped (true);
 
       /* Create and enter a new lexical context.  */
-      b = new_block (objfile, FUNCTION_BLOCK, s->language ());
+      b = new_block (objfile, FUNCTION_BLOCK, s->language (), false);
       s->set_value_block (b);
       b->set_function (s);
       b->set_start (sh->value);
@@ -1135,7 +1144,13 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	}
 
       top_stack->blocktype = stBlock;
-      b = new_block (objfile, NON_FUNCTION_BLOCK, psymtab_language);
+
+	  /* Using psymtab_langauge fixes commit 76fc0f62138e.
+	     This file does not use buildsym-lecacy.
+	     start_compunit_symtab () is never called.
+	     get_current_subfile () will crash because
+	     buildsym_compunit has never been initialized. */
+      b = new_block (objfile, NON_FUNCTION_BLOCK, psymtab_language, false);
       b->set_start (sh->value + top_stack->procadr);
       b->set_superblock (top_stack->cur_block);
       top_stack->cur_block = b;
@@ -1247,7 +1262,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	f->set_loc_bitpos (sh->value);
 	bitsize = 0;
 	f->set_type (parse_type (cur_fd, ax, sh->index, &bitsize, bigend,
-				 name));
+				 name, false));
 	f->set_bitsize (bitsize);
       }
       break;
@@ -1269,7 +1284,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
       pend = is_pending_symbol (cur_fdr, ext_sh);
       if (pend == NULL)
 	{
-	  t = parse_type (cur_fd, ax, sh->index, NULL, bigend, name);
+	  t = parse_type (cur_fd, ax, sh->index, NULL, bigend, name, true);
 	  add_pending (cur_fdr, ext_sh, t);
 	}
       else
@@ -1382,7 +1397,7 @@ basic_type (int bt, struct objfile *objfile)
   if (map_bt[bt])
     return map_bt[bt];
 
-  type_allocator alloc (objfile, get_current_subfile ()->language);
+  type_allocator alloc (objfile, psymtab_language);
 
   switch (bt)
     {
@@ -1514,7 +1529,7 @@ basic_type (int bt, struct objfile *objfile)
 
 static struct type *
 parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
-	    int bigend, const char *sym_name)
+	    int bigend, const char *sym_name, bool is_stTypedef)
 {
   TIR t[1];
   struct type *tp = 0;
@@ -1571,7 +1586,7 @@ parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
 	}
     }
 
-  type_allocator alloc (mdebugread_objfile, get_current_subfile ()->language);
+  type_allocator alloc (mdebugread_objfile, psymtab_language);
 
   /* Move on to next aux.  */
   ax++;
@@ -1628,7 +1643,7 @@ parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
       xref_fh = get_rfd (fd, rf);
       xref_fd = xref_fh - debug_info->fdr;
       tp = parse_type (xref_fd, debug_info->external_aux + xref_fh->iauxBase,
-		    rn->index, NULL, xref_fh->fBigendian, sym_name);
+		    rn->index, NULL, xref_fh->fBigendian, sym_name, false);
     }
 
   /* All these types really point to some (common) MIPS type
@@ -1785,6 +1800,13 @@ parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
   if (t->continued)
     complaint (_("illegal TIR continued for %s"), sym_name);
 
+  if (is_stTypedef)
+    {
+	struct type *wrap = alloc.new_type (TYPE_CODE_TYPEDEF, 0, sym_name);
+	wrap->set_target_type(tp);
+	tp = wrap;
+    }
+
   return tp;
 }
 
@@ -1839,7 +1861,7 @@ upgrade_type (int fd, struct type **tpp, int tq, union aux_ext *ax, int bigend,
 
       indx = parse_type (fh - debug_info->fdr,
 			 debug_info->external_aux + fh->iauxBase,
-			 id, NULL, bigend, sym_name);
+			 id, NULL, bigend, sym_name, false);
 
       /* The bounds type should be an integer type, but might be anything
 	 else due to corrupt aux entries.  */
@@ -2155,7 +2177,7 @@ parse_external (EXTR *es, int bigend, const section_offsets &section_offsets,
 
 static void
 parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines,
-	     CORE_ADDR lowest_pdr_addr)
+			   CORE_ADDR textlow, CORE_ADDR lowest_pdr_addr)
 {
   unsigned char *base;
   int j, k;
@@ -2169,7 +2191,6 @@ parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines,
   for (j = 0; j < fh->cpd; j++, pr++)
     {
       CORE_ADDR l;
-      CORE_ADDR adr;
       unsigned char *halt;
 
       /* No code for this one.  */
@@ -2186,9 +2207,15 @@ parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines,
 	halt = base + fh->cbLine;
       base += pr->cbLineOffset;
 
-      adr = pr->adr - lowest_pdr_addr;
-
-      l = adr >> 2;		/* in words */
+      /* textlow is the FDR->adr, for the file containing these PDRs.
+	  FDR->adr is the absolute address of the lowest PDR.
+	  PDR->adr's themselves, are relative offsets.
+	  The PDR->adr's may start at 0, or they could have an offset
+	  based on the PDRs position in an object file.
+	  This is why the lowest PDR address is subtracted
+	  from all other PDRs addresses, to subtract out the potential offset.
+	  See bfd/ecofflink.c comments. */
+      l = (textlow +  pr->adr - lowest_pdr_addr) >> 2;		/* in words */
       for (lineno = pr->lnLow; base < halt;)
 	{
 	  count = *base & 0x0f;
@@ -3047,28 +3074,49 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 		       index into the namestring which indicates the
 		       debugging type symbol.  */
 
+			int section;
+			bfd_vma address;
+
 		    switch (p[1])
 		      {
 		      case 'S':
+			  section = SECT_OFF_DATA (objfile);
+			  address = sh.value;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			     Their sections are relocated in symfile.c default_symfile_offsets
+			     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			  address += objfile->sections_start[section].the_bfd_section->vma;
+			}
 			pst->add_psymbol (gdb::string_view (namestring,
 							    p - namestring),
 					  true, VAR_DOMAIN, LOC_STATIC,
-					  SECT_OFF_DATA (objfile),
+					  section,
 					  psymbol_placement::STATIC,
-					  unrelocated_addr (sh.value),
+					  unrelocated_addr (address),
 					  psymtab_language,
 					  partial_symtabs, objfile);
 			continue;
 		      case 'G':
+			  section = SECT_OFF_DATA (objfile);
+			  address = sh.value;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			    Their sections are relocated in symfile.c default_symfile_offsets
+			    but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			  address += objfile->sections_start[section].the_bfd_section->vma;
+			}
 			/* The addresses in these entries are reported
 			   to be wrong.  See the code that reads 'G's
 			   for symtabs.  */
 			pst->add_psymbol (gdb::string_view (namestring,
 							    p - namestring),
 					  true, VAR_DOMAIN, LOC_STATIC,
-					  SECT_OFF_DATA (objfile),
+					  section,
 					  psymbol_placement::GLOBAL,
-					  unrelocated_addr (sh.value),
+					  unrelocated_addr (address),
 					  psymtab_language,
 					  partial_symtabs, objfile);
 			continue;
@@ -3215,12 +3263,21 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 			    function_outside_compilation_unit_complaint
 			      (copy.c_str ());
 			  }
+			section = SECT_OFF_TEXT (objfile);
+			address = sh.value;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			     Their sections are relocated in symfile.c default_symfile_offsets
+			     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			  address += objfile->sections_start[section].the_bfd_section->vma;
+			}
 			pst->add_psymbol (gdb::string_view (namestring,
 							    p - namestring),
 					  true, VAR_DOMAIN, LOC_BLOCK,
-					  SECT_OFF_TEXT (objfile),
+					  section,
 					  psymbol_placement::STATIC,
-					  unrelocated_addr (sh.value),
+					  unrelocated_addr (address),
 					  psymtab_language,
 					  partial_symtabs, objfile);
 			continue;
@@ -3236,12 +3293,21 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 			    function_outside_compilation_unit_complaint
 			      (copy.c_str ());
 			  }
+			section = SECT_OFF_TEXT (objfile);
+			address = sh.value;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			     Their sections are relocated in symfile.c default_symfile_offsets
+			     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			  address += objfile->sections_start[section].the_bfd_section->vma;
+			}
 			pst->add_psymbol (gdb::string_view (namestring,
 							    p - namestring),
 					  true, VAR_DOMAIN, LOC_BLOCK,
-					  SECT_OFF_TEXT (objfile),
+					  section,
 					  psymbol_placement::GLOBAL,
-					  unrelocated_addr (sh.value),
+					  unrelocated_addr (address),
 					  psymtab_language,
 					  partial_symtabs, objfile);
 			continue;
@@ -3419,6 +3485,9 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 		  break;
 		}
 
+		bfd_vma address;
+		/* some symbols don't use the above section */
+		int section_override;
 	      switch (sh.st)
 		{
 		  unrelocated_addr high;
@@ -3426,9 +3495,18 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 		  int new_sdx;
 
 		case stStaticProc:
-		  reader.record_with_info (sym_name, minsym_value,
+		    section_override = SECT_OFF_TEXT (objfile);
+			address = (bfd_vma)minsym_value;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			     Their sections are relocated in symfile.c default_symfile_offsets
+			     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			  address += objfile->sections_start[section_override].the_bfd_section->vma;
+			}
+		  reader.record_with_info (sym_name, (unrelocated_addr)address,
 					   mst_file_text,
-					   SECT_OFF_TEXT (objfile));
+					   section_override);
 
 		  /* FALLTHROUGH */
 
@@ -3476,12 +3554,21 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 		     still able to find the PROGRAM name via the partial
 		     symbol table, and the MAIN__ symbol via the minimal
 		     symbol table.  */
+
+		  address = sh.value;
+		  if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+		  {
+			 /* This is attempting to detect .o files.
+			 Their sections are relocated in symfile.c default_symfile_offsets
+			 but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			 address += objfile->sections_start[section].the_bfd_section->vma;
+		  }
 		  if (sh.st == stProc)
 		    pst->add_psymbol (sym_name, true,
 				      VAR_DOMAIN, LOC_BLOCK,
 				      section,
 				      psymbol_placement::GLOBAL,
-				      unrelocated_addr (sh.value),
+				      unrelocated_addr (address),
 				      psymtab_language,
 				      partial_symtabs, objfile);
 		  else
@@ -3489,7 +3576,7 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 				      VAR_DOMAIN, LOC_BLOCK,
 				      section,
 				      psymbol_placement::STATIC,
-				      unrelocated_addr (sh.value),
+				      unrelocated_addr (address),
 				      psymtab_language,
 				      partial_symtabs, objfile);
 
@@ -3517,13 +3604,36 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 
 		case stStatic:	/* Variable */
 		  if (SC_IS_DATA (sh.sc))
-		    reader.record_with_info (sym_name, minsym_value,
+		  {
+			section_override = SECT_OFF_DATA (objfile);
+			address = 0;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			     Their sections are relocated in symfile.c default_symfile_offsets
+			     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			     address += objfile->sections_start[section_override].the_bfd_section->vma;
+			}
+		    reader.record_with_info (sym_name, (unrelocated_addr)(((bfd_vma)minsym_value + address)),
 					     mst_file_data,
-					     SECT_OFF_DATA (objfile));
+					     section_override);
+		  }
 		  else
-		    reader.record_with_info (sym_name, minsym_value,
+		  {
+			section_override = SECT_OFF_BSS (objfile);
+			address = 0;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			     Their sections are relocated in symfile.c default_symfile_offsets
+			     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			  address += objfile->sections_start[section_override].the_bfd_section->vma;
+			}
+		    reader.record_with_info (sym_name, (unrelocated_addr)(((bfd_vma)minsym_value + address)),
 					     mst_file_bss,
-					     SECT_OFF_BSS (objfile));
+					     section_override);
+		  }
+
 		  theclass = LOC_STATIC;
 		  break;
 
@@ -3596,11 +3706,19 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 		  cur_sdx++;
 		  continue;
 		}
+			address = sh.value;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			     Their sections are relocated in symfile.c default_symfile_offsets
+			     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			  address += objfile->sections_start[section].the_bfd_section->vma;
+			}
 	      /* Use this gdb symbol.  */
 	      pst->add_psymbol (sym_name, true,
 				VAR_DOMAIN, theclass, section,
 				psymbol_placement::STATIC,
-				unrelocated_addr (sh.value),
+				unrelocated_addr (address),
 				psymtab_language,
 				partial_symtabs, objfile);
 	    skip:
@@ -3676,12 +3794,20 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 		  theclass = LOC_STATIC;
 		  break;
 		}
+		  bfd_vma address = svalue;
 	      char *sym_name = debug_info->ssext + psh->iss;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			     Their sections are relocated in symfile.c default_symfile_offsets
+			     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			  address += objfile->sections_start[section].the_bfd_section->vma;
+			}
 	      pst->add_psymbol (sym_name, true,
 				VAR_DOMAIN, theclass,
 				section,
 				psymbol_placement::GLOBAL,
-				unrelocated_addr (svalue),
+				unrelocated_addr (address),
 				psymtab_language,
 				partial_symtabs, objfile);
 	    }
@@ -4163,7 +4289,7 @@ mdebug_expand_psymtab (legacy_psymtab *pst, struct objfile *objfile)
 		}
 
 	      parse_lines (fh, pr_block.data (), lines, maxlines,
-			   lowest_pdr_addr);
+			   (CORE_ADDR) pst->unrelocated_text_low (), lowest_pdr_addr);
 	      if (lines->nitems < fh->cline)
 		lines = shrink_linetable (lines);
 
@@ -4291,7 +4417,7 @@ cross_ref (int fd, union aux_ext *ax, struct type **tpp,
       rf = rn->rfd;
     }
 
-  type_allocator alloc (mdebugread_objfile, get_current_subfile ()->language);
+  type_allocator alloc (mdebugread_objfile, psymtab_language);
 
   /* mips cc uses a rf of -1 for opaque struct definitions.
      Set TYPE_STUB for these types so that check_typedef will
@@ -4412,7 +4538,8 @@ cross_ref (int fd, union aux_ext *ax, struct type **tpp,
 				 sh.index,
 				 NULL,
 				 fh->fBigendian,
-				 debug_info->ss + fh->issBase + sh.iss);
+				 debug_info->ss + fh->issBase + sh.iss,
+				 sh.st == stTypedef);
 	      add_pending (fh, esh, *tpp);
 	      break;
 
@@ -4438,7 +4565,8 @@ cross_ref (int fd, union aux_ext *ax, struct type **tpp,
 			     sh.index,
 			     NULL,
 			     fh->fBigendian,
-			     debug_info->ss + fh->issBase + sh.iss);
+			     debug_info->ss + fh->issBase + sh.iss,
+			     true);
 	}
       else
 	{
@@ -4542,6 +4670,8 @@ add_line (struct linetable *lt, int lineno, CORE_ADDR adr, int last)
     return lineno;
 
   lt->item[lt->nitems].line = lineno;
+  lt->item[lt->nitems].is_stmt = 1;
+  lt->item[lt->nitems].prologue_end = 1;
   lt->item[lt->nitems++].set_unrelocated_pc (unrelocated_addr (adr << 2));
   return lineno;
 }
@@ -4634,9 +4764,10 @@ new_symtab (const char *name, int maxlines, struct objfile *objfile)
 
   /* All symtabs must have at least two blocks.  */
   bv = new_bvect (2);
-  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang));
-  bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang));
+  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang, true));
+  bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang, false));
   bv->static_block ()->set_superblock (bv->global_block ());
+  bv->global_block ()->set_compunit_symtab(cust);
   cust->set_blockvector (bv);
 
   cust->set_debugformat ("ECOFF");
@@ -4723,9 +4854,11 @@ new_bvect (int nblocks)
 
 static struct block *
 new_block (struct objfile *objfile, enum block_type type,
-	   enum language language)
+	   enum language language, bool is_global)
 {
-  struct block *retval = new (&objfile->objfile_obstack) block;
+  struct block *retval = (is_global
+    ? new (&objfile->objfile_obstack) global_block
+    : new (&objfile->objfile_obstack) block);
 
   if (type == FUNCTION_BLOCK)
     retval->set_multidict (mdict_create_linear_expandable (language));
@@ -4754,8 +4887,7 @@ new_type (char *name)
 {
   struct type *t;
 
-  t = type_allocator (mdebugread_objfile,
-		      get_current_subfile ()->language).new_type ();
+  t = type_allocator (mdebugread_objfile, psymtab_language).new_type ();
   t->set_name (name);
   INIT_CPLUS_SPECIFIC (t);
   return t;
diff --git a/gdb/testsuite/boards/qemu-user.exp b/gdb/testsuite/boards/qemu-user.exp
new file mode 100644
index 00000000000..8a48ab7f6d4
--- /dev/null
+++ b/gdb/testsuite/boards/qemu-user.exp
@@ -0,0 +1,267 @@
+# Copyright 2020-2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# NOTICE:
+# QEMU user mode gdbstub has multithreading problems
+# https://gitlab.com/qemu-project/qemu/-/issues/1671
+# run with --ignore gdb.threads/access-mem-running-thread-exit.exp
+# since this test in particular seems to hang. Could be related.
+
+# This technique is from native-extended-gdbserver.exp
+# We must load this explicitly here, and rename the procedures we want
+# to override.  If we didn't do this, given that mi-support.exp is
+# loaded later in the test files, the procedures loaded then would
+# override our definitions.
+load_lib mi-support.exp
+
+set qemu ""
+set qemu_arch ""
+set qemu_user_spawn_id ""
+set qemu_user_last_load_file ""
+set port "2345"
+
+# add sections for other architectures
+# as needed
+case "$target_triplet" in {
+	{ "mips" } {
+		set qemu "qemu-mips"
+		set qemu_arch "mips"
+	}
+	default {
+		puts "No target hardware for $target_triplet"
+	}
+}
+
+# QEMU supports some of extended-remote, but not the run command
+# which causes problems for the test suite
+set_board_info gdb_protocol "remote"
+set_board_info use_gdb_stub 1
+set_board_info gdb,do_reload_on_run 1
+set_board_info exit_is_reliable 1
+# static link so we do not need to configure an QEMU LD prefix and install
+# libraries for the target architecture
+set_board_info ldflags "-static"
+
+# technique from simavr.exp
+#
+# Load executable into GDB
+#
+proc gdb_load { arg } {
+    global gdb_prompt
+    global verbose
+    global spawn_id
+    global qemu
+    global qemu_user_last_load_file
+    global qemu_user_spawn_id
+    global port
+
+    # keep track of last loaded file
+    # to simulate run restart like behavior
+    if { $arg == "" } {
+		set arg $qemu_user_last_load_file
+		if { $arg == ""  } {
+			global last_loaded_file
+			# this fallback is needed
+			# for tests like gdb.base/break-unload-file.exp
+			set arg $last_loaded_file
+		}
+    } else {
+       set qemu_user_last_load_file $arg
+    }
+
+    if { $arg != "" } {
+		if {[gdb_file_cmd $arg]} then { return -1 }
+    }
+
+    # Close any previous qemu user instance.
+    if { $qemu_user_spawn_id != "" } {
+		verbose -log "qemu user: closing previous spawn id $qemu_user_spawn_id"
+		if [catch { close -i $qemu_user_spawn_id } != 0] {
+			warning "qemu user: failed to close connection to previous qemu user instance"
+		}
+
+		# some tests get QEMU
+		# into a state where the process doesn't want to die
+		# so -nowait to not hang the entire test run
+		wait -nowait -i $qemu_user_spawn_id
+		set qemu_user_spawn_id ""
+    }
+
+    # technique from gdbserver-support.exp
+    # Loop till we find a free port.
+    # This is to cope with qemu sometimes getting
+    # into a bad state and not closing.
+    # Do a pkill -f qemu-mips -9 after running tests
+    # to remove stragglers
+    while 1 {
+		# Run QEMU user
+		set cmd "spawn -noecho $qemu -g $port $arg"
+		verbose -log "Spawning qemu user: $cmd"
+		eval $cmd
+		set qemu_user_spawn_id $spawn_id
+
+		# without inferior_spawn_id
+		# tests such as gdb.base/a2-run.exp
+		# can't look at the right process output
+		global inferior_spawn_id
+		set inferior_spawn_id $spawn_id
+
+		expect {
+			-i $qemu_user_spawn_id
+			-timeout 1
+			-re ".*qemu: could not open gdbserver on.*" {
+				verbose -log "Port $port is already in use."
+				if [catch { close -i $qemu_user_spawn_id } != 0] {
+					warning "qemu user: failed to close connection to previous qemu user instance"
+				}
+				# Bump the port number to avoid the conflict.
+				wait -i $qemu_user_spawn_id
+				incr port
+				continue
+			}
+		}
+		break
+	}
+
+    # arch needs set because some tests
+    # take actions that need arch to be correct
+    # immediately, before file command loads the binary
+    # for example gdb.base/break-unload-file.exp
+    global qemu_arch
+    send_gdb "set arch $qemu_arch\n"
+
+    # Connect to qemu user.
+    send_gdb "target remote :$port\n"
+    gdb_expect {
+		# qemu user mode gdb stub does not support non stop mode
+		# this is so tests that attempt it die quickly
+		-re ".*Non-stop mode requested, but remote does not support non-stop.*" {
+			error "qemu user does not support non-stop"
+		}
+		# cannot have multiple targets connected
+		# some tests end up causing this to be attempted in gdb.multi
+		-re ".*Already connected to a remote target.  Disconnect?.*" {
+			send_gdb "y\n"
+			gdb_expect {
+				-re ".*$gdb_prompt $" {
+					error "connected to new target while connected to existing"
+				}
+			}
+		}
+		-re ".*$gdb_prompt $" {
+			if $verbose>1 then {
+				send_user "Connected to QEMU target\n"
+			}
+		}
+		-re "Remote debugging using .*$gdb_prompt $" {
+			verbose "Set target to remote for QEMU"
+		}
+		timeout {
+			verbose -log "qemu-user: unable to connect to qemu user, closing qemu user spawn id"
+			close -i $qemu_user_spawn_id
+			verbose -log "qemu-user: unable to connect to qemu user, waiting for qemu user process exit"
+			wait -i $qemu_user_spawn_id
+			set qemu_user_spawn_id ""
+			error "unable to connect to qemu user stub"
+		}
+	}
+
+	return 0
+}
+
+# technique from native-extended-gdbserver.exp
+# for overriding mi-support.exp procs
+if { [info procs original_mi_gdb_load] == "" } {
+    rename mi_gdb_load original_gdbserver_mi_gdb_load
+}
+
+# same ideas as gdb_load, but adapted for mi
+proc mi_gdb_load { arg } {
+    global gdb_prompt
+    global verbose
+    global spawn_id
+    global qemu
+    global qemu_user_last_load_file
+    global qemu_user_spawn_id
+    global port
+
+    if { $arg == "" } {
+		set arg $qemu_user_last_load_file
+		if { $arg == ""  } {
+			global last_loaded_file
+			set arg $last_loaded_file
+		}
+    } else {
+		set qemu_user_last_load_file $arg
+    }
+
+    if { $arg != "" } {
+		if {[mi_gdb_file_cmd $arg]} then { return -1 }
+    }
+
+    #Close any previous qemu user instance.
+    if { $qemu_user_spawn_id != "" } {
+		verbose -log "qemu user: closing previous spawn id $qemu_user_spawn_id"
+		if [catch { close -i $qemu_user_spawn_id } != 0] {
+			warning "qemu user: failed to close previous qemu user instance"
+		}
+
+		# some tests get QEMU/its gdb stub
+		# into a state where the process doesn't want to die
+		# so -nowait to not hang the entire test run
+		wait -nowait -i $qemu_user_spawn_id
+		set qemu_user_spawn_id ""
+    }
+
+    # technique from gdbserver-support.exp
+    # Loop till we find a free port.
+    while 1 {
+		# Run QEMU user
+		set cmd "spawn -noecho $qemu -g $port $arg"
+		verbose -log "Spawning qemu user: $cmd"
+		eval $cmd
+		set qemu_user_spawn_id $spawn_id
+
+		# without inferior_spawn_id
+		# tests such as gdb.base/a2-run.exp
+		# can't look at the right process output
+		global inferior_spawn_id
+		set inferior_spawn_id $spawn_id
+
+		expect {
+			-i $qemu_user_spawn_id
+			-timeout 1
+			-re ".*qemu: could not open gdbserver on.*" {
+				verbose -log "Port $port is already in use."
+				if [catch { close -i $qemu_user_spawn_id } != 0] {
+					warning "qemu user: failed to close connection to previous qemu user instance"
+				}
+				# Bump the port number to avoid the conflict.
+				wait -i $qemu_user_spawn_id
+				incr port
+				continue
+			}
+		}
+		break
+	}
+
+	return 0
+}
+
+# technique from stdio-gdb-server-base.exp
+proc mi_gdb_target_load { } {
+    global port
+    return [mi_gdb_target_cmd "remote" ":$port"]
+}
diff --git a/gdb/testsuite/gdb.mdebug/README b/gdb/testsuite/gdb.mdebug/README
new file mode 100644
index 00000000000..f749d3e1cbc
--- /dev/null
+++ b/gdb/testsuite/gdb.mdebug/README
@@ -0,0 +1,67 @@
+These tests are adapted for dealing with the limitations of old compilers and the mdebug format.
+
+Execute the mdebug tests with something like:
+runtest gdb.mdebug/info-types-c.exp CC_FOR_TARGET=idowrapper MDEBUG=1 --target mips --target_board qemu-user
+
+The CC_FOR_TARGET is probably going to be a wrapper script
+for an old compiler running in an emulator.
+Example at the end of this readme.
+Put it somewhere in your PATH.
+Do not give the path to the script as CC_FOR_TARGET, that does not work.
+
+The qemu-user board will run mips programs in qemu user mode
+and connect gdb to QEMU's gdb stub.
+
+mdebug does not record line numbers for type definitions.
+This is why the info types tests are modified
+
+mdebug output can be odd in how line mappings deal with the start of functions.
+This is why the break tests are modified.
+
+Most compilers outputting mdebug data are so old they cannot deal with C99 features.
+This makes parsing standard library headers for modern implementations a problem.
+Also, even past that, the headers may eventually lead to compiler specific code
+in things like stddef.h that may not be appriopriate for your old compiler.
+This is why break.c has some modified standard function signatures at the beginning.
+
+A few of the break tests *sometimes* fail still.
+Several memory initialization bugs were fixed but whatever is causing that
+is very inconsistent. Or it could be some issue with the exp file.
+
+The best documentation on mdebug are these documents:
+
+"Tru64 UNIX Object File and Symbol Table Format Specification"
+Sections 5, 9, and 10
+Note, the word "mdebug" is never used, but it is that format
+carried through from DEC to Compaq.
+
+"MIPS Mdebug Debugging Information" by David Anderson
+
+A wrapper script like this was used to create .o files with IDO running in an emulator.
+The final link commands are run with regular linux gcc.
+
+#!/bin/sh
+
+echo "$@" >> theargs
+
+for arg do
+  if [ "$arg" = "-static" ]
+  then
+    link=1
+  fi
+done
+
+if [ "$link" = 1 ]
+then
+  mips-unknown-linux-gnu-gcc "$@" -fno-exceptions
+else
+for arg do
+  shift
+  [ "$arg" = "-fdiagnostics-color=never" ] && continue
+  [ "$arg" = "-g" ] && continue
+  set -- "$@" "$arg"
+done
+
+/home/someuser/qemu-irix/build/irix-linux-user/qemu-irix -L /home/someuser/ido/ido7.1_compiler /home/someuser/ido/ido7.1_compiler/usr/bin/cc -Xcpluscomm -nostdlib -nostdinc -mips2
+-g2 "$@" 2>/dev/null
+fi
diff --git a/gdb/testsuite/gdb.mdebug/break.c b/gdb/testsuite/gdb.mdebug/break.c
new file mode 100644
index 00000000000..d0b26998c77
--- /dev/null
+++ b/gdb/testsuite/gdb.mdebug/break.c
@@ -0,0 +1,92 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 1992-2023 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int printf (const char *__format, ...);
+int fprintf (void *__stream, const char *__format, ...);
+int atoi(const char *_Str);
+void *malloc(int size);
+
+extern void * stderr;
+
+extern int marker1 (void);
+extern int marker2 (int a);
+extern void marker3 (char *a, char *b);
+extern void marker4 (long d);
+
+/* We're used by a test that requires malloc, so make sure it is in
+   the executable.  */
+void *need_malloc ()
+{
+  return malloc (1);
+}
+
+/*
+ *	This simple classical example of recursion is useful for
+ *	testing stack backtraces and such.
+ */
+
+int factorial(int);
+
+int
+main (int argc, char **argv, char **envp)
+{ /* set breakpoint 6 here */
+  if (argc == 12345) {  /* an unlikely value < 2^16, in case uninited */
+	fprintf (stderr, "usage:  factorial <number>\n");
+	return 1;
+    }
+    printf ("%d\n", factorial (atoi ("6")));  /* set breakpoint 1 here */
+    /* set breakpoint 12 here */
+    marker1 ();  /* set breakpoint 11 here */
+    marker2 (43); /* set breakpoint 20 here */
+    marker3 ("stack", "trace"); /* set breakpoint 21 here */
+    marker4 (177601976L);
+    /* We're used by a test that requires malloc, so make sure it is
+       in the executable.  */
+    (void)malloc (1);
+
+    argc = (argc == 12345); /* This is silly, but we can step off of it */ /* set breakpoint 2 here */
+    return argc;  /* set breakpoint 10 here */
+} /* set breakpoint 10a here */
+
+int factorial (int value)
+{ /* set breakpoint 7 here */
+  if (value > 1) { /* set breakpoint 7a here */
+	value *= factorial (value - 1);
+    }
+    return (value); /* set breakpoint 19 here */
+}
+
+int multi_line_if_conditional (int a, int b, int c)
+{ /* set breakpoint 3 here */
+  if (a
+      && b
+      && c)
+    return 0;
+  else
+    return 1;
+}
+
+int multi_line_while_conditional (int a, int b, int c)
+{ /* set breakpoint 4 here */
+  while (a
+      && b
+      && c)
+    {
+      a--, b--, c--;
+    }
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.mdebug/break.exp b/gdb/testsuite/gdb.mdebug/break.exp
new file mode 100644
index 00000000000..cb74c1b0081
--- /dev/null
+++ b/gdb/testsuite/gdb.mdebug/break.exp
@@ -0,0 +1,921 @@
+#   Copyright 1988-2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file was written by Rob Savoye. (rob@cygnus.com)
+
+if { [build_executable "failed to prepare" "break" {break.c break1.c} {debug nowarnings}] } {
+    return -1
+}
+set srcfile break.c
+set srcfile1 break1.c
+
+set bp_location1 [gdb_get_line_number "set breakpoint 1 here"]
+set bp_location2 [gdb_get_line_number "set breakpoint 2 here"]
+set bp_location3 [gdb_get_line_number "set breakpoint 3 here"]
+set bp_location4 [gdb_get_line_number "set breakpoint 4 here"]
+set bp_location6 [gdb_get_line_number "set breakpoint 6 here"]
+set bp_location7 [gdb_get_line_number "set breakpoint 7 here"]
+set bp_location8 [gdb_get_line_number "set breakpoint 8 here" $srcfile1]
+set bp_location11 [gdb_get_line_number "set breakpoint 11 here"]
+
+set main_line $bp_location6
+
+# In C++ mode, we see a full prototype; in C mode, we only see the
+# function name, with no parameter info.
+proc func {name} {
+    return "${name}(?:\(\[^\r\n\]*\))?"
+}
+
+# test simple breakpoint setting commands
+
+proc_with_prefix test_break {} {
+    clean_restart break
+
+    # Test deleting all breakpoints when there are none installed,
+    # GDB should not prompt for confirmation.
+    # Note that lib/gdb.exp provides a "delete_breakpoints" proc
+    # for general use elsewhere.
+    send_gdb "delete breakpoints\n"
+    gdb_expect {
+	 -re "Delete all breakpoints.*$" {
+		send_gdb "y\n"
+		gdb_expect {
+		    -re "$::gdb_prompt $" {
+			fail "delete all breakpoints when none (unexpected prompt)"
+		    }
+		    timeout	{ fail "delete all breakpoints when none (timeout after unexpected prompt)" }
+		}
+	    }
+	 -re ".*$::gdb_prompt $"       { pass "delete all breakpoints when none" }
+	timeout	            { fail "delete all breakpoints when none (timeout)" }
+    }
+
+    # test break at function
+    gdb_test "break -q main" \
+	"Breakpoint.*at.* file .*$::srcfile, line.*" \
+	"breakpoint function"
+
+    # test break at quoted function
+    gdb_test "break \"marker2\"" \
+	"Breakpoint.*at.* file .*$::srcfile1, line.*" \
+	"breakpoint quoted function"
+
+    # test break at function in file
+    gdb_test "break $::srcfile:factorial" \
+	"Breakpoint.*at.* file .*$::srcfile, line.*" \
+	"breakpoint function in file"
+
+    # test break at line number
+    #
+    # Note that the default source file is the last one whose source text
+    # was printed.  For native debugging, before we've executed the
+    # program, this is the file containing main, but for remote debugging,
+    # it's wherever the processor was stopped when we connected to the
+    # board.  So, to be sure, we do a list command.
+    gdb_test "list -q main" \
+	".*main \\(int argc, char \\*\\*argv, char \\*\\*envp\\).*" \
+	"use `list' to establish default source file"
+
+    gdb_test "break $::bp_location1" \
+	"Breakpoint.*at.* file .*$::srcfile, line $::bp_location1\\." \
+	"breakpoint line number"
+
+    # test duplicate breakpoint
+    gdb_test "break $::bp_location1" \
+	"Note: breakpoint \[0-9\]+ also set at pc.*Breakpoint \[0-9\]+ at.* file .*$::srcfile, line $::bp_location1\\." \
+	"breakpoint duplicate"
+
+    # test break at line number in file
+    gdb_test "break $::srcfile:$::bp_location2" \
+	"Breakpoint.*at.* file .*$::srcfile, line $::bp_location2\\." \
+	"breakpoint line number in file"
+
+    # Test putting a break at the start of a multi-line if conditional.
+    # Verify the breakpoint was put at the start of the conditional.
+    gdb_test "break multi_line_if_conditional" \
+	"Breakpoint.*at.* file .*$::srcfile, line $::bp_location3\\." \
+	"breakpoint at start of multi line if conditional"
+
+    gdb_test "break multi_line_while_conditional" \
+	"Breakpoint.*at.* file .*$::srcfile, line $::bp_location4\\." \
+	"breakpoint at start of multi line while conditional"
+
+    gdb_test "info break" \
+	[multi_line "Num     Type\[ \]+Disp Enb Address\[ \]+What.*" \
+		    "$::decimal\[\t \]+breakpoint     keep y.* in [func main] at .*$::srcfile:$::main_line.*" \
+		    "$::decimal\[\t \]+breakpoint     keep y.* in [func marker2] at .*$::srcfile1:$::bp_location8.*" \
+		    "$::decimal\[\t \]+breakpoint     keep y.* in [func factorial] at .*$::srcfile:$::bp_location7.*" \
+		    "$::decimal\[\t \]+breakpoint     keep y.* in [func main] at .*$::srcfile:$::bp_location1.*" \
+		    "$::decimal\[\t \]+breakpoint     keep y.* in [func main] at .*$::srcfile:$::bp_location1.*" \
+		    "$::decimal\[\t \]+breakpoint     keep y.* in [func main] at .*$::srcfile:$::bp_location2.*" \
+		    "$::decimal\[\t \]+breakpoint     keep y.* in [func multi_line_if_conditional] at .*$::srcfile:$::bp_location3.*" \
+		    "$::decimal\[\t \]+breakpoint     keep y.* in [func multi_line_while_conditional] at .*$::srcfile:$::bp_location4"] \
+	"breakpoint info"
+
+    #
+    # Test info breakpoint with arguments
+    #
+
+    set see1 0
+    set see2 0
+    set see3 0
+    set see4 0
+    set see5 0
+    set see6 0
+
+    gdb_test_multiple "info break 2 4 6" "info break 2 4 6" {
+	-re "1\[\t \]+breakpoint *keep y\[^\r\n\]*:$::main_line\[^\r\n\]*" {
+	    set see1 1
+	    exp_continue
+	}
+	-re "2\[\t \]+breakpoint *keep y\[^\r\n\]* in [func marker2] at \[^\r\n\]*" {
+	    set see2 1
+	    exp_continue
+	}
+	-re "3\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location7\[^\r\n\]*" {
+	    set see3 1
+	    exp_continue
+	}
+	-re "4\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
+	    set see4 1
+	    exp_continue
+	}
+	-re "5\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
+	    set see5 1
+	    exp_continue
+	}
+	-re "6\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location2\[^\r\n\]*" {
+	    set see6 1
+	    exp_continue
+	}
+	-re ".*$::gdb_prompt $" {
+	    if {!$see1 && $see2 && !$see3 && $see4 && !$see5 && $see6} {
+		pass "info break 2 4 6"
+	    } else {
+		fail "info break 2 4 6"
+	    }
+	}
+    }
+
+    set see1 0
+    set see2 0
+    set see3 0
+    set see4 0
+    set see5 0
+    set see6 0
+
+    gdb_test_multiple "info break 3-5" "info break 3-5" {
+	-re "1\[\t \]+breakpoint *keep y.* in [func main] at .*:$::main_line\[^\r\n\]*" {
+	    set see1 1
+	    exp_continue
+	}
+	-re "2\[\t \]+breakpoint *keep y\[^\r\n\]* in [func marker2] at \[^\r\n\]*" {
+	    set see2 1
+	    exp_continue
+	}
+	-re "3\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location7\[^\r\n\]*" {
+	    set see3 1
+	    exp_continue
+	}
+	-re "4\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
+	    set see4 1
+	    exp_continue
+	}
+	-re "5\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
+	    set see5 1
+	    exp_continue
+	}
+	-re "6\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location2\[^\r\n\]*" {
+	    set see6 1
+	    exp_continue
+	}
+	-re ".*$::gdb_prompt $" {
+	    if {!$see1 && !$see2 && $see3 && $see4 && $see5 && !$see6} {
+		pass "info break 3-5"
+	    } else {
+		fail "info break 3-5"
+	    }
+	}
+    }
+
+    #
+    # Test disable/enable with arguments
+    #
+
+    # Test with value history
+
+    with_test_prefix "with value history" {
+	gdb_test "print 1"
+	gdb_test "print 2"
+	gdb_test "print 3"
+	gdb_test "print 4"
+	gdb_test "print 5"
+	gdb_test "print 6"
+
+	# $2 is 2 and $$ is 5
+	gdb_test_no_output "disable \$2 \$\$" "disable using history values"
+
+	set see1 0
+	set see2 0
+	set see3 0
+	set see4 0
+	set see5 0
+	set see6 0
+
+	gdb_test_multiple "info break" "check disable with history values" {
+	    -re "1\[\t \]+breakpoint *keep y.* in [func main] at .*:$::main_line\[^\r\n\]*" {
+		set see1 1
+		exp_continue
+	    }
+	    -re "2\[\t \]+breakpoint *keep n\[^\r\n\]* in [func marker2] at \[^\r\n\]*" {
+		set see2 1
+		exp_continue
+	    }
+	    -re "3\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location7\[^\r\n\]*" {
+		set see3 1
+		exp_continue
+	    }
+	    -re "4\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
+		set see4 1
+		exp_continue
+	    }
+	    -re "5\[\t \]+breakpoint *keep n\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
+		set see5 1
+		exp_continue
+	    }
+	    -re "6\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location2\[^\r\n\]*" {
+		set see6 1
+		exp_continue
+	    }
+	    -re ".*$::gdb_prompt $" {
+		if {$see1 && $see2 && $see3 && $see4 && $see5 && $see6} {
+		    pass "check disable with history values"
+		} else {
+		    fail "check disable with history values"
+		}
+	    }
+	}
+    }
+
+    with_test_prefix "with convenience vars" {
+	gdb_test "enable"
+	gdb_test "set \$foo = 3"
+	gdb_test "set \$bar = 6"
+	gdb_test_no_output "disable \$foo \$bar" "disable with convenience values"
+
+	set see1 0
+	set see2 0
+	set see3 0
+	set see4 0
+	set see5 0
+	set see6 0
+
+	gdb_test_multiple "info break" "check disable with convenience values" {
+	    -re "1\[\t \]+breakpoint *keep y.* in [func main] at .*:$::main_line\[^\r\n\]*" {
+		set see1 1
+		exp_continue
+	    }
+	    -re "2\[\t \]+breakpoint *keep y\[^\r\n\]* in [func marker2] at \[^\r\n\]*" {
+		set see2 1
+		exp_continue
+	    }
+	    -re "3\[\t \]+breakpoint *keep n\[^\r\n\]*$::bp_location7\[^\r\n\]*" {
+		set see3 1
+		exp_continue
+	    }
+	    -re "4\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
+		set see4 1
+		exp_continue
+	    }
+	    -re "5\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
+		set see5 1
+		exp_continue
+	    }
+	    -re "6\[\t \]+breakpoint *keep n\[^\r\n\]*$::bp_location2\[^\r\n\]*" {
+		set see6 1
+		exp_continue
+	    }
+	    -re ".*$::gdb_prompt $" {
+		if {$see1 && $see2 && $see3 && $see4 && $see5 && $see6} {
+		    pass "check disable with convenience values"
+		} else {
+		    fail "check disable with convenience values"
+		}
+	    }
+	}
+    }
+
+    # test with bad values
+
+    with_test_prefix "bad values" {
+	gdb_test "enable"
+	gdb_test "disable 10" "No breakpoint number 10." \
+	    "disable non-existent breakpoint 10"
+
+	gdb_test_no_output "set \$baz = 1.234"
+	gdb_test "disable \$baz" \
+	    "Convenience variable must have integer value.*" \
+	    "disable with non-integer convenience var"
+	gdb_test "disable \$grbx" \
+	    "Convenience variable must have integer value.*" \
+	    "disable with non-existent convenience var"
+	gdb_test "disable \$10" \
+	    "History has not yet reached .10." \
+	    "disable with non-existent history value"
+	gdb_test "disable \$1foo" \
+	    "Convenience variable must have integer value.*" \
+	    "disable with badly formed history value"
+    }
+
+    # FIXME: The rest of this test doesn't work with anything that can't
+    # handle arguments.
+    # Huh? There doesn't *appear* to be anything that passes arguments
+    # below.
+
+    #
+    # run until the breakpoint at main is hit. For non-stubs-using targets.
+    #
+    gdb_run_cmd
+    gdb_test "" \
+	"Breakpoint \[0-9\]+,.*main .*argc.*argv.* at .*$::srcfile:$::bp_location6.*$::bp_location6\[\t \]+\{.*" \
+	"run until function breakpoint"
+
+    # Test the 'list' commands sets current file for the 'break LINENO' command.
+    set bp_marker1 [gdb_get_line_number "set breakpoint 15 here" $::srcfile1]
+    gdb_test "list marker1" ".*"
+    gdb_test "break $bp_marker1" "Breakpoint \[0-9\]+ at 0x\[0-9a-f\]+: file .*$::srcfile1, line ${bp_marker1}\\." \
+	     "break lineno"
+    gdb_test_no_output {delete $bpnum}
+
+    #
+    # run until the breakpoint at a line number
+    #
+    gdb_test continue "Continuing\\..*Breakpoint \[0-9\]+, main \\(argc=.*, argv=.*, envp=.*\\) at .*$::srcfile:$::bp_location1.*$::bp_location1\[\t \]+printf.*factorial.*" \
+			    "run until breakpoint set at a line number"
+
+    #
+    # Run until the breakpoint set in a function in a file
+    #
+	set bp_location7a [gdb_get_line_number "set breakpoint 7a here"]
+	send_gdb "del 3\n"
+	send_gdb "del 7\n"
+	gdb_test "break $bp_location7a" \
+	"Breakpoint.*at.* file .*$::srcfile, line $bp_location7a\\." \
+	"setting breakpoint at 7a"
+
+    for {set i 6} {$i >= 1} {incr i -1} {
+	    gdb_test continue "Continuing\\..*Breakpoint \[0-9\]+, factorial \\(value=$i\\) at .*$::srcfile:$bp_location7a.*$bp_location7a\[\t \]+.*if .value > 1. \{.*" \
+			    "run until file:function($i) breakpoint"
+    }
+
+    send_gdb "break $::bp_location3\n"
+
+	send_gdb "break $::bp_location7\n"
+
+    send_gdb "del 2\n"
+
+    #
+    # run until the file:function breakpoint at a line number in a file
+    #
+    gdb_test continue "Continuing\\..*Breakpoint \[0-9\]+, main \\(argc=.*, argv=.*, envp=.*\\) at .*$::srcfile:$::bp_location2.*$::bp_location2\[\t \]+argc = \\(argc == 12345\\);.*" \
+		    "run until file:linenum breakpoint"
+
+    # Test break at offset +1
+    set bp_location10 [gdb_get_line_number "set breakpoint 10 here"]
+
+    gdb_test "break +1" \
+	"Breakpoint.*at.* file .*$::srcfile, line $bp_location10\\." \
+	"breakpoint offset +1"
+
+    # Check to see if breakpoint is hit when stepped onto
+
+    gdb_test "step" \
+	".*Breakpoint \[0-9\]+, main \\(argc=.*, argv=.*, envp=.*\\) at .*$::srcfile:$bp_location10.*$bp_location10\[\t \]+return argc;.*breakpoint 10 here.*" \
+	"step onto breakpoint"
+}
+
+test_break
+
+proc_with_prefix test_tbreak {} {
+    clean_restart break
+
+    # test temporary breakpoint at function
+    gdb_test "tbreak -q main" "Temporary breakpoint.*at.* file .*$::srcfile, line.*" "temporary breakpoint function"
+
+    # test break at function in file
+    gdb_test "tbreak $::srcfile:factorial" "Temporary breakpoint.*at.* file .*$::srcfile, line.*" \
+	    "Temporary breakpoint function in file"
+
+    # test break at line number
+    gdb_test "tbreak $::bp_location1" \
+	"Temporary breakpoint.*at.* file .*$::srcfile, line $::bp_location1.*" \
+	"temporary breakpoint line number #1"
+
+    gdb_test "tbreak $::bp_location6" "Temporary breakpoint.*at.* file .*$::srcfile, line $::bp_location6.*" "temporary breakpoint line number #2"
+
+    # test break at line number in file
+    gdb_test "tbreak $::srcfile:$::bp_location2" \
+	"Temporary breakpoint.*at.* file .*$::srcfile, line $::bp_location2.*" \
+	"temporary breakpoint line number in file #1"
+
+    gdb_test  "tbreak $::srcfile:$::bp_location11" "Temporary breakpoint.*at.* file .*$::srcfile, line $::bp_location11.*" "Temporary breakpoint line number in file #2"
+
+    # check to see what breakpoints are set (temporary this time)
+    gdb_test "info break" \
+	[multi_line "Num     Type.*Disp Enb Address.*What.*" \
+		    "$::decimal\[\t \]+breakpoint     del.*y.*in [func main] at .*$::srcfile:$::main_line.*" \
+		    "$::decimal\[\t \]+breakpoint     del.*y.*in [func factorial] at .*$::srcfile:$::bp_location7.*" \
+		    "$::decimal\[\t \]+breakpoint     del.*y.*in [func main] at .*$::srcfile:$::bp_location1.*" \
+		    "$::decimal\[\t \]+breakpoint     del.*y.*in [func main] at .*$::srcfile:$::bp_location6.*" \
+		    "$::decimal\[\t \]+breakpoint     del.*y.*in [func main] at .*$::srcfile:$::bp_location2.*" \
+		    "$::decimal\[\t \]+breakpoint     del.*y.*in [func main] at .*$::srcfile:$::bp_location11.*"] \
+	"Temporary breakpoint info"
+}
+
+test_tbreak
+
+# Verify that GDB responds gracefully when asked to set a breakpoint
+# on a nonexistent source line.
+
+proc_with_prefix test_break_nonexistent_line {} {
+    clean_restart break
+
+    if {![runto_main]} {
+	return
+    }
+
+    gdb_test_no_output "set breakpoint pending off"
+    gdb_test "break 999" \
+	"No line 999 in the current file." \
+	"break on non-existent source line"
+}
+
+test_break_nonexistent_line
+
+proc_with_prefix test_break_default {} {
+    clean_restart break
+
+    if {![runto_main]} {
+	return
+    }
+
+    # Run to the desired default location. If not positioned here, the
+    # tests below don't work.
+    #
+    gdb_test "until $::bp_location1" "main .* at .*:$::bp_location1.*" \
+	"until bp_location1"
+
+    # Verify that GDB allows one to just say "break", which is treated
+    # as the "default" breakpoint.  Note that GDB gets cute when printing
+    # the informational message about other breakpoints at the same
+    # location.  We'll hit that bird with this stone too.
+    #
+    gdb_test "break" "Breakpoint \[0-9\]*.*" \
+	"break on default location, 1st time"
+
+    gdb_test "break" \
+	"Note: breakpoint \[0-9\]* also set at .*Breakpoint \[0-9\]*.*" \
+	"break on default location, 2nd time"
+
+    gdb_test "break" \
+	"Note: breakpoints \[0-9\]* and \[0-9\]* also set at .*Breakpoint \[0-9\]*.*" \
+	"break on default location, 3rd time"
+
+    gdb_test "break" \
+	"Note: breakpoints \[0-9\]*, \[0-9\]* and \[0-9\]* also set at .*Breakpoint \[0-9\]*.*" \
+	"break on default location, 4th time"
+
+    # Check setting a breakpoint at the default location with a condition attached.
+    gdb_test "break if (1)" \
+	"Note: breakpoints \[0-9\]*, \[0-9\]*, \[0-9\]* and \[0-9\]* also set at .*Breakpoint \[0-9\]*.*" \
+	"break on the default location, 5th time, but with a condition"
+}
+
+test_break_default
+
+# Verify that a "silent" breakpoint can be set, and that GDB is indeed
+# "silent" about its triggering.
+
+proc_with_prefix test_break_silent_and_more {} {
+    clean_restart break
+
+    if {![runto_main]} {
+	return
+    }
+
+    gdb_test_multiple "break $::bp_location1" \
+	"set to-be-silent break bp_location1" {
+	    -re "Breakpoint (\[0-9\]*) at .*, line $::bp_location1.*$::gdb_prompt $" {
+		set bpno $expect_out(1,string)
+		pass "set to-be-silent break bp_location1"
+	    }
+	}
+
+    gdb_test "commands $bpno\nsilent\nend" ">end" "set silent break bp_location1"
+
+    gdb_test "info break $bpno" \
+	"\[0-9\]*\[ \t\]*breakpoint.*:$::bp_location1\r\n\[ \t\]*silent.*" \
+	"info silent break bp_location1"
+
+    gdb_test "continue" "Continuing." \
+	"hit silent break bp_location1"
+
+    gdb_test "bt" "#0  main .* at .*:$::bp_location1.*" \
+	"stopped for silent break bp_location1"
+
+    # Verify the $_hit_bpnum convenience variable is set to the silent hit bpno.
+    gdb_test "printf \"%d\\n\", \$_hit_bpnum" "$bpno" \
+	"Silent breakpoint hit \$_hit_bpnum is silent $bpno"
+
+    # Verify that GDB can at least parse a breakpoint with the
+    # "thread" keyword.  (We won't attempt to test here that a
+    # thread-specific breakpoint really triggers appropriately.
+    # The gdb.threads subdirectory contains tests for that.)
+    #
+    set bp_location12 [gdb_get_line_number "set breakpoint 12 here"]
+    gdb_test "break $bp_location12 thread 999" "Unknown thread 999.*" \
+	"thread-specific breakpoint on non-existent thread disallowed"
+
+    gdb_test "break $bp_location12 thread foo" \
+	"Invalid thread ID: foo" \
+	"thread-specific breakpoint on bogus thread ID disallowed"
+
+    # Verify that GDB responds gracefully to a breakpoint command with
+    # trailing garbage.
+    #
+    gdb_test "break $bp_location12 foo" \
+	"malformed linespec error: unexpected string, \"foo\".*" \
+	"breakpoint with trailing garbage disallowed"
+
+    # Verify that GDB responds gracefully to a "clear" command that has
+    # no matching breakpoint.  (First, get us off the current source line,
+    # which we know has a breakpoint.)
+    #
+    gdb_test "next" "marker1.*" "step over breakpoint"
+
+    gdb_test "clear 81" "No breakpoint at 81.*" \
+	"clear line has no breakpoint disallowed"
+
+    gdb_test "clear" "No breakpoint at this line.*" \
+	"clear current line has no breakpoint disallowed"
+
+    # Verify that we can set and clear multiple breakpoints.
+    #
+    # We don't test that it deletes the correct breakpoints.  We do at
+    # least test that it deletes more than one breakpoint.
+    #
+    gdb_test "break marker3" "Breakpoint.*at.*" "break marker3 #1"
+    gdb_test "break marker3" "Breakpoint.*at.*" "break marker3 #2"
+    gdb_test "clear marker3" {Deleted breakpoints [0-9]+ [0-9]+.*}
+}
+
+test_break_silent_and_more
+
+# Verify that a breakpoint can be set via a convenience variable.
+
+proc_with_prefix test_break_line_convenience_var {} {
+    clean_restart break
+
+    if { ![runto_main] } {
+	return
+    }
+
+    gdb_test_no_output "set \$foo=$::bp_location11" \
+	"set convenience variable \$foo to bp_location11"
+
+    gdb_test "break \$foo" \
+	"Breakpoint (\[0-9\]*) at .*, line $::bp_location11.*"
+
+    # Verify that GDB responds gracefully to an attempt to set a
+    # breakpoint via a convenience variable whose type is not integer.
+
+    gdb_test_no_output "set \$foo=81.5" \
+	"set convenience variable \$foo to 81.5"
+
+    gdb_test "break \$foo" \
+	"Convenience variables used in line specs must have integer values.*" \
+	"non-integer convenience variable disallowed"
+}
+
+test_break_line_convenience_var
+
+# Verify that we can set and trigger a breakpoint in a user-called function.
+
+proc_with_prefix test_break_user_call {} {
+    clean_restart break
+
+    if { ![runto_main] } {
+	return
+    }
+
+    gdb_test "break marker2" \
+	"Breakpoint (\[0-9\]*) at .*, line $::bp_location8.*" \
+	"set breakpoint on to-be-called function"
+
+    gdb_test "print marker2(99)" \
+	"The program being debugged stopped while in a function called from GDB.\r\nEvaluation of the expression containing the function\r\n.[func marker2]. will be abandoned.\r\nWhen the function is done executing, GDB will silently stop.*" \
+	"hit breakpoint on called function"
+
+    # As long as we're stopped (breakpointed) in a called function,
+    # verify that we can successfully backtrace & such from here.
+    gdb_test "bt" \
+	"#0\[ \t\]*($::hex in )?marker2.*:$::bp_location8\r\n#1\[ \t\]*<function called from gdb>.*" \
+	"backtrace while in called function"
+
+    # Return from the called function.  For remote targets, it's important to do
+    # this before runto_main, which otherwise may silently stop on the dummy
+    # breakpoint inserted by GDB at the program's entry point.
+    #
+    gdb_test_multiple "finish" "finish from called function" {
+	-re "Run till exit from .*marker2.* at .*$::bp_location8\r\n.*function called from gdb.*$::gdb_prompt $" {
+	    pass "finish from called function"
+	}
+	-re "Run till exit from .*marker2.* at .*$::bp_location8\r\n.*Value returned.*$::gdb_prompt $" {
+	    pass "finish from called function"
+	}
+    }
+}
+
+test_break_user_call
+
+# Verify that GDB responds gracefully to a "finish" command with
+# arguments.
+
+proc_with_prefix test_finish_arguments {} {
+    clean_restart break
+
+    if {![runto_main]} {
+	return
+    }
+
+    send_gdb "finish 123\n"
+    gdb_expect {
+      -re "The \"finish\" command does not take any arguments.\r\n$::gdb_prompt $"\
+	      {pass "finish with arguments disallowed"}
+      -re "$::gdb_prompt $"\
+	      {fail "finish with arguments disallowed"}
+      timeout {fail "(timeout) finish with arguments disallowed"}
+    }
+
+    # Verify that GDB responds gracefully to a request to "finish" from
+    # the outermost frame.  On a stub that never exits, this will just
+    # run to the stubs routine, so we don't get this error...  Thus the
+    # second condition.
+    #
+
+    gdb_test_multiple "finish" "finish from outermost frame disallowed" {
+	-re "\"finish\" not meaningful in the outermost frame.\r\n$::gdb_prompt $" {
+	    pass "finish from outermost frame disallowed"
+	}
+	-re "Run till exit from.*\r\n$::gdb_prompt $" {
+	    pass "finish from outermost frame disallowed"
+	}
+    }
+}
+
+test_finish_arguments
+
+#********
+
+
+#
+# Test "next" over recursive function call.
+#
+
+proc_with_prefix test_next_with_recursion {} {
+    global gdb_prompt
+    global decimal
+    global binfile
+
+    gdb_test "kill" "" "kill program" "Kill the program being debugged.*y or n. $" "y"
+    delete_breakpoints
+
+	set bp_location7a [gdb_get_line_number "set breakpoint 7a here"]
+	gdb_test "break $bp_location7a" \
+	"Breakpoint.*at.* file .*$::srcfile, line $bp_location7a\\." \
+	"setting breakpoint at 7a"
+
+    # Run until we call factorial with 6
+
+    gdb_run_cmd
+    gdb_test "" "Break.* factorial .value=6. .*" "run to factorial(6)"
+
+    # Continue until we call factorial recursively with 5.
+
+    gdb_test "continue" \
+	"Continuing.*Break.* factorial .value=5. .*" \
+	"continue to factorial(5)"
+
+    # Do a backtrace just to confirm how many levels deep we are.
+
+    gdb_test "backtrace" \
+	"#0\[ \t\]+ factorial .value=5..*" \
+	"backtrace from factorial(5)"
+
+    # Now a "next" should position us at the recursive call, which
+    # we will be performing with 4.
+
+    gdb_test "next" \
+	".* factorial .value - 1.;.*" \
+	"next to recursive call"
+
+    # Disable the breakpoint at the entry to factorial by deleting them all.
+    # The "next" should run until we return to the next line from this
+    # recursive call to factorial with 4.
+    # Buggy versions of gdb will stop instead at the innermost frame on
+    # the line where we are trying to "next" to.
+
+    delete_breakpoints
+
+    if [istarget "mips*tx39-*"] {
+	set timeout 60
+    }
+    # We used to set timeout here for all other targets as well.  This
+    # is almost certainly wrong.  The proper timeout depends on the
+    # target system in use, and how we communicate with it, so there
+    # is no single value appropriate for all targets.  The timeout
+    # should be established by the Dejagnu config file(s) for the
+    # board, and respected by the test suite.
+    #
+    # For example, if I'm running GDB over an SSH tunnel talking to a
+    # portmaster in California talking to an ancient 68k board running
+    # a crummy ROM monitor (a situation I can only wish were
+    # hypothetical), then I need a large timeout.  But that's not the
+    # kind of knowledge that belongs in this file.
+
+    gdb_test next "\[0-9\]*\[\t \]+return \\(value\\);.*" \
+	    "next over recursive call"
+
+    # OK, we should be back in the same stack frame we started from.
+    # Do a backtrace just to confirm.
+
+    gdb_test "backtrace" \
+	    "#0\[ \t\]+ factorial .value=120.*\r\n#1\[ \t\]+ \[0-9a-fx\]+ in factorial .value=6..*" \
+	    "backtrace from factorial(5.1)"
+
+    if { ![target_info exists gdb,noresults] } {
+	gdb_continue_to_end "recursive next test"
+    }
+}
+
+test_next_with_recursion
+
+
+#********
+
+# build a new file with optimization enabled so that we can try breakpoints
+# on targets with optimized prologues
+
+if { [build_executable "failed to prepare" "breako2" {break.c break1.c} {debug nowarnings optimize=-O2}] } {
+    return -1
+}
+
+proc_with_prefix test_break_optimized_prologue {} {
+    clean_restart breako2
+
+    # test break at function
+    gdb_test "break -q main" \
+	"Breakpoint.*at.* file .*, line.*" \
+	"breakpoint function, optimized file"
+
+    # test break at function
+    gdb_test "break marker4" \
+	"Breakpoint.*at.* file .*$::srcfile1, line.*" \
+	"breakpoint small function, optimized file"
+
+    # run until the breakpoint at main is hit. For non-stubs-using targets.
+    gdb_run_cmd
+
+    set test "run until function breakpoint, optimized file"
+    gdb_test_multiple "" $test {
+	-re "Breakpoint \[0-9\]+,.*main .*argc.*argv.* at .*$::srcfile:$::bp_location6.*$::bp_location6\[\t \]+\{ if .argc.* \{.*$::gdb_prompt $" {
+	    pass $test
+	}
+	-re "Breakpoint \[0-9\]+,.*main .*argc.*argv.* at .*$::gdb_prompt $" {
+	    pass "$test (code motion)"
+	}
+    }
+
+    # run until the breakpoint at a small function
+    #
+    # Add a second pass pattern.  The behavior differs here between stabs
+    # and dwarf for one-line functions.  Stabs preserves two line symbols
+    # (one before the prologue and one after) with the same line number,
+    # but dwarf regards these as duplicates and discards one of them.
+    # Therefore the address after the prologue (where the breakpoint is)
+    # has no exactly matching line symbol, and GDB reports the breakpoint
+    # as if it were in the middle of a line rather than at the beginning.
+
+    set bp_location14 [gdb_get_line_number "set breakpoint 14 here" $::srcfile1]
+
+    gdb_test_multiple "continue" \
+	"run until breakpoint set at small function, optimized file" {
+	    -re "Breakpoint $::decimal, marker4 \\(d=(d@entry=)?177601976\\) at .*$::srcfile1:$bp_location14\[\r\n\]+$bp_location14\[\t \]+void marker4.*" {
+		pass "run until breakpoint set at small function, optimized file (line bp_location14)"
+	    }
+	    -re "Breakpoint $::decimal, factorial \\(.*\\) .*\{\r\n$::gdb_prompt" {
+		# GCC 4.3 emits bad line number information - see gcc/36748.
+		if { [test_compiler_info "gcc-4-3-*"] } {
+		    setup_xfail *-*-*
+		}
+		fail "run until breakpoint set at small function, optimized file"
+	    }
+	}
+}
+
+test_break_optimized_prologue
+
+# test that 'rbreak' on a symbol that may be from a shared library doesn't
+# cause a "Junk at end of arguments." error.
+#
+# On x86 GNU/Linux, this test will choke on e.g. __libc_start_main@plt.
+#
+# Note that this test won't necessarily choke on all targets even if
+# all the rbreak issue is present.  rbreak needs to match and set a
+# breakpoint on a symbol causes 'break' to choke.
+
+proc_with_prefix test_rbreak_shlib {} {
+    clean_restart breako2
+
+    gdb_test_no_output "set breakpoint pending on" "rbreak junk pending setup"
+
+    # We expect at least one breakpoint to be set when we "rbreak main".
+    gdb_test "rbreak main" \
+	".*Breakpoint.*at.* file .*$::srcfile, line.*"
+
+    # Run to a breakpoint.  Fail if we see "Junk at end of arguments".
+    gdb_run_cmd
+
+    gdb_test_multiple "" "rbreak junk" {
+	-re -wrap "Junk at end of arguments.*" {
+	    fail $gdb_test_name
+	}
+	-re -wrap ".*Breakpoint \[0-9\]+,.*" {
+	    pass $gdb_test_name
+	}
+    }
+}
+
+test_rbreak_shlib
+
+# Test break via convenience variable with file name
+
+proc_with_prefix test_break_file_line_convenience_var {} {
+    clean_restart breako2
+
+    set line [gdb_get_line_number "set breakpoint 1 here"]
+    gdb_test_no_output "set \$l = $line"
+
+    set line_actual "-1"
+    set test "break $::srcfile:\$l"
+    gdb_test_multiple "$test" $test {
+	-re "Breakpoint $::decimal at $::hex: file .*break\\.c, line ($::decimal)\\.\r\n$::gdb_prompt $" {
+	    # Save the actual line number on which the breakpoint was
+	    # actually set. On some systems (Eg: Ubuntu 16.04 with GCC
+	    # version 5.4.0), that line gets completely inlined, including
+	    # the call to printf, and so we end up inserting the breakpoint
+	    # on one of the following lines instead.
+	    set line_actual $expect_out(1,string)
+	    pass $test
+	}
+    }
+
+    gdb_test_no_output "set \$foo=81.5" \
+	"set convenience variable \$foo to 81.5"
+    gdb_test "break $::srcfile:\$foo" \
+	"Convenience variables used in line specs must have integer values.*" \
+	"non-integer convenience variable disallowed"
+}
+
+test_break_file_line_convenience_var
+
+# Test that commands can be cleared without error.
+
+proc_with_prefix test_break_commands_clear {} {
+    clean_restart breako2
+
+    set line [gdb_get_line_number "set breakpoint 1 here"]
+    gdb_breakpoint $line
+
+    gdb_test "commands\nprint 232323\nend" ">end" "set some breakpoint commands"
+    gdb_test "commands\nend" ">end" "clear breakpoint commands"
+
+    # We verify that the commands were cleared by ensuring that the last
+    # breakpoint's location ends the output -- if there were commands,
+    # they would have been printed after the location.
+    gdb_test "info break" "$::srcfile:$::decimal" "verify that they were cleared"
+}
+
+test_break_commands_clear
diff --git a/gdb/testsuite/gdb.mdebug/break1.c b/gdb/testsuite/gdb.mdebug/break1.c
new file mode 100644
index 00000000000..5382ec06fa9
--- /dev/null
+++ b/gdb/testsuite/gdb.mdebug/break1.c
@@ -0,0 +1,59 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 1992-2023 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* The code for this file was extracted from the gdb testsuite
+   testcase "break.c". */
+
+/* A structure we use for field name completion tests.  */
+struct some_struct
+{
+  int a_field;
+  int b_field;
+  union { int z_field; };
+};
+
+struct some_struct values[50];
+
+/* Some definitions for tag completion.  */
+enum some_enum { VALUE };
+
+enum some_enum some_enum_global;
+
+union some_union
+{
+  int f1;
+  double f2;
+};
+
+union some_union some_union_global;
+
+/* A variable with a name "similar" to the above struct, to test that
+   tag completion works ok.  */
+int some_variable;
+
+/* The following functions do nothing useful.  They are included
+   simply as places to try setting breakpoints at.  They are
+   explicitly "one-line functions" to verify that this case works
+   (some versions of gcc have or have had problems with this).
+
+   These functions are in a separate source file to prevent an
+   optimizing compiler from inlining them and optimizing them away. */
+
+int marker1 (void) { return (0); }	/* set breakpoint 15 here */
+int marker2 (int a) { return (1); }	/* set breakpoint 8 here */
+void marker3 (char *a, char *b) {}	/* set breakpoint 17 here */
+void marker4 (long d) { values[0].a_field = d; }	/* set breakpoint 14 here */
diff --git a/gdb/testsuite/gdb.mdebug/info-types-c.exp b/gdb/testsuite/gdb.mdebug/info-types-c.exp
new file mode 100644
index 00000000000..d8c3abeade0
--- /dev/null
+++ b/gdb/testsuite/gdb.mdebug/info-types-c.exp
@@ -0,0 +1,67 @@
+# Copyright 2019-2023 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+require {info exists MDEBUG}
+
+global testfile
+global srcfile
+global binfile
+global subdir
+global srcdir
+global compile_flags
+
+standard_testfile info-types.c
+
+if {[prepare_for_testing "failed to prepare" \
+     "${testfile}" $srcfile "debug c"]} {
+return -1
+}
+
+gdb_test_no_output "set auto-solib-add off"
+
+if ![runto_main] then {
+return 0
+}
+
+set file_re "File .*[string_to_regexp $srcfile]:"\
+
+set output_lines \
+    [list \
+	 "^All defined types:" \
+	 ".*" \
+	 $file_re \
+	 "\[\t \]+typedef enum {\\.\\.\\.} anon_enum_t;" \
+	 "\[\t \]+typedef struct {\\.\\.\\.} anon_struct_t;" \
+	 "\[\t \]+typedef union {\\.\\.\\.} anon_union_t;" \
+	 "\[\t \]+typedef struct baz_t baz;" \
+	 "\[\t \]+typedef struct baz_t \\* baz_ptr;" \
+	 "\[\t \]+typedef struct baz_t baz_t;" \
+	 "\[\t \]+enum enum_t;" \
+	 "\[\t \]+typedef enum enum_t my_enum_t;" \
+	 "\[\t \]+typedef float my_float_t;" \
+	 "\[\t \]+typedef int my_int_t;" \
+	 "\[\t \]+typedef enum {\\.\\.\\.} nested_anon_enum_t;" \
+	 "\[\t \]+typedef struct {\\.\\.\\.} nested_anon_struct_t;" \
+	 "\[\t \]+typedef union {\\.\\.\\.} nested_anon_union_t;" \
+	 "\[\t \]+typedef struct baz_t nested_baz;" \
+	 "\[\t \]+typedef struct baz_t nested_baz_t;" \
+	 "\[\t \]+typedef enum enum_t nested_enum_t;" \
+	 "\[\t \]+typedef float nested_float_t;" \
+	 "\[\t \]+typedef int nested_int_t;" \
+	 "\[\t \]+typedef union union_t nested_union_t;" \
+	 "\[\t \]+union union_t;" \
+	 "($|\r\n.*)"]
+
+gdb_test_lines "info types" "" [multi_line {*}$output_lines]
diff --git a/gdb/testsuite/gdb.mdebug/info-types.c b/gdb/testsuite/gdb.mdebug/info-types.c
new file mode 100644
index 00000000000..15a74b3016a
--- /dev/null
+++ b/gdb/testsuite/gdb.mdebug/info-types.c
@@ -0,0 +1,116 @@
+/* Copyright 2019-2023 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+typedef int my_int_t;
+typedef float my_float_t;
+typedef my_int_t nested_int_t;
+typedef my_float_t nested_float_t;
+
+struct baz_t
+{
+  float f;
+  double d;
+};
+
+typedef struct baz_t baz_t;
+typedef struct baz_t baz;
+typedef baz_t nested_baz_t;
+typedef baz nested_baz;
+typedef struct baz_t *baz_ptr;
+
+enum enum_t
+{
+ AA, BB, CC
+};
+
+typedef enum enum_t my_enum_t;
+typedef my_enum_t nested_enum_t;
+
+typedef struct
+{
+  double d;
+  float f;
+} anon_struct_t;
+
+typedef anon_struct_t nested_anon_struct_t;
+
+typedef enum
+{
+ DD, EE, FF
+} anon_enum_t;
+
+typedef anon_enum_t nested_anon_enum_t;
+
+union union_t
+{
+  int i;
+  float f;
+};
+
+typedef union union_t nested_union_t;
+
+typedef union
+{
+  int i;
+  double d;
+} anon_union_t;
+
+typedef anon_union_t nested_anon_union_t;
+
+volatile int var_a;
+volatile float var_b;
+volatile my_int_t var_c;
+volatile my_float_t var_d;
+volatile nested_int_t var_e;
+volatile nested_float_t var_f;
+volatile struct baz_t var_g;
+volatile baz_t var_h;
+volatile baz var_i;
+volatile nested_baz_t var_j;
+volatile nested_baz var_k;
+volatile baz_ptr var_l;
+volatile enum enum_t var_m;
+volatile my_enum_t var_n;
+volatile nested_enum_t var_o;
+volatile anon_struct_t var_p;
+volatile nested_anon_struct_t var_q;
+volatile anon_enum_t var_r;
+volatile nested_anon_enum_t var_s;
+volatile union union_t var_t;
+volatile nested_union_t var_u;
+volatile anon_union_t var_v;
+volatile nested_anon_union_t var_w;
+
+#ifdef __cplusplus
+
+class CL
+{
+  int a;
+};
+
+typedef CL my_cl;
+typedef CL *my_ptr;
+
+volatile CL var_cpp_a;
+volatile my_cl var_cpp_b;
+volatile my_ptr var_cpp_c;
+
+#endif /* __cplusplus */
+
+int
+main ()
+{
+  return 0;
+}
-- 
2.41.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [RFC][PATCH?] fixed some segfaults and bugs in mdebug support
  2023-12-25  5:42                 ` Zeck S
  2024-02-07 13:33                   ` [PATCH] mdebug fix Zeck S
@ 2024-02-16  2:45                   ` Zeck S
  2024-03-13  2:09                     ` Zeck S
  1 sibling, 1 reply; 15+ messages in thread
From: Zeck S @ 2024-02-16  2:45 UTC (permalink / raw)
  To: gdb-patches; +Cc: Zeck S

Tried to send this a week ago, but I see somehow send email did not thread it correctly.
Trying again, I think it was because the subject line was messed up.

Addressed comments, adapted some core tests, fixed more bugs.
In addition to the modified tests, I specifically also got
gdb.base/relocate.exp working to sort out some problems.

As always, glad to address feedback, and thanks for the patience.

I reverted some of my code around parse_lines addresses.
The Procedure Descriptor Record format is weird.
The File Descriptor Record has the final correct address
of the LOWEST PDR, only. This is used as a base address
for a kind of janky fixup/relocation system.
I suspect originally the PDR addresses were directly useful in simpler
formats.

Based on comments in bfd ecofflink and documentation about something
similar in
"MIPS Mdebug Debugging Information" by David Anderson
that describes HDRR offsets as offsets from the beginning of the a.out
as a "design mistake" I think some compilers
set the PDR address to a location in an object file.

This means there's basically a garbage offset added to all the PDRs.
So all PDRs have their addresses subtracted from the lowest to kind
treat it as 0, and then the lowest PDRs REAL correct address
is used from the FDR, as an offset.

IDO actually always outputs the lowest PDR's address as 0
so subtracting it out does nothing, but hurts nothing.

I can find no real documentation about this.

I did find precedent for what my reverted code was doing in
section 5.3.4.1 of ""Tru64 UNIX Object File and Symbol Table Format Specification"
but after I figured out what the original code was doing I
reverted those changes to distrub the code less.

---
 gdb/mdebugread.c                          | 230 ++++--
 gdb/testsuite/boards/qemu-user.exp        | 267 +++++++
 gdb/testsuite/gdb.mdebug/README           |  67 ++
 gdb/testsuite/gdb.mdebug/break.c          |  92 +++
 gdb/testsuite/gdb.mdebug/break.exp        | 921 ++++++++++++++++++++++
 gdb/testsuite/gdb.mdebug/break1.c         |  59 ++
 gdb/testsuite/gdb.mdebug/info-types-c.exp |  67 ++
 gdb/testsuite/gdb.mdebug/info-types.c     | 116 +++
 8 files changed, 1770 insertions(+), 49 deletions(-)
 create mode 100644 gdb/testsuite/boards/qemu-user.exp
 create mode 100644 gdb/testsuite/gdb.mdebug/README
 create mode 100644 gdb/testsuite/gdb.mdebug/break.c
 create mode 100644 gdb/testsuite/gdb.mdebug/break.exp
 create mode 100644 gdb/testsuite/gdb.mdebug/break1.c
 create mode 100644 gdb/testsuite/gdb.mdebug/info-types-c.exp
 create mode 100644 gdb/testsuite/gdb.mdebug/info-types.c

diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
index cd6638224e7..0118507a8f3 100644
--- a/gdb/mdebugread.c
+++ b/gdb/mdebugread.c
@@ -237,7 +237,7 @@ static struct type *new_type (char *);
 enum block_type { FUNCTION_BLOCK, NON_FUNCTION_BLOCK };
 
 static struct block *new_block (struct objfile *objfile,
-				enum block_type, enum language);
+				enum block_type, enum language, bool is_global);
 
 static struct compunit_symtab *new_symtab (const char *, int, struct objfile *);
 
@@ -246,7 +246,7 @@ static struct linetable *new_linetable (int);
 static struct blockvector *new_bvect (int);
 
 static struct type *parse_type (int, union aux_ext *, unsigned int, int *,
-				int, const char *);
+				int, const char *, bool);
 
 static struct symbol *mylookup_symbol (const char *, const struct block *,
 				       domain_enum, enum address_class);
@@ -572,7 +572,7 @@ add_data_symbol (SYMR *sh, union aux_ext *ax, int bigend,
       || sh->sc == scNil || sh->index == indexNil)
     s->set_type (builtin_type (objfile)->nodebug_data_symbol);
   else
-    s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name));
+    s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name, false));
   /* Value of a data symbol is its memory address.  */
 }
 
@@ -624,8 +624,17 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
       break;
     }
 
-  if (section_index != -1)
-    sh->value += section_offsets[section_index];
+  if (section_index != -1) {
+	int offset = section_offsets[section_index];
+    if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+    {
+	  /* This is attempting to detect .o files.
+	     Their sections are relocated in symfile.c default_symfile_offsets
+	     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+	  offset += objfile->sections_start[section_index].the_bfd_section->vma;
+    }
+	sh->value += offset;
+  }
 
   switch (sh->st)
     {
@@ -705,7 +714,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	  break;
 	}
       s->set_value_longest (svalue);
-      s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name));
+      s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name, false));
       add_symbol (s, top_stack->cur_st, top_stack->cur_block);
       break;
 
@@ -761,7 +770,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	t = builtin_type (objfile)->builtin_int;
       else
 	{
-	  t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name);
+	  t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name, false);
 	  if (strcmp (name, "malloc") == 0
 	      && t->code () == TYPE_CODE_VOID)
 	    {
@@ -805,7 +814,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	s->type ()->set_is_prototyped (true);
 
       /* Create and enter a new lexical context.  */
-      b = new_block (objfile, FUNCTION_BLOCK, s->language ());
+      b = new_block (objfile, FUNCTION_BLOCK, s->language (), false);
       s->set_value_block (b);
       b->set_function (s);
       b->set_start (sh->value);
@@ -1135,7 +1144,13 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	}
 
       top_stack->blocktype = stBlock;
-      b = new_block (objfile, NON_FUNCTION_BLOCK, psymtab_language);
+
+	  /* Using psymtab_langauge fixes commit 76fc0f62138e.
+	     This file does not use buildsym-lecacy.
+	     start_compunit_symtab () is never called.
+	     get_current_subfile () will crash because
+	     buildsym_compunit has never been initialized. */
+      b = new_block (objfile, NON_FUNCTION_BLOCK, psymtab_language, false);
       b->set_start (sh->value + top_stack->procadr);
       b->set_superblock (top_stack->cur_block);
       top_stack->cur_block = b;
@@ -1247,7 +1262,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
 	f->set_loc_bitpos (sh->value);
 	bitsize = 0;
 	f->set_type (parse_type (cur_fd, ax, sh->index, &bitsize, bigend,
-				 name));
+				 name, false));
 	f->set_bitsize (bitsize);
       }
       break;
@@ -1269,7 +1284,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char *ext_sh, int bigend,
       pend = is_pending_symbol (cur_fdr, ext_sh);
       if (pend == NULL)
 	{
-	  t = parse_type (cur_fd, ax, sh->index, NULL, bigend, name);
+	  t = parse_type (cur_fd, ax, sh->index, NULL, bigend, name, true);
 	  add_pending (cur_fdr, ext_sh, t);
 	}
       else
@@ -1382,7 +1397,7 @@ basic_type (int bt, struct objfile *objfile)
   if (map_bt[bt])
     return map_bt[bt];
 
-  type_allocator alloc (objfile, get_current_subfile ()->language);
+  type_allocator alloc (objfile, psymtab_language);
 
   switch (bt)
     {
@@ -1514,7 +1529,7 @@ basic_type (int bt, struct objfile *objfile)
 
 static struct type *
 parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
-	    int bigend, const char *sym_name)
+	    int bigend, const char *sym_name, bool is_stTypedef)
 {
   TIR t[1];
   struct type *tp = 0;
@@ -1571,7 +1586,7 @@ parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
 	}
     }
 
-  type_allocator alloc (mdebugread_objfile, get_current_subfile ()->language);
+  type_allocator alloc (mdebugread_objfile, psymtab_language);
 
   /* Move on to next aux.  */
   ax++;
@@ -1628,7 +1643,7 @@ parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
       xref_fh = get_rfd (fd, rf);
       xref_fd = xref_fh - debug_info->fdr;
       tp = parse_type (xref_fd, debug_info->external_aux + xref_fh->iauxBase,
-		    rn->index, NULL, xref_fh->fBigendian, sym_name);
+		    rn->index, NULL, xref_fh->fBigendian, sym_name, false);
     }
 
   /* All these types really point to some (common) MIPS type
@@ -1785,6 +1800,13 @@ parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
   if (t->continued)
     complaint (_("illegal TIR continued for %s"), sym_name);
 
+  if (is_stTypedef)
+    {
+	struct type *wrap = alloc.new_type (TYPE_CODE_TYPEDEF, 0, sym_name);
+	wrap->set_target_type(tp);
+	tp = wrap;
+    }
+
   return tp;
 }
 
@@ -1839,7 +1861,7 @@ upgrade_type (int fd, struct type **tpp, int tq, union aux_ext *ax, int bigend,
 
       indx = parse_type (fh - debug_info->fdr,
 			 debug_info->external_aux + fh->iauxBase,
-			 id, NULL, bigend, sym_name);
+			 id, NULL, bigend, sym_name, false);
 
       /* The bounds type should be an integer type, but might be anything
 	 else due to corrupt aux entries.  */
@@ -2155,7 +2177,7 @@ parse_external (EXTR *es, int bigend, const section_offsets &section_offsets,
 
 static void
 parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines,
-	     CORE_ADDR lowest_pdr_addr)
+			   CORE_ADDR textlow, CORE_ADDR lowest_pdr_addr)
 {
   unsigned char *base;
   int j, k;
@@ -2169,7 +2191,6 @@ parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines,
   for (j = 0; j < fh->cpd; j++, pr++)
     {
       CORE_ADDR l;
-      CORE_ADDR adr;
       unsigned char *halt;
 
       /* No code for this one.  */
@@ -2186,9 +2207,15 @@ parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines,
 	halt = base + fh->cbLine;
       base += pr->cbLineOffset;
 
-      adr = pr->adr - lowest_pdr_addr;
-
-      l = adr >> 2;		/* in words */
+      /* textlow is the FDR->adr, for the file containing these PDRs.
+	  FDR->adr is the absolute address of the lowest PDR.
+	  PDR->adr's themselves, are relative offsets.
+	  The PDR->adr's may start at 0, or they could have an offset
+	  based on the PDRs position in an object file.
+	  This is why the lowest PDR address is subtracted
+	  from all other PDRs addresses, to subtract out the potential offset.
+	  See bfd/ecofflink.c comments. */
+      l = (textlow +  pr->adr - lowest_pdr_addr) >> 2;		/* in words */
       for (lineno = pr->lnLow; base < halt;)
 	{
 	  count = *base & 0x0f;
@@ -3047,28 +3074,49 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 		       index into the namestring which indicates the
 		       debugging type symbol.  */
 
+			int section;
+			bfd_vma address;
+
 		    switch (p[1])
 		      {
 		      case 'S':
+			  section = SECT_OFF_DATA (objfile);
+			  address = sh.value;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			     Their sections are relocated in symfile.c default_symfile_offsets
+			     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			  address += objfile->sections_start[section].the_bfd_section->vma;
+			}
 			pst->add_psymbol (gdb::string_view (namestring,
 							    p - namestring),
 					  true, VAR_DOMAIN, LOC_STATIC,
-					  SECT_OFF_DATA (objfile),
+					  section,
 					  psymbol_placement::STATIC,
-					  unrelocated_addr (sh.value),
+					  unrelocated_addr (address),
 					  psymtab_language,
 					  partial_symtabs, objfile);
 			continue;
 		      case 'G':
+			  section = SECT_OFF_DATA (objfile);
+			  address = sh.value;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			    Their sections are relocated in symfile.c default_symfile_offsets
+			    but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			  address += objfile->sections_start[section].the_bfd_section->vma;
+			}
 			/* The addresses in these entries are reported
 			   to be wrong.  See the code that reads 'G's
 			   for symtabs.  */
 			pst->add_psymbol (gdb::string_view (namestring,
 							    p - namestring),
 					  true, VAR_DOMAIN, LOC_STATIC,
-					  SECT_OFF_DATA (objfile),
+					  section,
 					  psymbol_placement::GLOBAL,
-					  unrelocated_addr (sh.value),
+					  unrelocated_addr (address),
 					  psymtab_language,
 					  partial_symtabs, objfile);
 			continue;
@@ -3215,12 +3263,21 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 			    function_outside_compilation_unit_complaint
 			      (copy.c_str ());
 			  }
+			section = SECT_OFF_TEXT (objfile);
+			address = sh.value;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			     Their sections are relocated in symfile.c default_symfile_offsets
+			     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			  address += objfile->sections_start[section].the_bfd_section->vma;
+			}
 			pst->add_psymbol (gdb::string_view (namestring,
 							    p - namestring),
 					  true, VAR_DOMAIN, LOC_BLOCK,
-					  SECT_OFF_TEXT (objfile),
+					  section,
 					  psymbol_placement::STATIC,
-					  unrelocated_addr (sh.value),
+					  unrelocated_addr (address),
 					  psymtab_language,
 					  partial_symtabs, objfile);
 			continue;
@@ -3236,12 +3293,21 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 			    function_outside_compilation_unit_complaint
 			      (copy.c_str ());
 			  }
+			section = SECT_OFF_TEXT (objfile);
+			address = sh.value;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			     Their sections are relocated in symfile.c default_symfile_offsets
+			     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			  address += objfile->sections_start[section].the_bfd_section->vma;
+			}
 			pst->add_psymbol (gdb::string_view (namestring,
 							    p - namestring),
 					  true, VAR_DOMAIN, LOC_BLOCK,
-					  SECT_OFF_TEXT (objfile),
+					  section,
 					  psymbol_placement::GLOBAL,
-					  unrelocated_addr (sh.value),
+					  unrelocated_addr (address),
 					  psymtab_language,
 					  partial_symtabs, objfile);
 			continue;
@@ -3419,6 +3485,9 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 		  break;
 		}
 
+		bfd_vma address;
+		/* some symbols don't use the above section */
+		int section_override;
 	      switch (sh.st)
 		{
 		  unrelocated_addr high;
@@ -3426,9 +3495,18 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 		  int new_sdx;
 
 		case stStaticProc:
-		  reader.record_with_info (sym_name, minsym_value,
+		    section_override = SECT_OFF_TEXT (objfile);
+			address = (bfd_vma)minsym_value;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			     Their sections are relocated in symfile.c default_symfile_offsets
+			     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			  address += objfile->sections_start[section_override].the_bfd_section->vma;
+			}
+		  reader.record_with_info (sym_name, (unrelocated_addr)address,
 					   mst_file_text,
-					   SECT_OFF_TEXT (objfile));
+					   section_override);
 
 		  /* FALLTHROUGH */
 
@@ -3476,12 +3554,21 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 		     still able to find the PROGRAM name via the partial
 		     symbol table, and the MAIN__ symbol via the minimal
 		     symbol table.  */
+
+		  address = sh.value;
+		  if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+		  {
+			 /* This is attempting to detect .o files.
+			 Their sections are relocated in symfile.c default_symfile_offsets
+			 but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			 address += objfile->sections_start[section].the_bfd_section->vma;
+		  }
 		  if (sh.st == stProc)
 		    pst->add_psymbol (sym_name, true,
 				      VAR_DOMAIN, LOC_BLOCK,
 				      section,
 				      psymbol_placement::GLOBAL,
-				      unrelocated_addr (sh.value),
+				      unrelocated_addr (address),
 				      psymtab_language,
 				      partial_symtabs, objfile);
 		  else
@@ -3489,7 +3576,7 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 				      VAR_DOMAIN, LOC_BLOCK,
 				      section,
 				      psymbol_placement::STATIC,
-				      unrelocated_addr (sh.value),
+				      unrelocated_addr (address),
 				      psymtab_language,
 				      partial_symtabs, objfile);
 
@@ -3517,13 +3604,36 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 
 		case stStatic:	/* Variable */
 		  if (SC_IS_DATA (sh.sc))
-		    reader.record_with_info (sym_name, minsym_value,
+		  {
+			section_override = SECT_OFF_DATA (objfile);
+			address = 0;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			     Their sections are relocated in symfile.c default_symfile_offsets
+			     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			     address += objfile->sections_start[section_override].the_bfd_section->vma;
+			}
+		    reader.record_with_info (sym_name, (unrelocated_addr)(((bfd_vma)minsym_value + address)),
 					     mst_file_data,
-					     SECT_OFF_DATA (objfile));
+					     section_override);
+		  }
 		  else
-		    reader.record_with_info (sym_name, minsym_value,
+		  {
+			section_override = SECT_OFF_BSS (objfile);
+			address = 0;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			     Their sections are relocated in symfile.c default_symfile_offsets
+			     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			  address += objfile->sections_start[section_override].the_bfd_section->vma;
+			}
+		    reader.record_with_info (sym_name, (unrelocated_addr)(((bfd_vma)minsym_value + address)),
 					     mst_file_bss,
-					     SECT_OFF_BSS (objfile));
+					     section_override);
+		  }
+
 		  theclass = LOC_STATIC;
 		  break;
 
@@ -3596,11 +3706,19 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 		  cur_sdx++;
 		  continue;
 		}
+			address = sh.value;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			     Their sections are relocated in symfile.c default_symfile_offsets
+			     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			  address += objfile->sections_start[section].the_bfd_section->vma;
+			}
 	      /* Use this gdb symbol.  */
 	      pst->add_psymbol (sym_name, true,
 				VAR_DOMAIN, theclass, section,
 				psymbol_placement::STATIC,
-				unrelocated_addr (sh.value),
+				unrelocated_addr (address),
 				psymtab_language,
 				partial_symtabs, objfile);
 	    skip:
@@ -3676,12 +3794,20 @@ parse_partial_symbols (minimal_symbol_reader &reader,
 		  theclass = LOC_STATIC;
 		  break;
 		}
+		  bfd_vma address = svalue;
 	      char *sym_name = debug_info->ssext + psh->iss;
+			if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC)) == 0)
+			{
+			  /* This is attempting to detect .o files.
+			     Their sections are relocated in symfile.c default_symfile_offsets
+			     but section_offsets are set to 0 there and the offset is put in the vma.	*/
+			  address += objfile->sections_start[section].the_bfd_section->vma;
+			}
 	      pst->add_psymbol (sym_name, true,
 				VAR_DOMAIN, theclass,
 				section,
 				psymbol_placement::GLOBAL,
-				unrelocated_addr (svalue),
+				unrelocated_addr (address),
 				psymtab_language,
 				partial_symtabs, objfile);
 	    }
@@ -4163,7 +4289,7 @@ mdebug_expand_psymtab (legacy_psymtab *pst, struct objfile *objfile)
 		}
 
 	      parse_lines (fh, pr_block.data (), lines, maxlines,
-			   lowest_pdr_addr);
+			   (CORE_ADDR) pst->unrelocated_text_low (), lowest_pdr_addr);
 	      if (lines->nitems < fh->cline)
 		lines = shrink_linetable (lines);
 
@@ -4291,7 +4417,7 @@ cross_ref (int fd, union aux_ext *ax, struct type **tpp,
       rf = rn->rfd;
     }
 
-  type_allocator alloc (mdebugread_objfile, get_current_subfile ()->language);
+  type_allocator alloc (mdebugread_objfile, psymtab_language);
 
   /* mips cc uses a rf of -1 for opaque struct definitions.
      Set TYPE_STUB for these types so that check_typedef will
@@ -4412,7 +4538,8 @@ cross_ref (int fd, union aux_ext *ax, struct type **tpp,
 				 sh.index,
 				 NULL,
 				 fh->fBigendian,
-				 debug_info->ss + fh->issBase + sh.iss);
+				 debug_info->ss + fh->issBase + sh.iss,
+				 sh.st == stTypedef);
 	      add_pending (fh, esh, *tpp);
 	      break;
 
@@ -4438,7 +4565,8 @@ cross_ref (int fd, union aux_ext *ax, struct type **tpp,
 			     sh.index,
 			     NULL,
 			     fh->fBigendian,
-			     debug_info->ss + fh->issBase + sh.iss);
+			     debug_info->ss + fh->issBase + sh.iss,
+			     true);
 	}
       else
 	{
@@ -4542,6 +4670,8 @@ add_line (struct linetable *lt, int lineno, CORE_ADDR adr, int last)
     return lineno;
 
   lt->item[lt->nitems].line = lineno;
+  lt->item[lt->nitems].is_stmt = 1;
+  lt->item[lt->nitems].prologue_end = 1;
   lt->item[lt->nitems++].set_unrelocated_pc (unrelocated_addr (adr << 2));
   return lineno;
 }
@@ -4634,9 +4764,10 @@ new_symtab (const char *name, int maxlines, struct objfile *objfile)
 
   /* All symtabs must have at least two blocks.  */
   bv = new_bvect (2);
-  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang));
-  bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang));
+  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang, true));
+  bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK, lang, false));
   bv->static_block ()->set_superblock (bv->global_block ());
+  bv->global_block ()->set_compunit_symtab(cust);
   cust->set_blockvector (bv);
 
   cust->set_debugformat ("ECOFF");
@@ -4723,9 +4854,11 @@ new_bvect (int nblocks)
 
 static struct block *
 new_block (struct objfile *objfile, enum block_type type,
-	   enum language language)
+	   enum language language, bool is_global)
 {
-  struct block *retval = new (&objfile->objfile_obstack) block;
+  struct block *retval = (is_global
+    ? new (&objfile->objfile_obstack) global_block
+    : new (&objfile->objfile_obstack) block);
 
   if (type == FUNCTION_BLOCK)
     retval->set_multidict (mdict_create_linear_expandable (language));
@@ -4754,8 +4887,7 @@ new_type (char *name)
 {
   struct type *t;
 
-  t = type_allocator (mdebugread_objfile,
-		      get_current_subfile ()->language).new_type ();
+  t = type_allocator (mdebugread_objfile, psymtab_language).new_type ();
   t->set_name (name);
   INIT_CPLUS_SPECIFIC (t);
   return t;
diff --git a/gdb/testsuite/boards/qemu-user.exp b/gdb/testsuite/boards/qemu-user.exp
new file mode 100644
index 00000000000..8a48ab7f6d4
--- /dev/null
+++ b/gdb/testsuite/boards/qemu-user.exp
@@ -0,0 +1,267 @@
+# Copyright 2020-2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# NOTICE:
+# QEMU user mode gdbstub has multithreading problems
+# https://gitlab.com/qemu-project/qemu/-/issues/1671
+# run with --ignore gdb.threads/access-mem-running-thread-exit.exp
+# since this test in particular seems to hang. Could be related.
+
+# This technique is from native-extended-gdbserver.exp
+# We must load this explicitly here, and rename the procedures we want
+# to override.  If we didn't do this, given that mi-support.exp is
+# loaded later in the test files, the procedures loaded then would
+# override our definitions.
+load_lib mi-support.exp
+
+set qemu ""
+set qemu_arch ""
+set qemu_user_spawn_id ""
+set qemu_user_last_load_file ""
+set port "2345"
+
+# add sections for other architectures
+# as needed
+case "$target_triplet" in {
+	{ "mips" } {
+		set qemu "qemu-mips"
+		set qemu_arch "mips"
+	}
+	default {
+		puts "No target hardware for $target_triplet"
+	}
+}
+
+# QEMU supports some of extended-remote, but not the run command
+# which causes problems for the test suite
+set_board_info gdb_protocol "remote"
+set_board_info use_gdb_stub 1
+set_board_info gdb,do_reload_on_run 1
+set_board_info exit_is_reliable 1
+# static link so we do not need to configure an QEMU LD prefix and install
+# libraries for the target architecture
+set_board_info ldflags "-static"
+
+# technique from simavr.exp
+#
+# Load executable into GDB
+#
+proc gdb_load { arg } {
+    global gdb_prompt
+    global verbose
+    global spawn_id
+    global qemu
+    global qemu_user_last_load_file
+    global qemu_user_spawn_id
+    global port
+
+    # keep track of last loaded file
+    # to simulate run restart like behavior
+    if { $arg == "" } {
+		set arg $qemu_user_last_load_file
+		if { $arg == ""  } {
+			global last_loaded_file
+			# this fallback is needed
+			# for tests like gdb.base/break-unload-file.exp
+			set arg $last_loaded_file
+		}
+    } else {
+       set qemu_user_last_load_file $arg
+    }
+
+    if { $arg != "" } {
+		if {[gdb_file_cmd $arg]} then { return -1 }
+    }
+
+    # Close any previous qemu user instance.
+    if { $qemu_user_spawn_id != "" } {
+		verbose -log "qemu user: closing previous spawn id $qemu_user_spawn_id"
+		if [catch { close -i $qemu_user_spawn_id } != 0] {
+			warning "qemu user: failed to close connection to previous qemu user instance"
+		}
+
+		# some tests get QEMU
+		# into a state where the process doesn't want to die
+		# so -nowait to not hang the entire test run
+		wait -nowait -i $qemu_user_spawn_id
+		set qemu_user_spawn_id ""
+    }
+
+    # technique from gdbserver-support.exp
+    # Loop till we find a free port.
+    # This is to cope with qemu sometimes getting
+    # into a bad state and not closing.
+    # Do a pkill -f qemu-mips -9 after running tests
+    # to remove stragglers
+    while 1 {
+		# Run QEMU user
+		set cmd "spawn -noecho $qemu -g $port $arg"
+		verbose -log "Spawning qemu user: $cmd"
+		eval $cmd
+		set qemu_user_spawn_id $spawn_id
+
+		# without inferior_spawn_id
+		# tests such as gdb.base/a2-run.exp
+		# can't look at the right process output
+		global inferior_spawn_id
+		set inferior_spawn_id $spawn_id
+
+		expect {
+			-i $qemu_user_spawn_id
+			-timeout 1
+			-re ".*qemu: could not open gdbserver on.*" {
+				verbose -log "Port $port is already in use."
+				if [catch { close -i $qemu_user_spawn_id } != 0] {
+					warning "qemu user: failed to close connection to previous qemu user instance"
+				}
+				# Bump the port number to avoid the conflict.
+				wait -i $qemu_user_spawn_id
+				incr port
+				continue
+			}
+		}
+		break
+	}
+
+    # arch needs set because some tests
+    # take actions that need arch to be correct
+    # immediately, before file command loads the binary
+    # for example gdb.base/break-unload-file.exp
+    global qemu_arch
+    send_gdb "set arch $qemu_arch\n"
+
+    # Connect to qemu user.
+    send_gdb "target remote :$port\n"
+    gdb_expect {
+		# qemu user mode gdb stub does not support non stop mode
+		# this is so tests that attempt it die quickly
+		-re ".*Non-stop mode requested, but remote does not support non-stop.*" {
+			error "qemu user does not support non-stop"
+		}
+		# cannot have multiple targets connected
+		# some tests end up causing this to be attempted in gdb.multi
+		-re ".*Already connected to a remote target.  Disconnect?.*" {
+			send_gdb "y\n"
+			gdb_expect {
+				-re ".*$gdb_prompt $" {
+					error "connected to new target while connected to existing"
+				}
+			}
+		}
+		-re ".*$gdb_prompt $" {
+			if $verbose>1 then {
+				send_user "Connected to QEMU target\n"
+			}
+		}
+		-re "Remote debugging using .*$gdb_prompt $" {
+			verbose "Set target to remote for QEMU"
+		}
+		timeout {
+			verbose -log "qemu-user: unable to connect to qemu user, closing qemu user spawn id"
+			close -i $qemu_user_spawn_id
+			verbose -log "qemu-user: unable to connect to qemu user, waiting for qemu user process exit"
+			wait -i $qemu_user_spawn_id
+			set qemu_user_spawn_id ""
+			error "unable to connect to qemu user stub"
+		}
+	}
+
+	return 0
+}
+
+# technique from native-extended-gdbserver.exp
+# for overriding mi-support.exp procs
+if { [info procs original_mi_gdb_load] == "" } {
+    rename mi_gdb_load original_gdbserver_mi_gdb_load
+}
+
+# same ideas as gdb_load, but adapted for mi
+proc mi_gdb_load { arg } {
+    global gdb_prompt
+    global verbose
+    global spawn_id
+    global qemu
+    global qemu_user_last_load_file
+    global qemu_user_spawn_id
+    global port
+
+    if { $arg == "" } {
+		set arg $qemu_user_last_load_file
+		if { $arg == ""  } {
+			global last_loaded_file
+			set arg $last_loaded_file
+		}
+    } else {
+		set qemu_user_last_load_file $arg
+    }
+
+    if { $arg != "" } {
+		if {[mi_gdb_file_cmd $arg]} then { return -1 }
+    }
+
+    #Close any previous qemu user instance.
+    if { $qemu_user_spawn_id != "" } {
+		verbose -log "qemu user: closing previous spawn id $qemu_user_spawn_id"
+		if [catch { close -i $qemu_user_spawn_id } != 0] {
+			warning "qemu user: failed to close previous qemu user instance"
+		}
+
+		# some tests get QEMU/its gdb stub
+		# into a state where the process doesn't want to die
+		# so -nowait to not hang the entire test run
+		wait -nowait -i $qemu_user_spawn_id
+		set qemu_user_spawn_id ""
+    }
+
+    # technique from gdbserver-support.exp
+    # Loop till we find a free port.
+    while 1 {
+		# Run QEMU user
+		set cmd "spawn -noecho $qemu -g $port $arg"
+		verbose -log "Spawning qemu user: $cmd"
+		eval $cmd
+		set qemu_user_spawn_id $spawn_id
+
+		# without inferior_spawn_id
+		# tests such as gdb.base/a2-run.exp
+		# can't look at the right process output
+		global inferior_spawn_id
+		set inferior_spawn_id $spawn_id
+
+		expect {
+			-i $qemu_user_spawn_id
+			-timeout 1
+			-re ".*qemu: could not open gdbserver on.*" {
+				verbose -log "Port $port is already in use."
+				if [catch { close -i $qemu_user_spawn_id } != 0] {
+					warning "qemu user: failed to close connection to previous qemu user instance"
+				}
+				# Bump the port number to avoid the conflict.
+				wait -i $qemu_user_spawn_id
+				incr port
+				continue
+			}
+		}
+		break
+	}
+
+	return 0
+}
+
+# technique from stdio-gdb-server-base.exp
+proc mi_gdb_target_load { } {
+    global port
+    return [mi_gdb_target_cmd "remote" ":$port"]
+}
diff --git a/gdb/testsuite/gdb.mdebug/README b/gdb/testsuite/gdb.mdebug/README
new file mode 100644
index 00000000000..f749d3e1cbc
--- /dev/null
+++ b/gdb/testsuite/gdb.mdebug/README
@@ -0,0 +1,67 @@
+These tests are adapted for dealing with the limitations of old compilers and the mdebug format.
+
+Execute the mdebug tests with something like:
+runtest gdb.mdebug/info-types-c.exp CC_FOR_TARGET=idowrapper MDEBUG=1 --target mips --target_board qemu-user
+
+The CC_FOR_TARGET is probably going to be a wrapper script
+for an old compiler running in an emulator.
+Example at the end of this readme.
+Put it somewhere in your PATH.
+Do not give the path to the script as CC_FOR_TARGET, that does not work.
+
+The qemu-user board will run mips programs in qemu user mode
+and connect gdb to QEMU's gdb stub.
+
+mdebug does not record line numbers for type definitions.
+This is why the info types tests are modified
+
+mdebug output can be odd in how line mappings deal with the start of functions.
+This is why the break tests are modified.
+
+Most compilers outputting mdebug data are so old they cannot deal with C99 features.
+This makes parsing standard library headers for modern implementations a problem.
+Also, even past that, the headers may eventually lead to compiler specific code
+in things like stddef.h that may not be appriopriate for your old compiler.
+This is why break.c has some modified standard function signatures at the beginning.
+
+A few of the break tests *sometimes* fail still.
+Several memory initialization bugs were fixed but whatever is causing that
+is very inconsistent. Or it could be some issue with the exp file.
+
+The best documentation on mdebug are these documents:
+
+"Tru64 UNIX Object File and Symbol Table Format Specification"
+Sections 5, 9, and 10
+Note, the word "mdebug" is never used, but it is that format
+carried through from DEC to Compaq.
+
+"MIPS Mdebug Debugging Information" by David Anderson
+
+A wrapper script like this was used to create .o files with IDO running in an emulator.
+The final link commands are run with regular linux gcc.
+
+#!/bin/sh
+
+echo "$@" >> theargs
+
+for arg do
+  if [ "$arg" = "-static" ]
+  then
+    link=1
+  fi
+done
+
+if [ "$link" = 1 ]
+then
+  mips-unknown-linux-gnu-gcc "$@" -fno-exceptions
+else
+for arg do
+  shift
+  [ "$arg" = "-fdiagnostics-color=never" ] && continue
+  [ "$arg" = "-g" ] && continue
+  set -- "$@" "$arg"
+done
+
+/home/someuser/qemu-irix/build/irix-linux-user/qemu-irix -L /home/someuser/ido/ido7.1_compiler /home/someuser/ido/ido7.1_compiler/usr/bin/cc -Xcpluscomm -nostdlib -nostdinc -mips2
+-g2 "$@" 2>/dev/null
+fi
diff --git a/gdb/testsuite/gdb.mdebug/break.c b/gdb/testsuite/gdb.mdebug/break.c
new file mode 100644
index 00000000000..d0b26998c77
--- /dev/null
+++ b/gdb/testsuite/gdb.mdebug/break.c
@@ -0,0 +1,92 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 1992-2023 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int printf (const char *__format, ...);
+int fprintf (void *__stream, const char *__format, ...);
+int atoi(const char *_Str);
+void *malloc(int size);
+
+extern void * stderr;
+
+extern int marker1 (void);
+extern int marker2 (int a);
+extern void marker3 (char *a, char *b);
+extern void marker4 (long d);
+
+/* We're used by a test that requires malloc, so make sure it is in
+   the executable.  */
+void *need_malloc ()
+{
+  return malloc (1);
+}
+
+/*
+ *	This simple classical example of recursion is useful for
+ *	testing stack backtraces and such.
+ */
+
+int factorial(int);
+
+int
+main (int argc, char **argv, char **envp)
+{ /* set breakpoint 6 here */
+  if (argc == 12345) {  /* an unlikely value < 2^16, in case uninited */
+	fprintf (stderr, "usage:  factorial <number>\n");
+	return 1;
+    }
+    printf ("%d\n", factorial (atoi ("6")));  /* set breakpoint 1 here */
+    /* set breakpoint 12 here */
+    marker1 ();  /* set breakpoint 11 here */
+    marker2 (43); /* set breakpoint 20 here */
+    marker3 ("stack", "trace"); /* set breakpoint 21 here */
+    marker4 (177601976L);
+    /* We're used by a test that requires malloc, so make sure it is
+       in the executable.  */
+    (void)malloc (1);
+
+    argc = (argc == 12345); /* This is silly, but we can step off of it */ /* set breakpoint 2 here */
+    return argc;  /* set breakpoint 10 here */
+} /* set breakpoint 10a here */
+
+int factorial (int value)
+{ /* set breakpoint 7 here */
+  if (value > 1) { /* set breakpoint 7a here */
+	value *= factorial (value - 1);
+    }
+    return (value); /* set breakpoint 19 here */
+}
+
+int multi_line_if_conditional (int a, int b, int c)
+{ /* set breakpoint 3 here */
+  if (a
+      && b
+      && c)
+    return 0;
+  else
+    return 1;
+}
+
+int multi_line_while_conditional (int a, int b, int c)
+{ /* set breakpoint 4 here */
+  while (a
+      && b
+      && c)
+    {
+      a--, b--, c--;
+    }
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.mdebug/break.exp b/gdb/testsuite/gdb.mdebug/break.exp
new file mode 100644
index 00000000000..cb74c1b0081
--- /dev/null
+++ b/gdb/testsuite/gdb.mdebug/break.exp
@@ -0,0 +1,921 @@
+#   Copyright 1988-2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file was written by Rob Savoye. (rob@cygnus.com)
+
+if { [build_executable "failed to prepare" "break" {break.c break1.c} {debug nowarnings}] } {
+    return -1
+}
+set srcfile break.c
+set srcfile1 break1.c
+
+set bp_location1 [gdb_get_line_number "set breakpoint 1 here"]
+set bp_location2 [gdb_get_line_number "set breakpoint 2 here"]
+set bp_location3 [gdb_get_line_number "set breakpoint 3 here"]
+set bp_location4 [gdb_get_line_number "set breakpoint 4 here"]
+set bp_location6 [gdb_get_line_number "set breakpoint 6 here"]
+set bp_location7 [gdb_get_line_number "set breakpoint 7 here"]
+set bp_location8 [gdb_get_line_number "set breakpoint 8 here" $srcfile1]
+set bp_location11 [gdb_get_line_number "set breakpoint 11 here"]
+
+set main_line $bp_location6
+
+# In C++ mode, we see a full prototype; in C mode, we only see the
+# function name, with no parameter info.
+proc func {name} {
+    return "${name}(?:\(\[^\r\n\]*\))?"
+}
+
+# test simple breakpoint setting commands
+
+proc_with_prefix test_break {} {
+    clean_restart break
+
+    # Test deleting all breakpoints when there are none installed,
+    # GDB should not prompt for confirmation.
+    # Note that lib/gdb.exp provides a "delete_breakpoints" proc
+    # for general use elsewhere.
+    send_gdb "delete breakpoints\n"
+    gdb_expect {
+	 -re "Delete all breakpoints.*$" {
+		send_gdb "y\n"
+		gdb_expect {
+		    -re "$::gdb_prompt $" {
+			fail "delete all breakpoints when none (unexpected prompt)"
+		    }
+		    timeout	{ fail "delete all breakpoints when none (timeout after unexpected prompt)" }
+		}
+	    }
+	 -re ".*$::gdb_prompt $"       { pass "delete all breakpoints when none" }
+	timeout	            { fail "delete all breakpoints when none (timeout)" }
+    }
+
+    # test break at function
+    gdb_test "break -q main" \
+	"Breakpoint.*at.* file .*$::srcfile, line.*" \
+	"breakpoint function"
+
+    # test break at quoted function
+    gdb_test "break \"marker2\"" \
+	"Breakpoint.*at.* file .*$::srcfile1, line.*" \
+	"breakpoint quoted function"
+
+    # test break at function in file
+    gdb_test "break $::srcfile:factorial" \
+	"Breakpoint.*at.* file .*$::srcfile, line.*" \
+	"breakpoint function in file"
+
+    # test break at line number
+    #
+    # Note that the default source file is the last one whose source text
+    # was printed.  For native debugging, before we've executed the
+    # program, this is the file containing main, but for remote debugging,
+    # it's wherever the processor was stopped when we connected to the
+    # board.  So, to be sure, we do a list command.
+    gdb_test "list -q main" \
+	".*main \\(int argc, char \\*\\*argv, char \\*\\*envp\\).*" \
+	"use `list' to establish default source file"
+
+    gdb_test "break $::bp_location1" \
+	"Breakpoint.*at.* file .*$::srcfile, line $::bp_location1\\." \
+	"breakpoint line number"
+
+    # test duplicate breakpoint
+    gdb_test "break $::bp_location1" \
+	"Note: breakpoint \[0-9\]+ also set at pc.*Breakpoint \[0-9\]+ at.* file .*$::srcfile, line $::bp_location1\\." \
+	"breakpoint duplicate"
+
+    # test break at line number in file
+    gdb_test "break $::srcfile:$::bp_location2" \
+	"Breakpoint.*at.* file .*$::srcfile, line $::bp_location2\\." \
+	"breakpoint line number in file"
+
+    # Test putting a break at the start of a multi-line if conditional.
+    # Verify the breakpoint was put at the start of the conditional.
+    gdb_test "break multi_line_if_conditional" \
+	"Breakpoint.*at.* file .*$::srcfile, line $::bp_location3\\." \
+	"breakpoint at start of multi line if conditional"
+
+    gdb_test "break multi_line_while_conditional" \
+	"Breakpoint.*at.* file .*$::srcfile, line $::bp_location4\\." \
+	"breakpoint at start of multi line while conditional"
+
+    gdb_test "info break" \
+	[multi_line "Num     Type\[ \]+Disp Enb Address\[ \]+What.*" \
+		    "$::decimal\[\t \]+breakpoint     keep y.* in [func main] at .*$::srcfile:$::main_line.*" \
+		    "$::decimal\[\t \]+breakpoint     keep y.* in [func marker2] at .*$::srcfile1:$::bp_location8.*" \
+		    "$::decimal\[\t \]+breakpoint     keep y.* in [func factorial] at .*$::srcfile:$::bp_location7.*" \
+		    "$::decimal\[\t \]+breakpoint     keep y.* in [func main] at .*$::srcfile:$::bp_location1.*" \
+		    "$::decimal\[\t \]+breakpoint     keep y.* in [func main] at .*$::srcfile:$::bp_location1.*" \
+		    "$::decimal\[\t \]+breakpoint     keep y.* in [func main] at .*$::srcfile:$::bp_location2.*" \
+		    "$::decimal\[\t \]+breakpoint     keep y.* in [func multi_line_if_conditional] at .*$::srcfile:$::bp_location3.*" \
+		    "$::decimal\[\t \]+breakpoint     keep y.* in [func multi_line_while_conditional] at .*$::srcfile:$::bp_location4"] \
+	"breakpoint info"
+
+    #
+    # Test info breakpoint with arguments
+    #
+
+    set see1 0
+    set see2 0
+    set see3 0
+    set see4 0
+    set see5 0
+    set see6 0
+
+    gdb_test_multiple "info break 2 4 6" "info break 2 4 6" {
+	-re "1\[\t \]+breakpoint *keep y\[^\r\n\]*:$::main_line\[^\r\n\]*" {
+	    set see1 1
+	    exp_continue
+	}
+	-re "2\[\t \]+breakpoint *keep y\[^\r\n\]* in [func marker2] at \[^\r\n\]*" {
+	    set see2 1
+	    exp_continue
+	}
+	-re "3\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location7\[^\r\n\]*" {
+	    set see3 1
+	    exp_continue
+	}
+	-re "4\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
+	    set see4 1
+	    exp_continue
+	}
+	-re "5\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
+	    set see5 1
+	    exp_continue
+	}
+	-re "6\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location2\[^\r\n\]*" {
+	    set see6 1
+	    exp_continue
+	}
+	-re ".*$::gdb_prompt $" {
+	    if {!$see1 && $see2 && !$see3 && $see4 && !$see5 && $see6} {
+		pass "info break 2 4 6"
+	    } else {
+		fail "info break 2 4 6"
+	    }
+	}
+    }
+
+    set see1 0
+    set see2 0
+    set see3 0
+    set see4 0
+    set see5 0
+    set see6 0
+
+    gdb_test_multiple "info break 3-5" "info break 3-5" {
+	-re "1\[\t \]+breakpoint *keep y.* in [func main] at .*:$::main_line\[^\r\n\]*" {
+	    set see1 1
+	    exp_continue
+	}
+	-re "2\[\t \]+breakpoint *keep y\[^\r\n\]* in [func marker2] at \[^\r\n\]*" {
+	    set see2 1
+	    exp_continue
+	}
+	-re "3\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location7\[^\r\n\]*" {
+	    set see3 1
+	    exp_continue
+	}
+	-re "4\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
+	    set see4 1
+	    exp_continue
+	}
+	-re "5\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
+	    set see5 1
+	    exp_continue
+	}
+	-re "6\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location2\[^\r\n\]*" {
+	    set see6 1
+	    exp_continue
+	}
+	-re ".*$::gdb_prompt $" {
+	    if {!$see1 && !$see2 && $see3 && $see4 && $see5 && !$see6} {
+		pass "info break 3-5"
+	    } else {
+		fail "info break 3-5"
+	    }
+	}
+    }
+
+    #
+    # Test disable/enable with arguments
+    #
+
+    # Test with value history
+
+    with_test_prefix "with value history" {
+	gdb_test "print 1"
+	gdb_test "print 2"
+	gdb_test "print 3"
+	gdb_test "print 4"
+	gdb_test "print 5"
+	gdb_test "print 6"
+
+	# $2 is 2 and $$ is 5
+	gdb_test_no_output "disable \$2 \$\$" "disable using history values"
+
+	set see1 0
+	set see2 0
+	set see3 0
+	set see4 0
+	set see5 0
+	set see6 0
+
+	gdb_test_multiple "info break" "check disable with history values" {
+	    -re "1\[\t \]+breakpoint *keep y.* in [func main] at .*:$::main_line\[^\r\n\]*" {
+		set see1 1
+		exp_continue
+	    }
+	    -re "2\[\t \]+breakpoint *keep n\[^\r\n\]* in [func marker2] at \[^\r\n\]*" {
+		set see2 1
+		exp_continue
+	    }
+	    -re "3\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location7\[^\r\n\]*" {
+		set see3 1
+		exp_continue
+	    }
+	    -re "4\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
+		set see4 1
+		exp_continue
+	    }
+	    -re "5\[\t \]+breakpoint *keep n\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
+		set see5 1
+		exp_continue
+	    }
+	    -re "6\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location2\[^\r\n\]*" {
+		set see6 1
+		exp_continue
+	    }
+	    -re ".*$::gdb_prompt $" {
+		if {$see1 && $see2 && $see3 && $see4 && $see5 && $see6} {
+		    pass "check disable with history values"
+		} else {
+		    fail "check disable with history values"
+		}
+	    }
+	}
+    }
+
+    with_test_prefix "with convenience vars" {
+	gdb_test "enable"
+	gdb_test "set \$foo = 3"
+	gdb_test "set \$bar = 6"
+	gdb_test_no_output "disable \$foo \$bar" "disable with convenience values"
+
+	set see1 0
+	set see2 0
+	set see3 0
+	set see4 0
+	set see5 0
+	set see6 0
+
+	gdb_test_multiple "info break" "check disable with convenience values" {
+	    -re "1\[\t \]+breakpoint *keep y.* in [func main] at .*:$::main_line\[^\r\n\]*" {
+		set see1 1
+		exp_continue
+	    }
+	    -re "2\[\t \]+breakpoint *keep y\[^\r\n\]* in [func marker2] at \[^\r\n\]*" {
+		set see2 1
+		exp_continue
+	    }
+	    -re "3\[\t \]+breakpoint *keep n\[^\r\n\]*$::bp_location7\[^\r\n\]*" {
+		set see3 1
+		exp_continue
+	    }
+	    -re "4\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
+		set see4 1
+		exp_continue
+	    }
+	    -re "5\[\t \]+breakpoint *keep y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
+		set see5 1
+		exp_continue
+	    }
+	    -re "6\[\t \]+breakpoint *keep n\[^\r\n\]*$::bp_location2\[^\r\n\]*" {
+		set see6 1
+		exp_continue
+	    }
+	    -re ".*$::gdb_prompt $" {
+		if {$see1 && $see2 && $see3 && $see4 && $see5 && $see6} {
+		    pass "check disable with convenience values"
+		} else {
+		    fail "check disable with convenience values"
+		}
+	    }
+	}
+    }
+
+    # test with bad values
+
+    with_test_prefix "bad values" {
+	gdb_test "enable"
+	gdb_test "disable 10" "No breakpoint number 10." \
+	    "disable non-existent breakpoint 10"
+
+	gdb_test_no_output "set \$baz = 1.234"
+	gdb_test "disable \$baz" \
+	    "Convenience variable must have integer value.*" \
+	    "disable with non-integer convenience var"
+	gdb_test "disable \$grbx" \
+	    "Convenience variable must have integer value.*" \
+	    "disable with non-existent convenience var"
+	gdb_test "disable \$10" \
+	    "History has not yet reached .10." \
+	    "disable with non-existent history value"
+	gdb_test "disable \$1foo" \
+	    "Convenience variable must have integer value.*" \
+	    "disable with badly formed history value"
+    }
+
+    # FIXME: The rest of this test doesn't work with anything that can't
+    # handle arguments.
+    # Huh? There doesn't *appear* to be anything that passes arguments
+    # below.
+
+    #
+    # run until the breakpoint at main is hit. For non-stubs-using targets.
+    #
+    gdb_run_cmd
+    gdb_test "" \
+	"Breakpoint \[0-9\]+,.*main .*argc.*argv.* at .*$::srcfile:$::bp_location6.*$::bp_location6\[\t \]+\{.*" \
+	"run until function breakpoint"
+
+    # Test the 'list' commands sets current file for the 'break LINENO' command.
+    set bp_marker1 [gdb_get_line_number "set breakpoint 15 here" $::srcfile1]
+    gdb_test "list marker1" ".*"
+    gdb_test "break $bp_marker1" "Breakpoint \[0-9\]+ at 0x\[0-9a-f\]+: file .*$::srcfile1, line ${bp_marker1}\\." \
+	     "break lineno"
+    gdb_test_no_output {delete $bpnum}
+
+    #
+    # run until the breakpoint at a line number
+    #
+    gdb_test continue "Continuing\\..*Breakpoint \[0-9\]+, main \\(argc=.*, argv=.*, envp=.*\\) at .*$::srcfile:$::bp_location1.*$::bp_location1\[\t \]+printf.*factorial.*" \
+			    "run until breakpoint set at a line number"
+
+    #
+    # Run until the breakpoint set in a function in a file
+    #
+	set bp_location7a [gdb_get_line_number "set breakpoint 7a here"]
+	send_gdb "del 3\n"
+	send_gdb "del 7\n"
+	gdb_test "break $bp_location7a" \
+	"Breakpoint.*at.* file .*$::srcfile, line $bp_location7a\\." \
+	"setting breakpoint at 7a"
+
+    for {set i 6} {$i >= 1} {incr i -1} {
+	    gdb_test continue "Continuing\\..*Breakpoint \[0-9\]+, factorial \\(value=$i\\) at .*$::srcfile:$bp_location7a.*$bp_location7a\[\t \]+.*if .value > 1. \{.*" \
+			    "run until file:function($i) breakpoint"
+    }
+
+    send_gdb "break $::bp_location3\n"
+
+	send_gdb "break $::bp_location7\n"
+
+    send_gdb "del 2\n"
+
+    #
+    # run until the file:function breakpoint at a line number in a file
+    #
+    gdb_test continue "Continuing\\..*Breakpoint \[0-9\]+, main \\(argc=.*, argv=.*, envp=.*\\) at .*$::srcfile:$::bp_location2.*$::bp_location2\[\t \]+argc = \\(argc == 12345\\);.*" \
+		    "run until file:linenum breakpoint"
+
+    # Test break at offset +1
+    set bp_location10 [gdb_get_line_number "set breakpoint 10 here"]
+
+    gdb_test "break +1" \
+	"Breakpoint.*at.* file .*$::srcfile, line $bp_location10\\." \
+	"breakpoint offset +1"
+
+    # Check to see if breakpoint is hit when stepped onto
+
+    gdb_test "step" \
+	".*Breakpoint \[0-9\]+, main \\(argc=.*, argv=.*, envp=.*\\) at .*$::srcfile:$bp_location10.*$bp_location10\[\t \]+return argc;.*breakpoint 10 here.*" \
+	"step onto breakpoint"
+}
+
+test_break
+
+proc_with_prefix test_tbreak {} {
+    clean_restart break
+
+    # test temporary breakpoint at function
+    gdb_test "tbreak -q main" "Temporary breakpoint.*at.* file .*$::srcfile, line.*" "temporary breakpoint function"
+
+    # test break at function in file
+    gdb_test "tbreak $::srcfile:factorial" "Temporary breakpoint.*at.* file .*$::srcfile, line.*" \
+	    "Temporary breakpoint function in file"
+
+    # test break at line number
+    gdb_test "tbreak $::bp_location1" \
+	"Temporary breakpoint.*at.* file .*$::srcfile, line $::bp_location1.*" \
+	"temporary breakpoint line number #1"
+
+    gdb_test "tbreak $::bp_location6" "Temporary breakpoint.*at.* file .*$::srcfile, line $::bp_location6.*" "temporary breakpoint line number #2"
+
+    # test break at line number in file
+    gdb_test "tbreak $::srcfile:$::bp_location2" \
+	"Temporary breakpoint.*at.* file .*$::srcfile, line $::bp_location2.*" \
+	"temporary breakpoint line number in file #1"
+
+    gdb_test  "tbreak $::srcfile:$::bp_location11" "Temporary breakpoint.*at.* file .*$::srcfile, line $::bp_location11.*" "Temporary breakpoint line number in file #2"
+
+    # check to see what breakpoints are set (temporary this time)
+    gdb_test "info break" \
+	[multi_line "Num     Type.*Disp Enb Address.*What.*" \
+		    "$::decimal\[\t \]+breakpoint     del.*y.*in [func main] at .*$::srcfile:$::main_line.*" \
+		    "$::decimal\[\t \]+breakpoint     del.*y.*in [func factorial] at .*$::srcfile:$::bp_location7.*" \
+		    "$::decimal\[\t \]+breakpoint     del.*y.*in [func main] at .*$::srcfile:$::bp_location1.*" \
+		    "$::decimal\[\t \]+breakpoint     del.*y.*in [func main] at .*$::srcfile:$::bp_location6.*" \
+		    "$::decimal\[\t \]+breakpoint     del.*y.*in [func main] at .*$::srcfile:$::bp_location2.*" \
+		    "$::decimal\[\t \]+breakpoint     del.*y.*in [func main] at .*$::srcfile:$::bp_location11.*"] \
+	"Temporary breakpoint info"
+}
+
+test_tbreak
+
+# Verify that GDB responds gracefully when asked to set a breakpoint
+# on a nonexistent source line.
+
+proc_with_prefix test_break_nonexistent_line {} {
+    clean_restart break
+
+    if {![runto_main]} {
+	return
+    }
+
+    gdb_test_no_output "set breakpoint pending off"
+    gdb_test "break 999" \
+	"No line 999 in the current file." \
+	"break on non-existent source line"
+}
+
+test_break_nonexistent_line
+
+proc_with_prefix test_break_default {} {
+    clean_restart break
+
+    if {![runto_main]} {
+	return
+    }
+
+    # Run to the desired default location. If not positioned here, the
+    # tests below don't work.
+    #
+    gdb_test "until $::bp_location1" "main .* at .*:$::bp_location1.*" \
+	"until bp_location1"
+
+    # Verify that GDB allows one to just say "break", which is treated
+    # as the "default" breakpoint.  Note that GDB gets cute when printing
+    # the informational message about other breakpoints at the same
+    # location.  We'll hit that bird with this stone too.
+    #
+    gdb_test "break" "Breakpoint \[0-9\]*.*" \
+	"break on default location, 1st time"
+
+    gdb_test "break" \
+	"Note: breakpoint \[0-9\]* also set at .*Breakpoint \[0-9\]*.*" \
+	"break on default location, 2nd time"
+
+    gdb_test "break" \
+	"Note: breakpoints \[0-9\]* and \[0-9\]* also set at .*Breakpoint \[0-9\]*.*" \
+	"break on default location, 3rd time"
+
+    gdb_test "break" \
+	"Note: breakpoints \[0-9\]*, \[0-9\]* and \[0-9\]* also set at .*Breakpoint \[0-9\]*.*" \
+	"break on default location, 4th time"
+
+    # Check setting a breakpoint at the default location with a condition attached.
+    gdb_test "break if (1)" \
+	"Note: breakpoints \[0-9\]*, \[0-9\]*, \[0-9\]* and \[0-9\]* also set at .*Breakpoint \[0-9\]*.*" \
+	"break on the default location, 5th time, but with a condition"
+}
+
+test_break_default
+
+# Verify that a "silent" breakpoint can be set, and that GDB is indeed
+# "silent" about its triggering.
+
+proc_with_prefix test_break_silent_and_more {} {
+    clean_restart break
+
+    if {![runto_main]} {
+	return
+    }
+
+    gdb_test_multiple "break $::bp_location1" \
+	"set to-be-silent break bp_location1" {
+	    -re "Breakpoint (\[0-9\]*) at .*, line $::bp_location1.*$::gdb_prompt $" {
+		set bpno $expect_out(1,string)
+		pass "set to-be-silent break bp_location1"
+	    }
+	}
+
+    gdb_test "commands $bpno\nsilent\nend" ">end" "set silent break bp_location1"
+
+    gdb_test "info break $bpno" \
+	"\[0-9\]*\[ \t\]*breakpoint.*:$::bp_location1\r\n\[ \t\]*silent.*" \
+	"info silent break bp_location1"
+
+    gdb_test "continue" "Continuing." \
+	"hit silent break bp_location1"
+
+    gdb_test "bt" "#0  main .* at .*:$::bp_location1.*" \
+	"stopped for silent break bp_location1"
+
+    # Verify the $_hit_bpnum convenience variable is set to the silent hit bpno.
+    gdb_test "printf \"%d\\n\", \$_hit_bpnum" "$bpno" \
+	"Silent breakpoint hit \$_hit_bpnum is silent $bpno"
+
+    # Verify that GDB can at least parse a breakpoint with the
+    # "thread" keyword.  (We won't attempt to test here that a
+    # thread-specific breakpoint really triggers appropriately.
+    # The gdb.threads subdirectory contains tests for that.)
+    #
+    set bp_location12 [gdb_get_line_number "set breakpoint 12 here"]
+    gdb_test "break $bp_location12 thread 999" "Unknown thread 999.*" \
+	"thread-specific breakpoint on non-existent thread disallowed"
+
+    gdb_test "break $bp_location12 thread foo" \
+	"Invalid thread ID: foo" \
+	"thread-specific breakpoint on bogus thread ID disallowed"
+
+    # Verify that GDB responds gracefully to a breakpoint command with
+    # trailing garbage.
+    #
+    gdb_test "break $bp_location12 foo" \
+	"malformed linespec error: unexpected string, \"foo\".*" \
+	"breakpoint with trailing garbage disallowed"
+
+    # Verify that GDB responds gracefully to a "clear" command that has
+    # no matching breakpoint.  (First, get us off the current source line,
+    # which we know has a breakpoint.)
+    #
+    gdb_test "next" "marker1.*" "step over breakpoint"
+
+    gdb_test "clear 81" "No breakpoint at 81.*" \
+	"clear line has no breakpoint disallowed"
+
+    gdb_test "clear" "No breakpoint at this line.*" \
+	"clear current line has no breakpoint disallowed"
+
+    # Verify that we can set and clear multiple breakpoints.
+    #
+    # We don't test that it deletes the correct breakpoints.  We do at
+    # least test that it deletes more than one breakpoint.
+    #
+    gdb_test "break marker3" "Breakpoint.*at.*" "break marker3 #1"
+    gdb_test "break marker3" "Breakpoint.*at.*" "break marker3 #2"
+    gdb_test "clear marker3" {Deleted breakpoints [0-9]+ [0-9]+.*}
+}
+
+test_break_silent_and_more
+
+# Verify that a breakpoint can be set via a convenience variable.
+
+proc_with_prefix test_break_line_convenience_var {} {
+    clean_restart break
+
+    if { ![runto_main] } {
+	return
+    }
+
+    gdb_test_no_output "set \$foo=$::bp_location11" \
+	"set convenience variable \$foo to bp_location11"
+
+    gdb_test "break \$foo" \
+	"Breakpoint (\[0-9\]*) at .*, line $::bp_location11.*"
+
+    # Verify that GDB responds gracefully to an attempt to set a
+    # breakpoint via a convenience variable whose type is not integer.
+
+    gdb_test_no_output "set \$foo=81.5" \
+	"set convenience variable \$foo to 81.5"
+
+    gdb_test "break \$foo" \
+	"Convenience variables used in line specs must have integer values.*" \
+	"non-integer convenience variable disallowed"
+}
+
+test_break_line_convenience_var
+
+# Verify that we can set and trigger a breakpoint in a user-called function.
+
+proc_with_prefix test_break_user_call {} {
+    clean_restart break
+
+    if { ![runto_main] } {
+	return
+    }
+
+    gdb_test "break marker2" \
+	"Breakpoint (\[0-9\]*) at .*, line $::bp_location8.*" \
+	"set breakpoint on to-be-called function"
+
+    gdb_test "print marker2(99)" \
+	"The program being debugged stopped while in a function called from GDB.\r\nEvaluation of the expression containing the function\r\n.[func marker2]. will be abandoned.\r\nWhen the function is done executing, GDB will silently stop.*" \
+	"hit breakpoint on called function"
+
+    # As long as we're stopped (breakpointed) in a called function,
+    # verify that we can successfully backtrace & such from here.
+    gdb_test "bt" \
+	"#0\[ \t\]*($::hex in )?marker2.*:$::bp_location8\r\n#1\[ \t\]*<function called from gdb>.*" \
+	"backtrace while in called function"
+
+    # Return from the called function.  For remote targets, it's important to do
+    # this before runto_main, which otherwise may silently stop on the dummy
+    # breakpoint inserted by GDB at the program's entry point.
+    #
+    gdb_test_multiple "finish" "finish from called function" {
+	-re "Run till exit from .*marker2.* at .*$::bp_location8\r\n.*function called from gdb.*$::gdb_prompt $" {
+	    pass "finish from called function"
+	}
+	-re "Run till exit from .*marker2.* at .*$::bp_location8\r\n.*Value returned.*$::gdb_prompt $" {
+	    pass "finish from called function"
+	}
+    }
+}
+
+test_break_user_call
+
+# Verify that GDB responds gracefully to a "finish" command with
+# arguments.
+
+proc_with_prefix test_finish_arguments {} {
+    clean_restart break
+
+    if {![runto_main]} {
+	return
+    }
+
+    send_gdb "finish 123\n"
+    gdb_expect {
+      -re "The \"finish\" command does not take any arguments.\r\n$::gdb_prompt $"\
+	      {pass "finish with arguments disallowed"}
+      -re "$::gdb_prompt $"\
+	      {fail "finish with arguments disallowed"}
+      timeout {fail "(timeout) finish with arguments disallowed"}
+    }
+
+    # Verify that GDB responds gracefully to a request to "finish" from
+    # the outermost frame.  On a stub that never exits, this will just
+    # run to the stubs routine, so we don't get this error...  Thus the
+    # second condition.
+    #
+
+    gdb_test_multiple "finish" "finish from outermost frame disallowed" {
+	-re "\"finish\" not meaningful in the outermost frame.\r\n$::gdb_prompt $" {
+	    pass "finish from outermost frame disallowed"
+	}
+	-re "Run till exit from.*\r\n$::gdb_prompt $" {
+	    pass "finish from outermost frame disallowed"
+	}
+    }
+}
+
+test_finish_arguments
+
+#********
+
+
+#
+# Test "next" over recursive function call.
+#
+
+proc_with_prefix test_next_with_recursion {} {
+    global gdb_prompt
+    global decimal
+    global binfile
+
+    gdb_test "kill" "" "kill program" "Kill the program being debugged.*y or n. $" "y"
+    delete_breakpoints
+
+	set bp_location7a [gdb_get_line_number "set breakpoint 7a here"]
+	gdb_test "break $bp_location7a" \
+	"Breakpoint.*at.* file .*$::srcfile, line $bp_location7a\\." \
+	"setting breakpoint at 7a"
+
+    # Run until we call factorial with 6
+
+    gdb_run_cmd
+    gdb_test "" "Break.* factorial .value=6. .*" "run to factorial(6)"
+
+    # Continue until we call factorial recursively with 5.
+
+    gdb_test "continue" \
+	"Continuing.*Break.* factorial .value=5. .*" \
+	"continue to factorial(5)"
+
+    # Do a backtrace just to confirm how many levels deep we are.
+
+    gdb_test "backtrace" \
+	"#0\[ \t\]+ factorial .value=5..*" \
+	"backtrace from factorial(5)"
+
+    # Now a "next" should position us at the recursive call, which
+    # we will be performing with 4.
+
+    gdb_test "next" \
+	".* factorial .value - 1.;.*" \
+	"next to recursive call"
+
+    # Disable the breakpoint at the entry to factorial by deleting them all.
+    # The "next" should run until we return to the next line from this
+    # recursive call to factorial with 4.
+    # Buggy versions of gdb will stop instead at the innermost frame on
+    # the line where we are trying to "next" to.
+
+    delete_breakpoints
+
+    if [istarget "mips*tx39-*"] {
+	set timeout 60
+    }
+    # We used to set timeout here for all other targets as well.  This
+    # is almost certainly wrong.  The proper timeout depends on the
+    # target system in use, and how we communicate with it, so there
+    # is no single value appropriate for all targets.  The timeout
+    # should be established by the Dejagnu config file(s) for the
+    # board, and respected by the test suite.
+    #
+    # For example, if I'm running GDB over an SSH tunnel talking to a
+    # portmaster in California talking to an ancient 68k board running
+    # a crummy ROM monitor (a situation I can only wish were
+    # hypothetical), then I need a large timeout.  But that's not the
+    # kind of knowledge that belongs in this file.
+
+    gdb_test next "\[0-9\]*\[\t \]+return \\(value\\);.*" \
+	    "next over recursive call"
+
+    # OK, we should be back in the same stack frame we started from.
+    # Do a backtrace just to confirm.
+
+    gdb_test "backtrace" \
+	    "#0\[ \t\]+ factorial .value=120.*\r\n#1\[ \t\]+ \[0-9a-fx\]+ in factorial .value=6..*" \
+	    "backtrace from factorial(5.1)"
+
+    if { ![target_info exists gdb,noresults] } {
+	gdb_continue_to_end "recursive next test"
+    }
+}
+
+test_next_with_recursion
+
+
+#********
+
+# build a new file with optimization enabled so that we can try breakpoints
+# on targets with optimized prologues
+
+if { [build_executable "failed to prepare" "breako2" {break.c break1.c} {debug nowarnings optimize=-O2}] } {
+    return -1
+}
+
+proc_with_prefix test_break_optimized_prologue {} {
+    clean_restart breako2
+
+    # test break at function
+    gdb_test "break -q main" \
+	"Breakpoint.*at.* file .*, line.*" \
+	"breakpoint function, optimized file"
+
+    # test break at function
+    gdb_test "break marker4" \
+	"Breakpoint.*at.* file .*$::srcfile1, line.*" \
+	"breakpoint small function, optimized file"
+
+    # run until the breakpoint at main is hit. For non-stubs-using targets.
+    gdb_run_cmd
+
+    set test "run until function breakpoint, optimized file"
+    gdb_test_multiple "" $test {
+	-re "Breakpoint \[0-9\]+,.*main .*argc.*argv.* at .*$::srcfile:$::bp_location6.*$::bp_location6\[\t \]+\{ if .argc.* \{.*$::gdb_prompt $" {
+	    pass $test
+	}
+	-re "Breakpoint \[0-9\]+,.*main .*argc.*argv.* at .*$::gdb_prompt $" {
+	    pass "$test (code motion)"
+	}
+    }
+
+    # run until the breakpoint at a small function
+    #
+    # Add a second pass pattern.  The behavior differs here between stabs
+    # and dwarf for one-line functions.  Stabs preserves two line symbols
+    # (one before the prologue and one after) with the same line number,
+    # but dwarf regards these as duplicates and discards one of them.
+    # Therefore the address after the prologue (where the breakpoint is)
+    # has no exactly matching line symbol, and GDB reports the breakpoint
+    # as if it were in the middle of a line rather than at the beginning.
+
+    set bp_location14 [gdb_get_line_number "set breakpoint 14 here" $::srcfile1]
+
+    gdb_test_multiple "continue" \
+	"run until breakpoint set at small function, optimized file" {
+	    -re "Breakpoint $::decimal, marker4 \\(d=(d@entry=)?177601976\\) at .*$::srcfile1:$bp_location14\[\r\n\]+$bp_location14\[\t \]+void marker4.*" {
+		pass "run until breakpoint set at small function, optimized file (line bp_location14)"
+	    }
+	    -re "Breakpoint $::decimal, factorial \\(.*\\) .*\{\r\n$::gdb_prompt" {
+		# GCC 4.3 emits bad line number information - see gcc/36748.
+		if { [test_compiler_info "gcc-4-3-*"] } {
+		    setup_xfail *-*-*
+		}
+		fail "run until breakpoint set at small function, optimized file"
+	    }
+	}
+}
+
+test_break_optimized_prologue
+
+# test that 'rbreak' on a symbol that may be from a shared library doesn't
+# cause a "Junk at end of arguments." error.
+#
+# On x86 GNU/Linux, this test will choke on e.g. __libc_start_main@plt.
+#
+# Note that this test won't necessarily choke on all targets even if
+# all the rbreak issue is present.  rbreak needs to match and set a
+# breakpoint on a symbol causes 'break' to choke.
+
+proc_with_prefix test_rbreak_shlib {} {
+    clean_restart breako2
+
+    gdb_test_no_output "set breakpoint pending on" "rbreak junk pending setup"
+
+    # We expect at least one breakpoint to be set when we "rbreak main".
+    gdb_test "rbreak main" \
+	".*Breakpoint.*at.* file .*$::srcfile, line.*"
+
+    # Run to a breakpoint.  Fail if we see "Junk at end of arguments".
+    gdb_run_cmd
+
+    gdb_test_multiple "" "rbreak junk" {
+	-re -wrap "Junk at end of arguments.*" {
+	    fail $gdb_test_name
+	}
+	-re -wrap ".*Breakpoint \[0-9\]+,.*" {
+	    pass $gdb_test_name
+	}
+    }
+}
+
+test_rbreak_shlib
+
+# Test break via convenience variable with file name
+
+proc_with_prefix test_break_file_line_convenience_var {} {
+    clean_restart breako2
+
+    set line [gdb_get_line_number "set breakpoint 1 here"]
+    gdb_test_no_output "set \$l = $line"
+
+    set line_actual "-1"
+    set test "break $::srcfile:\$l"
+    gdb_test_multiple "$test" $test {
+	-re "Breakpoint $::decimal at $::hex: file .*break\\.c, line ($::decimal)\\.\r\n$::gdb_prompt $" {
+	    # Save the actual line number on which the breakpoint was
+	    # actually set. On some systems (Eg: Ubuntu 16.04 with GCC
+	    # version 5.4.0), that line gets completely inlined, including
+	    # the call to printf, and so we end up inserting the breakpoint
+	    # on one of the following lines instead.
+	    set line_actual $expect_out(1,string)
+	    pass $test
+	}
+    }
+
+    gdb_test_no_output "set \$foo=81.5" \
+	"set convenience variable \$foo to 81.5"
+    gdb_test "break $::srcfile:\$foo" \
+	"Convenience variables used in line specs must have integer values.*" \
+	"non-integer convenience variable disallowed"
+}
+
+test_break_file_line_convenience_var
+
+# Test that commands can be cleared without error.
+
+proc_with_prefix test_break_commands_clear {} {
+    clean_restart breako2
+
+    set line [gdb_get_line_number "set breakpoint 1 here"]
+    gdb_breakpoint $line
+
+    gdb_test "commands\nprint 232323\nend" ">end" "set some breakpoint commands"
+    gdb_test "commands\nend" ">end" "clear breakpoint commands"
+
+    # We verify that the commands were cleared by ensuring that the last
+    # breakpoint's location ends the output -- if there were commands,
+    # they would have been printed after the location.
+    gdb_test "info break" "$::srcfile:$::decimal" "verify that they were cleared"
+}
+
+test_break_commands_clear
diff --git a/gdb/testsuite/gdb.mdebug/break1.c b/gdb/testsuite/gdb.mdebug/break1.c
new file mode 100644
index 00000000000..5382ec06fa9
--- /dev/null
+++ b/gdb/testsuite/gdb.mdebug/break1.c
@@ -0,0 +1,59 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 1992-2023 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* The code for this file was extracted from the gdb testsuite
+   testcase "break.c". */
+
+/* A structure we use for field name completion tests.  */
+struct some_struct
+{
+  int a_field;
+  int b_field;
+  union { int z_field; };
+};
+
+struct some_struct values[50];
+
+/* Some definitions for tag completion.  */
+enum some_enum { VALUE };
+
+enum some_enum some_enum_global;
+
+union some_union
+{
+  int f1;
+  double f2;
+};
+
+union some_union some_union_global;
+
+/* A variable with a name "similar" to the above struct, to test that
+   tag completion works ok.  */
+int some_variable;
+
+/* The following functions do nothing useful.  They are included
+   simply as places to try setting breakpoints at.  They are
+   explicitly "one-line functions" to verify that this case works
+   (some versions of gcc have or have had problems with this).
+
+   These functions are in a separate source file to prevent an
+   optimizing compiler from inlining them and optimizing them away. */
+
+int marker1 (void) { return (0); }	/* set breakpoint 15 here */
+int marker2 (int a) { return (1); }	/* set breakpoint 8 here */
+void marker3 (char *a, char *b) {}	/* set breakpoint 17 here */
+void marker4 (long d) { values[0].a_field = d; }	/* set breakpoint 14 here */
diff --git a/gdb/testsuite/gdb.mdebug/info-types-c.exp b/gdb/testsuite/gdb.mdebug/info-types-c.exp
new file mode 100644
index 00000000000..d8c3abeade0
--- /dev/null
+++ b/gdb/testsuite/gdb.mdebug/info-types-c.exp
@@ -0,0 +1,67 @@
+# Copyright 2019-2023 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+require {info exists MDEBUG}
+
+global testfile
+global srcfile
+global binfile
+global subdir
+global srcdir
+global compile_flags
+
+standard_testfile info-types.c
+
+if {[prepare_for_testing "failed to prepare" \
+     "${testfile}" $srcfile "debug c"]} {
+return -1
+}
+
+gdb_test_no_output "set auto-solib-add off"
+
+if ![runto_main] then {
+return 0
+}
+
+set file_re "File .*[string_to_regexp $srcfile]:"\
+
+set output_lines \
+    [list \
+	 "^All defined types:" \
+	 ".*" \
+	 $file_re \
+	 "\[\t \]+typedef enum {\\.\\.\\.} anon_enum_t;" \
+	 "\[\t \]+typedef struct {\\.\\.\\.} anon_struct_t;" \
+	 "\[\t \]+typedef union {\\.\\.\\.} anon_union_t;" \
+	 "\[\t \]+typedef struct baz_t baz;" \
+	 "\[\t \]+typedef struct baz_t \\* baz_ptr;" \
+	 "\[\t \]+typedef struct baz_t baz_t;" \
+	 "\[\t \]+enum enum_t;" \
+	 "\[\t \]+typedef enum enum_t my_enum_t;" \
+	 "\[\t \]+typedef float my_float_t;" \
+	 "\[\t \]+typedef int my_int_t;" \
+	 "\[\t \]+typedef enum {\\.\\.\\.} nested_anon_enum_t;" \
+	 "\[\t \]+typedef struct {\\.\\.\\.} nested_anon_struct_t;" \
+	 "\[\t \]+typedef union {\\.\\.\\.} nested_anon_union_t;" \
+	 "\[\t \]+typedef struct baz_t nested_baz;" \
+	 "\[\t \]+typedef struct baz_t nested_baz_t;" \
+	 "\[\t \]+typedef enum enum_t nested_enum_t;" \
+	 "\[\t \]+typedef float nested_float_t;" \
+	 "\[\t \]+typedef int nested_int_t;" \
+	 "\[\t \]+typedef union union_t nested_union_t;" \
+	 "\[\t \]+union union_t;" \
+	 "($|\r\n.*)"]
+
+gdb_test_lines "info types" "" [multi_line {*}$output_lines]
diff --git a/gdb/testsuite/gdb.mdebug/info-types.c b/gdb/testsuite/gdb.mdebug/info-types.c
new file mode 100644
index 00000000000..15a74b3016a
--- /dev/null
+++ b/gdb/testsuite/gdb.mdebug/info-types.c
@@ -0,0 +1,116 @@
+/* Copyright 2019-2023 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+typedef int my_int_t;
+typedef float my_float_t;
+typedef my_int_t nested_int_t;
+typedef my_float_t nested_float_t;
+
+struct baz_t
+{
+  float f;
+  double d;
+};
+
+typedef struct baz_t baz_t;
+typedef struct baz_t baz;
+typedef baz_t nested_baz_t;
+typedef baz nested_baz;
+typedef struct baz_t *baz_ptr;
+
+enum enum_t
+{
+ AA, BB, CC
+};
+
+typedef enum enum_t my_enum_t;
+typedef my_enum_t nested_enum_t;
+
+typedef struct
+{
+  double d;
+  float f;
+} anon_struct_t;
+
+typedef anon_struct_t nested_anon_struct_t;
+
+typedef enum
+{
+ DD, EE, FF
+} anon_enum_t;
+
+typedef anon_enum_t nested_anon_enum_t;
+
+union union_t
+{
+  int i;
+  float f;
+};
+
+typedef union union_t nested_union_t;
+
+typedef union
+{
+  int i;
+  double d;
+} anon_union_t;
+
+typedef anon_union_t nested_anon_union_t;
+
+volatile int var_a;
+volatile float var_b;
+volatile my_int_t var_c;
+volatile my_float_t var_d;
+volatile nested_int_t var_e;
+volatile nested_float_t var_f;
+volatile struct baz_t var_g;
+volatile baz_t var_h;
+volatile baz var_i;
+volatile nested_baz_t var_j;
+volatile nested_baz var_k;
+volatile baz_ptr var_l;
+volatile enum enum_t var_m;
+volatile my_enum_t var_n;
+volatile nested_enum_t var_o;
+volatile anon_struct_t var_p;
+volatile nested_anon_struct_t var_q;
+volatile anon_enum_t var_r;
+volatile nested_anon_enum_t var_s;
+volatile union union_t var_t;
+volatile nested_union_t var_u;
+volatile anon_union_t var_v;
+volatile nested_anon_union_t var_w;
+
+#ifdef __cplusplus
+
+class CL
+{
+  int a;
+};
+
+typedef CL my_cl;
+typedef CL *my_ptr;
+
+volatile CL var_cpp_a;
+volatile my_cl var_cpp_b;
+volatile my_ptr var_cpp_c;
+
+#endif /* __cplusplus */
+
+int
+main ()
+{
+  return 0;
+}
-- 
2.41.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH?] fixed some segfaults and bugs in mdebug support
  2024-02-16  2:45                   ` [RFC][PATCH?] fixed some segfaults and bugs in mdebug support Zeck S
@ 2024-03-13  2:09                     ` Zeck S
  0 siblings, 0 replies; 15+ messages in thread
From: Zeck S @ 2024-03-13  2:09 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, tom

[-- Attachment #1: Type: text/plain, Size: 95030 bytes --]

Just checking to see if there is still interest in taking this patch. I
could split it up somehow or remove parts of it if I need to.

On Thu, Feb 15, 2024 at 8:48 PM Zeck S <zeck654321@gmail.com> wrote:

> Tried to send this a week ago, but I see somehow send email did not thread
> it correctly.
> Trying again, I think it was because the subject line was messed up.
>
> Addressed comments, adapted some core tests, fixed more bugs.
> In addition to the modified tests, I specifically also got
> gdb.base/relocate.exp working to sort out some problems.
>
> As always, glad to address feedback, and thanks for the patience.
>
> I reverted some of my code around parse_lines addresses.
> The Procedure Descriptor Record format is weird.
> The File Descriptor Record has the final correct address
> of the LOWEST PDR, only. This is used as a base address
> for a kind of janky fixup/relocation system.
> I suspect originally the PDR addresses were directly useful in simpler
> formats.
>
> Based on comments in bfd ecofflink and documentation about something
> similar in
> "MIPS Mdebug Debugging Information" by David Anderson
> that describes HDRR offsets as offsets from the beginning of the a.out
> as a "design mistake" I think some compilers
> set the PDR address to a location in an object file.
>
> This means there's basically a garbage offset added to all the PDRs.
> So all PDRs have their addresses subtracted from the lowest to kind
> treat it as 0, and then the lowest PDRs REAL correct address
> is used from the FDR, as an offset.
>
> IDO actually always outputs the lowest PDR's address as 0
> so subtracting it out does nothing, but hurts nothing.
>
> I can find no real documentation about this.
>
> I did find precedent for what my reverted code was doing in
> section 5.3.4.1 of ""Tru64 UNIX Object File and Symbol Table Format
> Specification"
> but after I figured out what the original code was doing I
> reverted those changes to distrub the code less.
>
> ---
>  gdb/mdebugread.c                          | 230 ++++--
>  gdb/testsuite/boards/qemu-user.exp        | 267 +++++++
>  gdb/testsuite/gdb.mdebug/README           |  67 ++
>  gdb/testsuite/gdb.mdebug/break.c          |  92 +++
>  gdb/testsuite/gdb.mdebug/break.exp        | 921 ++++++++++++++++++++++
>  gdb/testsuite/gdb.mdebug/break1.c         |  59 ++
>  gdb/testsuite/gdb.mdebug/info-types-c.exp |  67 ++
>  gdb/testsuite/gdb.mdebug/info-types.c     | 116 +++
>  8 files changed, 1770 insertions(+), 49 deletions(-)
>  create mode 100644 gdb/testsuite/boards/qemu-user.exp
>  create mode 100644 gdb/testsuite/gdb.mdebug/README
>  create mode 100644 gdb/testsuite/gdb.mdebug/break.c
>  create mode 100644 gdb/testsuite/gdb.mdebug/break.exp
>  create mode 100644 gdb/testsuite/gdb.mdebug/break1.c
>  create mode 100644 gdb/testsuite/gdb.mdebug/info-types-c.exp
>  create mode 100644 gdb/testsuite/gdb.mdebug/info-types.c
>
> diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
> index cd6638224e7..0118507a8f3 100644
> --- a/gdb/mdebugread.c
> +++ b/gdb/mdebugread.c
> @@ -237,7 +237,7 @@ static struct type *new_type (char *);
>  enum block_type { FUNCTION_BLOCK, NON_FUNCTION_BLOCK };
>
>  static struct block *new_block (struct objfile *objfile,
> -                               enum block_type, enum language);
> +                               enum block_type, enum language, bool
> is_global);
>
>  static struct compunit_symtab *new_symtab (const char *, int, struct
> objfile *);
>
> @@ -246,7 +246,7 @@ static struct linetable *new_linetable (int);
>  static struct blockvector *new_bvect (int);
>
>  static struct type *parse_type (int, union aux_ext *, unsigned int, int *,
> -                               int, const char *);
> +                               int, const char *, bool);
>
>  static struct symbol *mylookup_symbol (const char *, const struct block *,
>                                        domain_enum, enum address_class);
> @@ -572,7 +572,7 @@ add_data_symbol (SYMR *sh, union aux_ext *ax, int
> bigend,
>        || sh->sc == scNil || sh->index == indexNil)
>      s->set_type (builtin_type (objfile)->nodebug_data_symbol);
>    else
> -    s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name));
> +    s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name,
> false));
>    /* Value of a data symbol is its memory address.  */
>  }
>
> @@ -624,8 +624,17 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> *ext_sh, int bigend,
>        break;
>      }
>
> -  if (section_index != -1)
> -    sh->value += section_offsets[section_index];
> +  if (section_index != -1) {
> +       int offset = section_offsets[section_index];
> +    if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P | DYNAMIC))
> == 0)
> +    {
> +         /* This is attempting to detect .o files.
> +            Their sections are relocated in symfile.c
> default_symfile_offsets
> +            but section_offsets are set to 0 there and the offset is put
> in the vma.   */
> +         offset +=
> objfile->sections_start[section_index].the_bfd_section->vma;
> +    }
> +       sh->value += offset;
> +  }
>
>    switch (sh->st)
>      {
> @@ -705,7 +714,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> *ext_sh, int bigend,
>           break;
>         }
>        s->set_value_longest (svalue);
> -      s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name));
> +      s->set_type (parse_type (cur_fd, ax, sh->index, 0, bigend, name,
> false));
>        add_symbol (s, top_stack->cur_st, top_stack->cur_block);
>        break;
>
> @@ -761,7 +770,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> *ext_sh, int bigend,
>         t = builtin_type (objfile)->builtin_int;
>        else
>         {
> -         t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name);
> +         t = parse_type (cur_fd, ax, sh->index + 1, 0, bigend, name,
> false);
>           if (strcmp (name, "malloc") == 0
>               && t->code () == TYPE_CODE_VOID)
>             {
> @@ -805,7 +814,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> *ext_sh, int bigend,
>         s->type ()->set_is_prototyped (true);
>
>        /* Create and enter a new lexical context.  */
> -      b = new_block (objfile, FUNCTION_BLOCK, s->language ());
> +      b = new_block (objfile, FUNCTION_BLOCK, s->language (), false);
>        s->set_value_block (b);
>        b->set_function (s);
>        b->set_start (sh->value);
> @@ -1135,7 +1144,13 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> *ext_sh, int bigend,
>         }
>
>        top_stack->blocktype = stBlock;
> -      b = new_block (objfile, NON_FUNCTION_BLOCK, psymtab_language);
> +
> +         /* Using psymtab_langauge fixes commit 76fc0f62138e.
> +            This file does not use buildsym-lecacy.
> +            start_compunit_symtab () is never called.
> +            get_current_subfile () will crash because
> +            buildsym_compunit has never been initialized. */
> +      b = new_block (objfile, NON_FUNCTION_BLOCK, psymtab_language,
> false);
>        b->set_start (sh->value + top_stack->procadr);
>        b->set_superblock (top_stack->cur_block);
>        top_stack->cur_block = b;
> @@ -1247,7 +1262,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> *ext_sh, int bigend,
>         f->set_loc_bitpos (sh->value);
>         bitsize = 0;
>         f->set_type (parse_type (cur_fd, ax, sh->index, &bitsize, bigend,
> -                                name));
> +                                name, false));
>         f->set_bitsize (bitsize);
>        }
>        break;
> @@ -1269,7 +1284,7 @@ parse_symbol (SYMR *sh, union aux_ext *ax, char
> *ext_sh, int bigend,
>        pend = is_pending_symbol (cur_fdr, ext_sh);
>        if (pend == NULL)
>         {
> -         t = parse_type (cur_fd, ax, sh->index, NULL, bigend, name);
> +         t = parse_type (cur_fd, ax, sh->index, NULL, bigend, name, true);
>           add_pending (cur_fdr, ext_sh, t);
>         }
>        else
> @@ -1382,7 +1397,7 @@ basic_type (int bt, struct objfile *objfile)
>    if (map_bt[bt])
>      return map_bt[bt];
>
> -  type_allocator alloc (objfile, get_current_subfile ()->language);
> +  type_allocator alloc (objfile, psymtab_language);
>
>    switch (bt)
>      {
> @@ -1514,7 +1529,7 @@ basic_type (int bt, struct objfile *objfile)
>
>  static struct type *
>  parse_type (int fd, union aux_ext *ax, unsigned int aux_index, int *bs,
> -           int bigend, const char *sym_name)
> +           int bigend, const char *sym_name, bool is_stTypedef)
>  {
>    TIR t[1];
>    struct type *tp = 0;
> @@ -1571,7 +1586,7 @@ parse_type (int fd, union aux_ext *ax, unsigned int
> aux_index, int *bs,
>         }
>      }
>
> -  type_allocator alloc (mdebugread_objfile, get_current_subfile
> ()->language);
> +  type_allocator alloc (mdebugread_objfile, psymtab_language);
>
>    /* Move on to next aux.  */
>    ax++;
> @@ -1628,7 +1643,7 @@ parse_type (int fd, union aux_ext *ax, unsigned int
> aux_index, int *bs,
>        xref_fh = get_rfd (fd, rf);
>        xref_fd = xref_fh - debug_info->fdr;
>        tp = parse_type (xref_fd, debug_info->external_aux +
> xref_fh->iauxBase,
> -                   rn->index, NULL, xref_fh->fBigendian, sym_name);
> +                   rn->index, NULL, xref_fh->fBigendian, sym_name, false);
>      }
>
>    /* All these types really point to some (common) MIPS type
> @@ -1785,6 +1800,13 @@ parse_type (int fd, union aux_ext *ax, unsigned int
> aux_index, int *bs,
>    if (t->continued)
>      complaint (_("illegal TIR continued for %s"), sym_name);
>
> +  if (is_stTypedef)
> +    {
> +       struct type *wrap = alloc.new_type (TYPE_CODE_TYPEDEF, 0,
> sym_name);
> +       wrap->set_target_type(tp);
> +       tp = wrap;
> +    }
> +
>    return tp;
>  }
>
> @@ -1839,7 +1861,7 @@ upgrade_type (int fd, struct type **tpp, int tq,
> union aux_ext *ax, int bigend,
>
>        indx = parse_type (fh - debug_info->fdr,
>                          debug_info->external_aux + fh->iauxBase,
> -                        id, NULL, bigend, sym_name);
> +                        id, NULL, bigend, sym_name, false);
>
>        /* The bounds type should be an integer type, but might be anything
>          else due to corrupt aux entries.  */
> @@ -2155,7 +2177,7 @@ parse_external (EXTR *es, int bigend, const
> section_offsets &section_offsets,
>
>  static void
>  parse_lines (FDR *fh, PDR *pr, struct linetable *lt, int maxlines,
> -            CORE_ADDR lowest_pdr_addr)
> +                          CORE_ADDR textlow, CORE_ADDR lowest_pdr_addr)
>  {
>    unsigned char *base;
>    int j, k;
> @@ -2169,7 +2191,6 @@ parse_lines (FDR *fh, PDR *pr, struct linetable *lt,
> int maxlines,
>    for (j = 0; j < fh->cpd; j++, pr++)
>      {
>        CORE_ADDR l;
> -      CORE_ADDR adr;
>        unsigned char *halt;
>
>        /* No code for this one.  */
> @@ -2186,9 +2207,15 @@ parse_lines (FDR *fh, PDR *pr, struct linetable
> *lt, int maxlines,
>         halt = base + fh->cbLine;
>        base += pr->cbLineOffset;
>
> -      adr = pr->adr - lowest_pdr_addr;
> -
> -      l = adr >> 2;            /* in words */
> +      /* textlow is the FDR->adr, for the file containing these PDRs.
> +         FDR->adr is the absolute address of the lowest PDR.
> +         PDR->adr's themselves, are relative offsets.
> +         The PDR->adr's may start at 0, or they could have an offset
> +         based on the PDRs position in an object file.
> +         This is why the lowest PDR address is subtracted
> +         from all other PDRs addresses, to subtract out the potential
> offset.
> +         See bfd/ecofflink.c comments. */
> +      l = (textlow +  pr->adr - lowest_pdr_addr) >> 2;         /* in
> words */
>        for (lineno = pr->lnLow; base < halt;)
>         {
>           count = *base & 0x0f;
> @@ -3047,28 +3074,49 @@ parse_partial_symbols (minimal_symbol_reader
> &reader,
>                        index into the namestring which indicates the
>                        debugging type symbol.  */
>
> +                       int section;
> +                       bfd_vma address;
> +
>                     switch (p[1])
>                       {
>                       case 'S':
> +                         section = SECT_OFF_DATA (objfile);
> +                         address = sh.value;
> +                       if ((bfd_get_file_flags (objfile->obfd.get ()) &
> (EXEC_P | DYNAMIC)) == 0)
> +                       {
> +                         /* This is attempting to detect .o files.
> +                            Their sections are relocated in symfile.c
> default_symfile_offsets
> +                            but section_offsets are set to 0 there and
> the offset is put in the vma.   */
> +                         address +=
> objfile->sections_start[section].the_bfd_section->vma;
> +                       }
>                         pst->add_psymbol (gdb::string_view (namestring,
>                                                             p -
> namestring),
>                                           true, VAR_DOMAIN, LOC_STATIC,
> -                                         SECT_OFF_DATA (objfile),
> +                                         section,
>                                           psymbol_placement::STATIC,
> -                                         unrelocated_addr (sh.value),
> +                                         unrelocated_addr (address),
>                                           psymtab_language,
>                                           partial_symtabs, objfile);
>                         continue;
>                       case 'G':
> +                         section = SECT_OFF_DATA (objfile);
> +                         address = sh.value;
> +                       if ((bfd_get_file_flags (objfile->obfd.get ()) &
> (EXEC_P | DYNAMIC)) == 0)
> +                       {
> +                         /* This is attempting to detect .o files.
> +                           Their sections are relocated in symfile.c
> default_symfile_offsets
> +                           but section_offsets are set to 0 there and the
> offset is put in the vma.    */
> +                         address +=
> objfile->sections_start[section].the_bfd_section->vma;
> +                       }
>                         /* The addresses in these entries are reported
>                            to be wrong.  See the code that reads 'G's
>                            for symtabs.  */
>                         pst->add_psymbol (gdb::string_view (namestring,
>                                                             p -
> namestring),
>                                           true, VAR_DOMAIN, LOC_STATIC,
> -                                         SECT_OFF_DATA (objfile),
> +                                         section,
>                                           psymbol_placement::GLOBAL,
> -                                         unrelocated_addr (sh.value),
> +                                         unrelocated_addr (address),
>                                           psymtab_language,
>                                           partial_symtabs, objfile);
>                         continue;
> @@ -3215,12 +3263,21 @@ parse_partial_symbols (minimal_symbol_reader
> &reader,
>                             function_outside_compilation_unit_complaint
>                               (copy.c_str ());
>                           }
> +                       section = SECT_OFF_TEXT (objfile);
> +                       address = sh.value;
> +                       if ((bfd_get_file_flags (objfile->obfd.get ()) &
> (EXEC_P | DYNAMIC)) == 0)
> +                       {
> +                         /* This is attempting to detect .o files.
> +                            Their sections are relocated in symfile.c
> default_symfile_offsets
> +                            but section_offsets are set to 0 there and
> the offset is put in the vma.   */
> +                         address +=
> objfile->sections_start[section].the_bfd_section->vma;
> +                       }
>                         pst->add_psymbol (gdb::string_view (namestring,
>                                                             p -
> namestring),
>                                           true, VAR_DOMAIN, LOC_BLOCK,
> -                                         SECT_OFF_TEXT (objfile),
> +                                         section,
>                                           psymbol_placement::STATIC,
> -                                         unrelocated_addr (sh.value),
> +                                         unrelocated_addr (address),
>                                           psymtab_language,
>                                           partial_symtabs, objfile);
>                         continue;
> @@ -3236,12 +3293,21 @@ parse_partial_symbols (minimal_symbol_reader
> &reader,
>                             function_outside_compilation_unit_complaint
>                               (copy.c_str ());
>                           }
> +                       section = SECT_OFF_TEXT (objfile);
> +                       address = sh.value;
> +                       if ((bfd_get_file_flags (objfile->obfd.get ()) &
> (EXEC_P | DYNAMIC)) == 0)
> +                       {
> +                         /* This is attempting to detect .o files.
> +                            Their sections are relocated in symfile.c
> default_symfile_offsets
> +                            but section_offsets are set to 0 there and
> the offset is put in the vma.   */
> +                         address +=
> objfile->sections_start[section].the_bfd_section->vma;
> +                       }
>                         pst->add_psymbol (gdb::string_view (namestring,
>                                                             p -
> namestring),
>                                           true, VAR_DOMAIN, LOC_BLOCK,
> -                                         SECT_OFF_TEXT (objfile),
> +                                         section,
>                                           psymbol_placement::GLOBAL,
> -                                         unrelocated_addr (sh.value),
> +                                         unrelocated_addr (address),
>                                           psymtab_language,
>                                           partial_symtabs, objfile);
>                         continue;
> @@ -3419,6 +3485,9 @@ parse_partial_symbols (minimal_symbol_reader &reader,
>                   break;
>                 }
>
> +               bfd_vma address;
> +               /* some symbols don't use the above section */
> +               int section_override;
>               switch (sh.st)
>                 {
>                   unrelocated_addr high;
> @@ -3426,9 +3495,18 @@ parse_partial_symbols (minimal_symbol_reader
> &reader,
>                   int new_sdx;
>
>                 case stStaticProc:
> -                 reader.record_with_info (sym_name, minsym_value,
> +                   section_override = SECT_OFF_TEXT (objfile);
> +                       address = (bfd_vma)minsym_value;
> +                       if ((bfd_get_file_flags (objfile->obfd.get ()) &
> (EXEC_P | DYNAMIC)) == 0)
> +                       {
> +                         /* This is attempting to detect .o files.
> +                            Their sections are relocated in symfile.c
> default_symfile_offsets
> +                            but section_offsets are set to 0 there and
> the offset is put in the vma.   */
> +                         address +=
> objfile->sections_start[section_override].the_bfd_section->vma;
> +                       }
> +                 reader.record_with_info (sym_name,
> (unrelocated_addr)address,
>                                            mst_file_text,
> -                                          SECT_OFF_TEXT (objfile));
> +                                          section_override);
>
>                   /* FALLTHROUGH */
>
> @@ -3476,12 +3554,21 @@ parse_partial_symbols (minimal_symbol_reader
> &reader,
>                      still able to find the PROGRAM name via the partial
>                      symbol table, and the MAIN__ symbol via the minimal
>                      symbol table.  */
> +
> +                 address = sh.value;
> +                 if ((bfd_get_file_flags (objfile->obfd.get ()) & (EXEC_P
> | DYNAMIC)) == 0)
> +                 {
> +                        /* This is attempting to detect .o files.
> +                        Their sections are relocated in symfile.c
> default_symfile_offsets
> +                        but section_offsets are set to 0 there and the
> offset is put in the vma.       */
> +                        address +=
> objfile->sections_start[section].the_bfd_section->vma;
> +                 }
>                   if (sh.st == stProc)
>                     pst->add_psymbol (sym_name, true,
>                                       VAR_DOMAIN, LOC_BLOCK,
>                                       section,
>                                       psymbol_placement::GLOBAL,
> -                                     unrelocated_addr (sh.value),
> +                                     unrelocated_addr (address),
>                                       psymtab_language,
>                                       partial_symtabs, objfile);
>                   else
> @@ -3489,7 +3576,7 @@ parse_partial_symbols (minimal_symbol_reader &reader,
>                                       VAR_DOMAIN, LOC_BLOCK,
>                                       section,
>                                       psymbol_placement::STATIC,
> -                                     unrelocated_addr (sh.value),
> +                                     unrelocated_addr (address),
>                                       psymtab_language,
>                                       partial_symtabs, objfile);
>
> @@ -3517,13 +3604,36 @@ parse_partial_symbols (minimal_symbol_reader
> &reader,
>
>                 case stStatic:  /* Variable */
>                   if (SC_IS_DATA (sh.sc))
> -                   reader.record_with_info (sym_name, minsym_value,
> +                 {
> +                       section_override = SECT_OFF_DATA (objfile);
> +                       address = 0;
> +                       if ((bfd_get_file_flags (objfile->obfd.get ()) &
> (EXEC_P | DYNAMIC)) == 0)
> +                       {
> +                         /* This is attempting to detect .o files.
> +                            Their sections are relocated in symfile.c
> default_symfile_offsets
> +                            but section_offsets are set to 0 there and
> the offset is put in the vma.   */
> +                            address +=
> objfile->sections_start[section_override].the_bfd_section->vma;
> +                       }
> +                   reader.record_with_info (sym_name,
> (unrelocated_addr)(((bfd_vma)minsym_value + address)),
>                                              mst_file_data,
> -                                            SECT_OFF_DATA (objfile));
> +                                            section_override);
> +                 }
>                   else
> -                   reader.record_with_info (sym_name, minsym_value,
> +                 {
> +                       section_override = SECT_OFF_BSS (objfile);
> +                       address = 0;
> +                       if ((bfd_get_file_flags (objfile->obfd.get ()) &
> (EXEC_P | DYNAMIC)) == 0)
> +                       {
> +                         /* This is attempting to detect .o files.
> +                            Their sections are relocated in symfile.c
> default_symfile_offsets
> +                            but section_offsets are set to 0 there and
> the offset is put in the vma.   */
> +                         address +=
> objfile->sections_start[section_override].the_bfd_section->vma;
> +                       }
> +                   reader.record_with_info (sym_name,
> (unrelocated_addr)(((bfd_vma)minsym_value + address)),
>                                              mst_file_bss,
> -                                            SECT_OFF_BSS (objfile));
> +                                            section_override);
> +                 }
> +
>                   theclass = LOC_STATIC;
>                   break;
>
> @@ -3596,11 +3706,19 @@ parse_partial_symbols (minimal_symbol_reader
> &reader,
>                   cur_sdx++;
>                   continue;
>                 }
> +                       address = sh.value;
> +                       if ((bfd_get_file_flags (objfile->obfd.get ()) &
> (EXEC_P | DYNAMIC)) == 0)
> +                       {
> +                         /* This is attempting to detect .o files.
> +                            Their sections are relocated in symfile.c
> default_symfile_offsets
> +                            but section_offsets are set to 0 there and
> the offset is put in the vma.   */
> +                         address +=
> objfile->sections_start[section].the_bfd_section->vma;
> +                       }
>               /* Use this gdb symbol.  */
>               pst->add_psymbol (sym_name, true,
>                                 VAR_DOMAIN, theclass, section,
>                                 psymbol_placement::STATIC,
> -                               unrelocated_addr (sh.value),
> +                               unrelocated_addr (address),
>                                 psymtab_language,
>                                 partial_symtabs, objfile);
>             skip:
> @@ -3676,12 +3794,20 @@ parse_partial_symbols (minimal_symbol_reader
> &reader,
>                   theclass = LOC_STATIC;
>                   break;
>                 }
> +                 bfd_vma address = svalue;
>               char *sym_name = debug_info->ssext + psh->iss;
> +                       if ((bfd_get_file_flags (objfile->obfd.get ()) &
> (EXEC_P | DYNAMIC)) == 0)
> +                       {
> +                         /* This is attempting to detect .o files.
> +                            Their sections are relocated in symfile.c
> default_symfile_offsets
> +                            but section_offsets are set to 0 there and
> the offset is put in the vma.   */
> +                         address +=
> objfile->sections_start[section].the_bfd_section->vma;
> +                       }
>               pst->add_psymbol (sym_name, true,
>                                 VAR_DOMAIN, theclass,
>                                 section,
>                                 psymbol_placement::GLOBAL,
> -                               unrelocated_addr (svalue),
> +                               unrelocated_addr (address),
>                                 psymtab_language,
>                                 partial_symtabs, objfile);
>             }
> @@ -4163,7 +4289,7 @@ mdebug_expand_psymtab (legacy_psymtab *pst, struct
> objfile *objfile)
>                 }
>
>               parse_lines (fh, pr_block.data (), lines, maxlines,
> -                          lowest_pdr_addr);
> +                          (CORE_ADDR) pst->unrelocated_text_low (),
> lowest_pdr_addr);
>               if (lines->nitems < fh->cline)
>                 lines = shrink_linetable (lines);
>
> @@ -4291,7 +4417,7 @@ cross_ref (int fd, union aux_ext *ax, struct type
> **tpp,
>        rf = rn->rfd;
>      }
>
> -  type_allocator alloc (mdebugread_objfile, get_current_subfile
> ()->language);
> +  type_allocator alloc (mdebugread_objfile, psymtab_language);
>
>    /* mips cc uses a rf of -1 for opaque struct definitions.
>       Set TYPE_STUB for these types so that check_typedef will
> @@ -4412,7 +4538,8 @@ cross_ref (int fd, union aux_ext *ax, struct type
> **tpp,
>                                  sh.index,
>                                  NULL,
>                                  fh->fBigendian,
> -                                debug_info->ss + fh->issBase + sh.iss);
> +                                debug_info->ss + fh->issBase + sh.iss,
> +                                sh.st == stTypedef);
>               add_pending (fh, esh, *tpp);
>               break;
>
> @@ -4438,7 +4565,8 @@ cross_ref (int fd, union aux_ext *ax, struct type
> **tpp,
>                              sh.index,
>                              NULL,
>                              fh->fBigendian,
> -                            debug_info->ss + fh->issBase + sh.iss);
> +                            debug_info->ss + fh->issBase + sh.iss,
> +                            true);
>         }
>        else
>         {
> @@ -4542,6 +4670,8 @@ add_line (struct linetable *lt, int lineno,
> CORE_ADDR adr, int last)
>      return lineno;
>
>    lt->item[lt->nitems].line = lineno;
> +  lt->item[lt->nitems].is_stmt = 1;
> +  lt->item[lt->nitems].prologue_end = 1;
>    lt->item[lt->nitems++].set_unrelocated_pc (unrelocated_addr (adr << 2));
>    return lineno;
>  }
> @@ -4634,9 +4764,10 @@ new_symtab (const char *name, int maxlines, struct
> objfile *objfile)
>
>    /* All symtabs must have at least two blocks.  */
>    bv = new_bvect (2);
> -  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
> lang));
> -  bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
> lang));
> +  bv->set_block (GLOBAL_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
> lang, true));
> +  bv->set_block (STATIC_BLOCK, new_block (objfile, NON_FUNCTION_BLOCK,
> lang, false));
>    bv->static_block ()->set_superblock (bv->global_block ());
> +  bv->global_block ()->set_compunit_symtab(cust);
>    cust->set_blockvector (bv);
>
>    cust->set_debugformat ("ECOFF");
> @@ -4723,9 +4854,11 @@ new_bvect (int nblocks)
>
>  static struct block *
>  new_block (struct objfile *objfile, enum block_type type,
> -          enum language language)
> +          enum language language, bool is_global)
>  {
> -  struct block *retval = new (&objfile->objfile_obstack) block;
> +  struct block *retval = (is_global
> +    ? new (&objfile->objfile_obstack) global_block
> +    : new (&objfile->objfile_obstack) block);
>
>    if (type == FUNCTION_BLOCK)
>      retval->set_multidict (mdict_create_linear_expandable (language));
> @@ -4754,8 +4887,7 @@ new_type (char *name)
>  {
>    struct type *t;
>
> -  t = type_allocator (mdebugread_objfile,
> -                     get_current_subfile ()->language).new_type ();
> +  t = type_allocator (mdebugread_objfile, psymtab_language).new_type ();
>    t->set_name (name);
>    INIT_CPLUS_SPECIFIC (t);
>    return t;
> diff --git a/gdb/testsuite/boards/qemu-user.exp
> b/gdb/testsuite/boards/qemu-user.exp
> new file mode 100644
> index 00000000000..8a48ab7f6d4
> --- /dev/null
> +++ b/gdb/testsuite/boards/qemu-user.exp
> @@ -0,0 +1,267 @@
> +# Copyright 2020-2023 Free Software Foundation, Inc.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +# NOTICE:
> +# QEMU user mode gdbstub has multithreading problems
> +# https://gitlab.com/qemu-project/qemu/-/issues/1671
> +# run with --ignore gdb.threads/access-mem-running-thread-exit.exp
> +# since this test in particular seems to hang. Could be related.
> +
> +# This technique is from native-extended-gdbserver.exp
> +# We must load this explicitly here, and rename the procedures we want
> +# to override.  If we didn't do this, given that mi-support.exp is
> +# loaded later in the test files, the procedures loaded then would
> +# override our definitions.
> +load_lib mi-support.exp
> +
> +set qemu ""
> +set qemu_arch ""
> +set qemu_user_spawn_id ""
> +set qemu_user_last_load_file ""
> +set port "2345"
> +
> +# add sections for other architectures
> +# as needed
> +case "$target_triplet" in {
> +       { "mips" } {
> +               set qemu "qemu-mips"
> +               set qemu_arch "mips"
> +       }
> +       default {
> +               puts "No target hardware for $target_triplet"
> +       }
> +}
> +
> +# QEMU supports some of extended-remote, but not the run command
> +# which causes problems for the test suite
> +set_board_info gdb_protocol "remote"
> +set_board_info use_gdb_stub 1
> +set_board_info gdb,do_reload_on_run 1
> +set_board_info exit_is_reliable 1
> +# static link so we do not need to configure an QEMU LD prefix and install
> +# libraries for the target architecture
> +set_board_info ldflags "-static"
> +
> +# technique from simavr.exp
> +#
> +# Load executable into GDB
> +#
> +proc gdb_load { arg } {
> +    global gdb_prompt
> +    global verbose
> +    global spawn_id
> +    global qemu
> +    global qemu_user_last_load_file
> +    global qemu_user_spawn_id
> +    global port
> +
> +    # keep track of last loaded file
> +    # to simulate run restart like behavior
> +    if { $arg == "" } {
> +               set arg $qemu_user_last_load_file
> +               if { $arg == ""  } {
> +                       global last_loaded_file
> +                       # this fallback is needed
> +                       # for tests like gdb.base/break-unload-file.exp
> +                       set arg $last_loaded_file
> +               }
> +    } else {
> +       set qemu_user_last_load_file $arg
> +    }
> +
> +    if { $arg != "" } {
> +               if {[gdb_file_cmd $arg]} then { return -1 }
> +    }
> +
> +    # Close any previous qemu user instance.
> +    if { $qemu_user_spawn_id != "" } {
> +               verbose -log "qemu user: closing previous spawn id
> $qemu_user_spawn_id"
> +               if [catch { close -i $qemu_user_spawn_id } != 0] {
> +                       warning "qemu user: failed to close connection to
> previous qemu user instance"
> +               }
> +
> +               # some tests get QEMU
> +               # into a state where the process doesn't want to die
> +               # so -nowait to not hang the entire test run
> +               wait -nowait -i $qemu_user_spawn_id
> +               set qemu_user_spawn_id ""
> +    }
> +
> +    # technique from gdbserver-support.exp
> +    # Loop till we find a free port.
> +    # This is to cope with qemu sometimes getting
> +    # into a bad state and not closing.
> +    # Do a pkill -f qemu-mips -9 after running tests
> +    # to remove stragglers
> +    while 1 {
> +               # Run QEMU user
> +               set cmd "spawn -noecho $qemu -g $port $arg"
> +               verbose -log "Spawning qemu user: $cmd"
> +               eval $cmd
> +               set qemu_user_spawn_id $spawn_id
> +
> +               # without inferior_spawn_id
> +               # tests such as gdb.base/a2-run.exp
> +               # can't look at the right process output
> +               global inferior_spawn_id
> +               set inferior_spawn_id $spawn_id
> +
> +               expect {
> +                       -i $qemu_user_spawn_id
> +                       -timeout 1
> +                       -re ".*qemu: could not open gdbserver on.*" {
> +                               verbose -log "Port $port is already in
> use."
> +                               if [catch { close -i $qemu_user_spawn_id }
> != 0] {
> +                                       warning "qemu user: failed to
> close connection to previous qemu user instance"
> +                               }
> +                               # Bump the port number to avoid the
> conflict.
> +                               wait -i $qemu_user_spawn_id
> +                               incr port
> +                               continue
> +                       }
> +               }
> +               break
> +       }
> +
> +    # arch needs set because some tests
> +    # take actions that need arch to be correct
> +    # immediately, before file command loads the binary
> +    # for example gdb.base/break-unload-file.exp
> +    global qemu_arch
> +    send_gdb "set arch $qemu_arch\n"
> +
> +    # Connect to qemu user.
> +    send_gdb "target remote :$port\n"
> +    gdb_expect {
> +               # qemu user mode gdb stub does not support non stop mode
> +               # this is so tests that attempt it die quickly
> +               -re ".*Non-stop mode requested, but remote does not
> support non-stop.*" {
> +                       error "qemu user does not support non-stop"
> +               }
> +               # cannot have multiple targets connected
> +               # some tests end up causing this to be attempted in
> gdb.multi
> +               -re ".*Already connected to a remote target.
> Disconnect?.*" {
> +                       send_gdb "y\n"
> +                       gdb_expect {
> +                               -re ".*$gdb_prompt $" {
> +                                       error "connected to new target
> while connected to existing"
> +                               }
> +                       }
> +               }
> +               -re ".*$gdb_prompt $" {
> +                       if $verbose>1 then {
> +                               send_user "Connected to QEMU target\n"
> +                       }
> +               }
> +               -re "Remote debugging using .*$gdb_prompt $" {
> +                       verbose "Set target to remote for QEMU"
> +               }
> +               timeout {
> +                       verbose -log "qemu-user: unable to connect to qemu
> user, closing qemu user spawn id"
> +                       close -i $qemu_user_spawn_id
> +                       verbose -log "qemu-user: unable to connect to qemu
> user, waiting for qemu user process exit"
> +                       wait -i $qemu_user_spawn_id
> +                       set qemu_user_spawn_id ""
> +                       error "unable to connect to qemu user stub"
> +               }
> +       }
> +
> +       return 0
> +}
> +
> +# technique from native-extended-gdbserver.exp
> +# for overriding mi-support.exp procs
> +if { [info procs original_mi_gdb_load] == "" } {
> +    rename mi_gdb_load original_gdbserver_mi_gdb_load
> +}
> +
> +# same ideas as gdb_load, but adapted for mi
> +proc mi_gdb_load { arg } {
> +    global gdb_prompt
> +    global verbose
> +    global spawn_id
> +    global qemu
> +    global qemu_user_last_load_file
> +    global qemu_user_spawn_id
> +    global port
> +
> +    if { $arg == "" } {
> +               set arg $qemu_user_last_load_file
> +               if { $arg == ""  } {
> +                       global last_loaded_file
> +                       set arg $last_loaded_file
> +               }
> +    } else {
> +               set qemu_user_last_load_file $arg
> +    }
> +
> +    if { $arg != "" } {
> +               if {[mi_gdb_file_cmd $arg]} then { return -1 }
> +    }
> +
> +    #Close any previous qemu user instance.
> +    if { $qemu_user_spawn_id != "" } {
> +               verbose -log "qemu user: closing previous spawn id
> $qemu_user_spawn_id"
> +               if [catch { close -i $qemu_user_spawn_id } != 0] {
> +                       warning "qemu user: failed to close previous qemu
> user instance"
> +               }
> +
> +               # some tests get QEMU/its gdb stub
> +               # into a state where the process doesn't want to die
> +               # so -nowait to not hang the entire test run
> +               wait -nowait -i $qemu_user_spawn_id
> +               set qemu_user_spawn_id ""
> +    }
> +
> +    # technique from gdbserver-support.exp
> +    # Loop till we find a free port.
> +    while 1 {
> +               # Run QEMU user
> +               set cmd "spawn -noecho $qemu -g $port $arg"
> +               verbose -log "Spawning qemu user: $cmd"
> +               eval $cmd
> +               set qemu_user_spawn_id $spawn_id
> +
> +               # without inferior_spawn_id
> +               # tests such as gdb.base/a2-run.exp
> +               # can't look at the right process output
> +               global inferior_spawn_id
> +               set inferior_spawn_id $spawn_id
> +
> +               expect {
> +                       -i $qemu_user_spawn_id
> +                       -timeout 1
> +                       -re ".*qemu: could not open gdbserver on.*" {
> +                               verbose -log "Port $port is already in
> use."
> +                               if [catch { close -i $qemu_user_spawn_id }
> != 0] {
> +                                       warning "qemu user: failed to
> close connection to previous qemu user instance"
> +                               }
> +                               # Bump the port number to avoid the
> conflict.
> +                               wait -i $qemu_user_spawn_id
> +                               incr port
> +                               continue
> +                       }
> +               }
> +               break
> +       }
> +
> +       return 0
> +}
> +
> +# technique from stdio-gdb-server-base.exp
> +proc mi_gdb_target_load { } {
> +    global port
> +    return [mi_gdb_target_cmd "remote" ":$port"]
> +}
> diff --git a/gdb/testsuite/gdb.mdebug/README
> b/gdb/testsuite/gdb.mdebug/README
> new file mode 100644
> index 00000000000..f749d3e1cbc
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mdebug/README
> @@ -0,0 +1,67 @@
> +These tests are adapted for dealing with the limitations of old compilers
> and the mdebug format.
> +
> +Execute the mdebug tests with something like:
> +runtest gdb.mdebug/info-types-c.exp CC_FOR_TARGET=idowrapper MDEBUG=1
> --target mips --target_board qemu-user
> +
> +The CC_FOR_TARGET is probably going to be a wrapper script
> +for an old compiler running in an emulator.
> +Example at the end of this readme.
> +Put it somewhere in your PATH.
> +Do not give the path to the script as CC_FOR_TARGET, that does not work.
> +
> +The qemu-user board will run mips programs in qemu user mode
> +and connect gdb to QEMU's gdb stub.
> +
> +mdebug does not record line numbers for type definitions.
> +This is why the info types tests are modified
> +
> +mdebug output can be odd in how line mappings deal with the start of
> functions.
> +This is why the break tests are modified.
> +
> +Most compilers outputting mdebug data are so old they cannot deal with
> C99 features.
> +This makes parsing standard library headers for modern implementations a
> problem.
> +Also, even past that, the headers may eventually lead to compiler
> specific code
> +in things like stddef.h that may not be appriopriate for your old
> compiler.
> +This is why break.c has some modified standard function signatures at the
> beginning.
> +
> +A few of the break tests *sometimes* fail still.
> +Several memory initialization bugs were fixed but whatever is causing that
> +is very inconsistent. Or it could be some issue with the exp file.
> +
> +The best documentation on mdebug are these documents:
> +
> +"Tru64 UNIX Object File and Symbol Table Format Specification"
> +Sections 5, 9, and 10
> +Note, the word "mdebug" is never used, but it is that format
> +carried through from DEC to Compaq.
> +
> +"MIPS Mdebug Debugging Information" by David Anderson
> +
> +A wrapper script like this was used to create .o files with IDO running
> in an emulator.
> +The final link commands are run with regular linux gcc.
> +
> +#!/bin/sh
> +
> +echo "$@" >> theargs
> +
> +for arg do
> +  if [ "$arg" = "-static" ]
> +  then
> +    link=1
> +  fi
> +done
> +
> +if [ "$link" = 1 ]
> +then
> +  mips-unknown-linux-gnu-gcc "$@" -fno-exceptions
> +else
> +for arg do
> +  shift
> +  [ "$arg" = "-fdiagnostics-color=never" ] && continue
> +  [ "$arg" = "-g" ] && continue
> +  set -- "$@" "$arg"
> +done
> +
> +/home/someuser/qemu-irix/build/irix-linux-user/qemu-irix -L
> /home/someuser/ido/ido7.1_compiler
> /home/someuser/ido/ido7.1_compiler/usr/bin/cc -Xcpluscomm -nostdlib
> -nostdinc -mips2
> +-g2 "$@" 2>/dev/null
> +fi
> diff --git a/gdb/testsuite/gdb.mdebug/break.c
> b/gdb/testsuite/gdb.mdebug/break.c
> new file mode 100644
> index 00000000000..d0b26998c77
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mdebug/break.c
> @@ -0,0 +1,92 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 1992-2023 Free Software Foundation, Inc.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.
> */
> +
> +int printf (const char *__format, ...);
> +int fprintf (void *__stream, const char *__format, ...);
> +int atoi(const char *_Str);
> +void *malloc(int size);
> +
> +extern void * stderr;
> +
> +extern int marker1 (void);
> +extern int marker2 (int a);
> +extern void marker3 (char *a, char *b);
> +extern void marker4 (long d);
> +
> +/* We're used by a test that requires malloc, so make sure it is in
> +   the executable.  */
> +void *need_malloc ()
> +{
> +  return malloc (1);
> +}
> +
> +/*
> + *     This simple classical example of recursion is useful for
> + *     testing stack backtraces and such.
> + */
> +
> +int factorial(int);
> +
> +int
> +main (int argc, char **argv, char **envp)
> +{ /* set breakpoint 6 here */
> +  if (argc == 12345) {  /* an unlikely value < 2^16, in case uninited */
> +       fprintf (stderr, "usage:  factorial <number>\n");
> +       return 1;
> +    }
> +    printf ("%d\n", factorial (atoi ("6")));  /* set breakpoint 1 here */
> +    /* set breakpoint 12 here */
> +    marker1 ();  /* set breakpoint 11 here */
> +    marker2 (43); /* set breakpoint 20 here */
> +    marker3 ("stack", "trace"); /* set breakpoint 21 here */
> +    marker4 (177601976L);
> +    /* We're used by a test that requires malloc, so make sure it is
> +       in the executable.  */
> +    (void)malloc (1);
> +
> +    argc = (argc == 12345); /* This is silly, but we can step off of it
> */ /* set breakpoint 2 here */
> +    return argc;  /* set breakpoint 10 here */
> +} /* set breakpoint 10a here */
> +
> +int factorial (int value)
> +{ /* set breakpoint 7 here */
> +  if (value > 1) { /* set breakpoint 7a here */
> +       value *= factorial (value - 1);
> +    }
> +    return (value); /* set breakpoint 19 here */
> +}
> +
> +int multi_line_if_conditional (int a, int b, int c)
> +{ /* set breakpoint 3 here */
> +  if (a
> +      && b
> +      && c)
> +    return 0;
> +  else
> +    return 1;
> +}
> +
> +int multi_line_while_conditional (int a, int b, int c)
> +{ /* set breakpoint 4 here */
> +  while (a
> +      && b
> +      && c)
> +    {
> +      a--, b--, c--;
> +    }
> +  return 0;
> +}
> diff --git a/gdb/testsuite/gdb.mdebug/break.exp
> b/gdb/testsuite/gdb.mdebug/break.exp
> new file mode 100644
> index 00000000000..cb74c1b0081
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mdebug/break.exp
> @@ -0,0 +1,921 @@
> +#   Copyright 1988-2023 Free Software Foundation, Inc.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +# This file was written by Rob Savoye. (rob@cygnus.com)
> +
> +if { [build_executable "failed to prepare" "break" {break.c break1.c}
> {debug nowarnings}] } {
> +    return -1
> +}
> +set srcfile break.c
> +set srcfile1 break1.c
> +
> +set bp_location1 [gdb_get_line_number "set breakpoint 1 here"]
> +set bp_location2 [gdb_get_line_number "set breakpoint 2 here"]
> +set bp_location3 [gdb_get_line_number "set breakpoint 3 here"]
> +set bp_location4 [gdb_get_line_number "set breakpoint 4 here"]
> +set bp_location6 [gdb_get_line_number "set breakpoint 6 here"]
> +set bp_location7 [gdb_get_line_number "set breakpoint 7 here"]
> +set bp_location8 [gdb_get_line_number "set breakpoint 8 here" $srcfile1]
> +set bp_location11 [gdb_get_line_number "set breakpoint 11 here"]
> +
> +set main_line $bp_location6
> +
> +# In C++ mode, we see a full prototype; in C mode, we only see the
> +# function name, with no parameter info.
> +proc func {name} {
> +    return "${name}(?:\(\[^\r\n\]*\))?"
> +}
> +
> +# test simple breakpoint setting commands
> +
> +proc_with_prefix test_break {} {
> +    clean_restart break
> +
> +    # Test deleting all breakpoints when there are none installed,
> +    # GDB should not prompt for confirmation.
> +    # Note that lib/gdb.exp provides a "delete_breakpoints" proc
> +    # for general use elsewhere.
> +    send_gdb "delete breakpoints\n"
> +    gdb_expect {
> +        -re "Delete all breakpoints.*$" {
> +               send_gdb "y\n"
> +               gdb_expect {
> +                   -re "$::gdb_prompt $" {
> +                       fail "delete all breakpoints when none (unexpected
> prompt)"
> +                   }
> +                   timeout     { fail "delete all breakpoints when none
> (timeout after unexpected prompt)" }
> +               }
> +           }
> +        -re ".*$::gdb_prompt $"       { pass "delete all breakpoints when
> none" }
> +       timeout             { fail "delete all breakpoints when none
> (timeout)" }
> +    }
> +
> +    # test break at function
> +    gdb_test "break -q main" \
> +       "Breakpoint.*at.* file .*$::srcfile, line.*" \
> +       "breakpoint function"
> +
> +    # test break at quoted function
> +    gdb_test "break \"marker2\"" \
> +       "Breakpoint.*at.* file .*$::srcfile1, line.*" \
> +       "breakpoint quoted function"
> +
> +    # test break at function in file
> +    gdb_test "break $::srcfile:factorial" \
> +       "Breakpoint.*at.* file .*$::srcfile, line.*" \
> +       "breakpoint function in file"
> +
> +    # test break at line number
> +    #
> +    # Note that the default source file is the last one whose source text
> +    # was printed.  For native debugging, before we've executed the
> +    # program, this is the file containing main, but for remote debugging,
> +    # it's wherever the processor was stopped when we connected to the
> +    # board.  So, to be sure, we do a list command.
> +    gdb_test "list -q main" \
> +       ".*main \\(int argc, char \\*\\*argv, char \\*\\*envp\\).*" \
> +       "use `list' to establish default source file"
> +
> +    gdb_test "break $::bp_location1" \
> +       "Breakpoint.*at.* file .*$::srcfile, line $::bp_location1\\." \
> +       "breakpoint line number"
> +
> +    # test duplicate breakpoint
> +    gdb_test "break $::bp_location1" \
> +       "Note: breakpoint \[0-9\]+ also set at pc.*Breakpoint \[0-9\]+
> at.* file .*$::srcfile, line $::bp_location1\\." \
> +       "breakpoint duplicate"
> +
> +    # test break at line number in file
> +    gdb_test "break $::srcfile:$::bp_location2" \
> +       "Breakpoint.*at.* file .*$::srcfile, line $::bp_location2\\." \
> +       "breakpoint line number in file"
> +
> +    # Test putting a break at the start of a multi-line if conditional.
> +    # Verify the breakpoint was put at the start of the conditional.
> +    gdb_test "break multi_line_if_conditional" \
> +       "Breakpoint.*at.* file .*$::srcfile, line $::bp_location3\\." \
> +       "breakpoint at start of multi line if conditional"
> +
> +    gdb_test "break multi_line_while_conditional" \
> +       "Breakpoint.*at.* file .*$::srcfile, line $::bp_location4\\." \
> +       "breakpoint at start of multi line while conditional"
> +
> +    gdb_test "info break" \
> +       [multi_line "Num     Type\[ \]+Disp Enb Address\[ \]+What.*" \
> +                   "$::decimal\[\t \]+breakpoint     keep y.* in [func
> main] at .*$::srcfile:$::main_line.*" \
> +                   "$::decimal\[\t \]+breakpoint     keep y.* in [func
> marker2] at .*$::srcfile1:$::bp_location8.*" \
> +                   "$::decimal\[\t \]+breakpoint     keep y.* in [func
> factorial] at .*$::srcfile:$::bp_location7.*" \
> +                   "$::decimal\[\t \]+breakpoint     keep y.* in [func
> main] at .*$::srcfile:$::bp_location1.*" \
> +                   "$::decimal\[\t \]+breakpoint     keep y.* in [func
> main] at .*$::srcfile:$::bp_location1.*" \
> +                   "$::decimal\[\t \]+breakpoint     keep y.* in [func
> main] at .*$::srcfile:$::bp_location2.*" \
> +                   "$::decimal\[\t \]+breakpoint     keep y.* in [func
> multi_line_if_conditional] at .*$::srcfile:$::bp_location3.*" \
> +                   "$::decimal\[\t \]+breakpoint     keep y.* in [func
> multi_line_while_conditional] at .*$::srcfile:$::bp_location4"] \
> +       "breakpoint info"
> +
> +    #
> +    # Test info breakpoint with arguments
> +    #
> +
> +    set see1 0
> +    set see2 0
> +    set see3 0
> +    set see4 0
> +    set see5 0
> +    set see6 0
> +
> +    gdb_test_multiple "info break 2 4 6" "info break 2 4 6" {
> +       -re "1\[\t \]+breakpoint *keep y\[^\r\n\]*:$::main_line\[^\r\n\]*"
> {
> +           set see1 1
> +           exp_continue
> +       }
> +       -re "2\[\t \]+breakpoint *keep y\[^\r\n\]* in [func marker2] at
> \[^\r\n\]*" {
> +           set see2 1
> +           exp_continue
> +       }
> +       -re "3\[\t \]+breakpoint *keep
> y\[^\r\n\]*$::bp_location7\[^\r\n\]*" {
> +           set see3 1
> +           exp_continue
> +       }
> +       -re "4\[\t \]+breakpoint *keep
> y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
> +           set see4 1
> +           exp_continue
> +       }
> +       -re "5\[\t \]+breakpoint *keep
> y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
> +           set see5 1
> +           exp_continue
> +       }
> +       -re "6\[\t \]+breakpoint *keep
> y\[^\r\n\]*$::bp_location2\[^\r\n\]*" {
> +           set see6 1
> +           exp_continue
> +       }
> +       -re ".*$::gdb_prompt $" {
> +           if {!$see1 && $see2 && !$see3 && $see4 && !$see5 && $see6} {
> +               pass "info break 2 4 6"
> +           } else {
> +               fail "info break 2 4 6"
> +           }
> +       }
> +    }
> +
> +    set see1 0
> +    set see2 0
> +    set see3 0
> +    set see4 0
> +    set see5 0
> +    set see6 0
> +
> +    gdb_test_multiple "info break 3-5" "info break 3-5" {
> +       -re "1\[\t \]+breakpoint *keep y.* in [func main] at
> .*:$::main_line\[^\r\n\]*" {
> +           set see1 1
> +           exp_continue
> +       }
> +       -re "2\[\t \]+breakpoint *keep y\[^\r\n\]* in [func marker2] at
> \[^\r\n\]*" {
> +           set see2 1
> +           exp_continue
> +       }
> +       -re "3\[\t \]+breakpoint *keep
> y\[^\r\n\]*$::bp_location7\[^\r\n\]*" {
> +           set see3 1
> +           exp_continue
> +       }
> +       -re "4\[\t \]+breakpoint *keep
> y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
> +           set see4 1
> +           exp_continue
> +       }
> +       -re "5\[\t \]+breakpoint *keep
> y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
> +           set see5 1
> +           exp_continue
> +       }
> +       -re "6\[\t \]+breakpoint *keep
> y\[^\r\n\]*$::bp_location2\[^\r\n\]*" {
> +           set see6 1
> +           exp_continue
> +       }
> +       -re ".*$::gdb_prompt $" {
> +           if {!$see1 && !$see2 && $see3 && $see4 && $see5 && !$see6} {
> +               pass "info break 3-5"
> +           } else {
> +               fail "info break 3-5"
> +           }
> +       }
> +    }
> +
> +    #
> +    # Test disable/enable with arguments
> +    #
> +
> +    # Test with value history
> +
> +    with_test_prefix "with value history" {
> +       gdb_test "print 1"
> +       gdb_test "print 2"
> +       gdb_test "print 3"
> +       gdb_test "print 4"
> +       gdb_test "print 5"
> +       gdb_test "print 6"
> +
> +       # $2 is 2 and $$ is 5
> +       gdb_test_no_output "disable \$2 \$\$" "disable using history
> values"
> +
> +       set see1 0
> +       set see2 0
> +       set see3 0
> +       set see4 0
> +       set see5 0
> +       set see6 0
> +
> +       gdb_test_multiple "info break" "check disable with history values"
> {
> +           -re "1\[\t \]+breakpoint *keep y.* in [func main] at
> .*:$::main_line\[^\r\n\]*" {
> +               set see1 1
> +               exp_continue
> +           }
> +           -re "2\[\t \]+breakpoint *keep n\[^\r\n\]* in [func marker2]
> at \[^\r\n\]*" {
> +               set see2 1
> +               exp_continue
> +           }
> +           -re "3\[\t \]+breakpoint *keep
> y\[^\r\n\]*$::bp_location7\[^\r\n\]*" {
> +               set see3 1
> +               exp_continue
> +           }
> +           -re "4\[\t \]+breakpoint *keep
> y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
> +               set see4 1
> +               exp_continue
> +           }
> +           -re "5\[\t \]+breakpoint *keep
> n\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
> +               set see5 1
> +               exp_continue
> +           }
> +           -re "6\[\t \]+breakpoint *keep
> y\[^\r\n\]*$::bp_location2\[^\r\n\]*" {
> +               set see6 1
> +               exp_continue
> +           }
> +           -re ".*$::gdb_prompt $" {
> +               if {$see1 && $see2 && $see3 && $see4 && $see5 && $see6} {
> +                   pass "check disable with history values"
> +               } else {
> +                   fail "check disable with history values"
> +               }
> +           }
> +       }
> +    }
> +
> +    with_test_prefix "with convenience vars" {
> +       gdb_test "enable"
> +       gdb_test "set \$foo = 3"
> +       gdb_test "set \$bar = 6"
> +       gdb_test_no_output "disable \$foo \$bar" "disable with convenience
> values"
> +
> +       set see1 0
> +       set see2 0
> +       set see3 0
> +       set see4 0
> +       set see5 0
> +       set see6 0
> +
> +       gdb_test_multiple "info break" "check disable with convenience
> values" {
> +           -re "1\[\t \]+breakpoint *keep y.* in [func main] at
> .*:$::main_line\[^\r\n\]*" {
> +               set see1 1
> +               exp_continue
> +           }
> +           -re "2\[\t \]+breakpoint *keep y\[^\r\n\]* in [func marker2]
> at \[^\r\n\]*" {
> +               set see2 1
> +               exp_continue
> +           }
> +           -re "3\[\t \]+breakpoint *keep
> n\[^\r\n\]*$::bp_location7\[^\r\n\]*" {
> +               set see3 1
> +               exp_continue
> +           }
> +           -re "4\[\t \]+breakpoint *keep
> y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
> +               set see4 1
> +               exp_continue
> +           }
> +           -re "5\[\t \]+breakpoint *keep
> y\[^\r\n\]*$::bp_location1\[^\r\n\]*" {
> +               set see5 1
> +               exp_continue
> +           }
> +           -re "6\[\t \]+breakpoint *keep
> n\[^\r\n\]*$::bp_location2\[^\r\n\]*" {
> +               set see6 1
> +               exp_continue
> +           }
> +           -re ".*$::gdb_prompt $" {
> +               if {$see1 && $see2 && $see3 && $see4 && $see5 && $see6} {
> +                   pass "check disable with convenience values"
> +               } else {
> +                   fail "check disable with convenience values"
> +               }
> +           }
> +       }
> +    }
> +
> +    # test with bad values
> +
> +    with_test_prefix "bad values" {
> +       gdb_test "enable"
> +       gdb_test "disable 10" "No breakpoint number 10." \
> +           "disable non-existent breakpoint 10"
> +
> +       gdb_test_no_output "set \$baz = 1.234"
> +       gdb_test "disable \$baz" \
> +           "Convenience variable must have integer value.*" \
> +           "disable with non-integer convenience var"
> +       gdb_test "disable \$grbx" \
> +           "Convenience variable must have integer value.*" \
> +           "disable with non-existent convenience var"
> +       gdb_test "disable \$10" \
> +           "History has not yet reached .10." \
> +           "disable with non-existent history value"
> +       gdb_test "disable \$1foo" \
> +           "Convenience variable must have integer value.*" \
> +           "disable with badly formed history value"
> +    }
> +
> +    # FIXME: The rest of this test doesn't work with anything that can't
> +    # handle arguments.
> +    # Huh? There doesn't *appear* to be anything that passes arguments
> +    # below.
> +
> +    #
> +    # run until the breakpoint at main is hit. For non-stubs-using
> targets.
> +    #
> +    gdb_run_cmd
> +    gdb_test "" \
> +       "Breakpoint \[0-9\]+,.*main .*argc.*argv.* at
> .*$::srcfile:$::bp_location6.*$::bp_location6\[\t \]+\{.*" \
> +       "run until function breakpoint"
> +
> +    # Test the 'list' commands sets current file for the 'break LINENO'
> command.
> +    set bp_marker1 [gdb_get_line_number "set breakpoint 15 here"
> $::srcfile1]
> +    gdb_test "list marker1" ".*"
> +    gdb_test "break $bp_marker1" "Breakpoint \[0-9\]+ at 0x\[0-9a-f\]+:
> file .*$::srcfile1, line ${bp_marker1}\\." \
> +            "break lineno"
> +    gdb_test_no_output {delete $bpnum}
> +
> +    #
> +    # run until the breakpoint at a line number
> +    #
> +    gdb_test continue "Continuing\\..*Breakpoint \[0-9\]+, main
> \\(argc=.*, argv=.*, envp=.*\\) at
> .*$::srcfile:$::bp_location1.*$::bp_location1\[\t \]+printf.*factorial.*" \
> +                           "run until breakpoint set at a line number"
> +
> +    #
> +    # Run until the breakpoint set in a function in a file
> +    #
> +       set bp_location7a [gdb_get_line_number "set breakpoint 7a here"]
> +       send_gdb "del 3\n"
> +       send_gdb "del 7\n"
> +       gdb_test "break $bp_location7a" \
> +       "Breakpoint.*at.* file .*$::srcfile, line $bp_location7a\\." \
> +       "setting breakpoint at 7a"
> +
> +    for {set i 6} {$i >= 1} {incr i -1} {
> +           gdb_test continue "Continuing\\..*Breakpoint \[0-9\]+,
> factorial \\(value=$i\\) at .*$::srcfile:$bp_location7a.*$bp_location7a\[\t
> \]+.*if .value > 1. \{.*" \
> +                           "run until file:function($i) breakpoint"
> +    }
> +
> +    send_gdb "break $::bp_location3\n"
> +
> +       send_gdb "break $::bp_location7\n"
> +
> +    send_gdb "del 2\n"
> +
> +    #
> +    # run until the file:function breakpoint at a line number in a file
> +    #
> +    gdb_test continue "Continuing\\..*Breakpoint \[0-9\]+, main
> \\(argc=.*, argv=.*, envp=.*\\) at
> .*$::srcfile:$::bp_location2.*$::bp_location2\[\t \]+argc = \\(argc ==
> 12345\\);.*" \
> +                   "run until file:linenum breakpoint"
> +
> +    # Test break at offset +1
> +    set bp_location10 [gdb_get_line_number "set breakpoint 10 here"]
> +
> +    gdb_test "break +1" \
> +       "Breakpoint.*at.* file .*$::srcfile, line $bp_location10\\." \
> +       "breakpoint offset +1"
> +
> +    # Check to see if breakpoint is hit when stepped onto
> +
> +    gdb_test "step" \
> +       ".*Breakpoint \[0-9\]+, main \\(argc=.*, argv=.*, envp=.*\\) at
> .*$::srcfile:$bp_location10.*$bp_location10\[\t \]+return argc;.*breakpoint
> 10 here.*" \
> +       "step onto breakpoint"
> +}
> +
> +test_break
> +
> +proc_with_prefix test_tbreak {} {
> +    clean_restart break
> +
> +    # test temporary breakpoint at function
> +    gdb_test "tbreak -q main" "Temporary breakpoint.*at.* file
> .*$::srcfile, line.*" "temporary breakpoint function"
> +
> +    # test break at function in file
> +    gdb_test "tbreak $::srcfile:factorial" "Temporary breakpoint.*at.*
> file .*$::srcfile, line.*" \
> +           "Temporary breakpoint function in file"
> +
> +    # test break at line number
> +    gdb_test "tbreak $::bp_location1" \
> +       "Temporary breakpoint.*at.* file .*$::srcfile, line
> $::bp_location1.*" \
> +       "temporary breakpoint line number #1"
> +
> +    gdb_test "tbreak $::bp_location6" "Temporary breakpoint.*at.* file
> .*$::srcfile, line $::bp_location6.*" "temporary breakpoint line number #2"
> +
> +    # test break at line number in file
> +    gdb_test "tbreak $::srcfile:$::bp_location2" \
> +       "Temporary breakpoint.*at.* file .*$::srcfile, line
> $::bp_location2.*" \
> +       "temporary breakpoint line number in file #1"
> +
> +    gdb_test  "tbreak $::srcfile:$::bp_location11" "Temporary
> breakpoint.*at.* file .*$::srcfile, line $::bp_location11.*" "Temporary
> breakpoint line number in file #2"
> +
> +    # check to see what breakpoints are set (temporary this time)
> +    gdb_test "info break" \
> +       [multi_line "Num     Type.*Disp Enb Address.*What.*" \
> +                   "$::decimal\[\t \]+breakpoint     del.*y.*in [func
> main] at .*$::srcfile:$::main_line.*" \
> +                   "$::decimal\[\t \]+breakpoint     del.*y.*in [func
> factorial] at .*$::srcfile:$::bp_location7.*" \
> +                   "$::decimal\[\t \]+breakpoint     del.*y.*in [func
> main] at .*$::srcfile:$::bp_location1.*" \
> +                   "$::decimal\[\t \]+breakpoint     del.*y.*in [func
> main] at .*$::srcfile:$::bp_location6.*" \
> +                   "$::decimal\[\t \]+breakpoint     del.*y.*in [func
> main] at .*$::srcfile:$::bp_location2.*" \
> +                   "$::decimal\[\t \]+breakpoint     del.*y.*in [func
> main] at .*$::srcfile:$::bp_location11.*"] \
> +       "Temporary breakpoint info"
> +}
> +
> +test_tbreak
> +
> +# Verify that GDB responds gracefully when asked to set a breakpoint
> +# on a nonexistent source line.
> +
> +proc_with_prefix test_break_nonexistent_line {} {
> +    clean_restart break
> +
> +    if {![runto_main]} {
> +       return
> +    }
> +
> +    gdb_test_no_output "set breakpoint pending off"
> +    gdb_test "break 999" \
> +       "No line 999 in the current file." \
> +       "break on non-existent source line"
> +}
> +
> +test_break_nonexistent_line
> +
> +proc_with_prefix test_break_default {} {
> +    clean_restart break
> +
> +    if {![runto_main]} {
> +       return
> +    }
> +
> +    # Run to the desired default location. If not positioned here, the
> +    # tests below don't work.
> +    #
> +    gdb_test "until $::bp_location1" "main .* at .*:$::bp_location1.*" \
> +       "until bp_location1"
> +
> +    # Verify that GDB allows one to just say "break", which is treated
> +    # as the "default" breakpoint.  Note that GDB gets cute when printing
> +    # the informational message about other breakpoints at the same
> +    # location.  We'll hit that bird with this stone too.
> +    #
> +    gdb_test "break" "Breakpoint \[0-9\]*.*" \
> +       "break on default location, 1st time"
> +
> +    gdb_test "break" \
> +       "Note: breakpoint \[0-9\]* also set at .*Breakpoint \[0-9\]*.*" \
> +       "break on default location, 2nd time"
> +
> +    gdb_test "break" \
> +       "Note: breakpoints \[0-9\]* and \[0-9\]* also set at .*Breakpoint
> \[0-9\]*.*" \
> +       "break on default location, 3rd time"
> +
> +    gdb_test "break" \
> +       "Note: breakpoints \[0-9\]*, \[0-9\]* and \[0-9\]* also set at
> .*Breakpoint \[0-9\]*.*" \
> +       "break on default location, 4th time"
> +
> +    # Check setting a breakpoint at the default location with a condition
> attached.
> +    gdb_test "break if (1)" \
> +       "Note: breakpoints \[0-9\]*, \[0-9\]*, \[0-9\]* and \[0-9\]* also
> set at .*Breakpoint \[0-9\]*.*" \
> +       "break on the default location, 5th time, but with a condition"
> +}
> +
> +test_break_default
> +
> +# Verify that a "silent" breakpoint can be set, and that GDB is indeed
> +# "silent" about its triggering.
> +
> +proc_with_prefix test_break_silent_and_more {} {
> +    clean_restart break
> +
> +    if {![runto_main]} {
> +       return
> +    }
> +
> +    gdb_test_multiple "break $::bp_location1" \
> +       "set to-be-silent break bp_location1" {
> +           -re "Breakpoint (\[0-9\]*) at .*, line
> $::bp_location1.*$::gdb_prompt $" {
> +               set bpno $expect_out(1,string)
> +               pass "set to-be-silent break bp_location1"
> +           }
> +       }
> +
> +    gdb_test "commands $bpno\nsilent\nend" ">end" "set silent break
> bp_location1"
> +
> +    gdb_test "info break $bpno" \
> +       "\[0-9\]*\[ \t\]*breakpoint.*:$::bp_location1\r\n\[ \t\]*silent.*"
> \
> +       "info silent break bp_location1"
> +
> +    gdb_test "continue" "Continuing." \
> +       "hit silent break bp_location1"
> +
> +    gdb_test "bt" "#0  main .* at .*:$::bp_location1.*" \
> +       "stopped for silent break bp_location1"
> +
> +    # Verify the $_hit_bpnum convenience variable is set to the silent
> hit bpno.
> +    gdb_test "printf \"%d\\n\", \$_hit_bpnum" "$bpno" \
> +       "Silent breakpoint hit \$_hit_bpnum is silent $bpno"
> +
> +    # Verify that GDB can at least parse a breakpoint with the
> +    # "thread" keyword.  (We won't attempt to test here that a
> +    # thread-specific breakpoint really triggers appropriately.
> +    # The gdb.threads subdirectory contains tests for that.)
> +    #
> +    set bp_location12 [gdb_get_line_number "set breakpoint 12 here"]
> +    gdb_test "break $bp_location12 thread 999" "Unknown thread 999.*" \
> +       "thread-specific breakpoint on non-existent thread disallowed"
> +
> +    gdb_test "break $bp_location12 thread foo" \
> +       "Invalid thread ID: foo" \
> +       "thread-specific breakpoint on bogus thread ID disallowed"
> +
> +    # Verify that GDB responds gracefully to a breakpoint command with
> +    # trailing garbage.
> +    #
> +    gdb_test "break $bp_location12 foo" \
> +       "malformed linespec error: unexpected string, \"foo\".*" \
> +       "breakpoint with trailing garbage disallowed"
> +
> +    # Verify that GDB responds gracefully to a "clear" command that has
> +    # no matching breakpoint.  (First, get us off the current source line,
> +    # which we know has a breakpoint.)
> +    #
> +    gdb_test "next" "marker1.*" "step over breakpoint"
> +
> +    gdb_test "clear 81" "No breakpoint at 81.*" \
> +       "clear line has no breakpoint disallowed"
> +
> +    gdb_test "clear" "No breakpoint at this line.*" \
> +       "clear current line has no breakpoint disallowed"
> +
> +    # Verify that we can set and clear multiple breakpoints.
> +    #
> +    # We don't test that it deletes the correct breakpoints.  We do at
> +    # least test that it deletes more than one breakpoint.
> +    #
> +    gdb_test "break marker3" "Breakpoint.*at.*" "break marker3 #1"
> +    gdb_test "break marker3" "Breakpoint.*at.*" "break marker3 #2"
> +    gdb_test "clear marker3" {Deleted breakpoints [0-9]+ [0-9]+.*}
> +}
> +
> +test_break_silent_and_more
> +
> +# Verify that a breakpoint can be set via a convenience variable.
> +
> +proc_with_prefix test_break_line_convenience_var {} {
> +    clean_restart break
> +
> +    if { ![runto_main] } {
> +       return
> +    }
> +
> +    gdb_test_no_output "set \$foo=$::bp_location11" \
> +       "set convenience variable \$foo to bp_location11"
> +
> +    gdb_test "break \$foo" \
> +       "Breakpoint (\[0-9\]*) at .*, line $::bp_location11.*"
> +
> +    # Verify that GDB responds gracefully to an attempt to set a
> +    # breakpoint via a convenience variable whose type is not integer.
> +
> +    gdb_test_no_output "set \$foo=81.5" \
> +       "set convenience variable \$foo to 81.5"
> +
> +    gdb_test "break \$foo" \
> +       "Convenience variables used in line specs must have integer
> values.*" \
> +       "non-integer convenience variable disallowed"
> +}
> +
> +test_break_line_convenience_var
> +
> +# Verify that we can set and trigger a breakpoint in a user-called
> function.
> +
> +proc_with_prefix test_break_user_call {} {
> +    clean_restart break
> +
> +    if { ![runto_main] } {
> +       return
> +    }
> +
> +    gdb_test "break marker2" \
> +       "Breakpoint (\[0-9\]*) at .*, line $::bp_location8.*" \
> +       "set breakpoint on to-be-called function"
> +
> +    gdb_test "print marker2(99)" \
> +       "The program being debugged stopped while in a function called
> from GDB.\r\nEvaluation of the expression containing the function\r\n.[func
> marker2]. will be abandoned.\r\nWhen the function is done executing, GDB
> will silently stop.*" \
> +       "hit breakpoint on called function"
> +
> +    # As long as we're stopped (breakpointed) in a called function,
> +    # verify that we can successfully backtrace & such from here.
> +    gdb_test "bt" \
> +       "#0\[ \t\]*($::hex in )?marker2.*:$::bp_location8\r\n#1\[
> \t\]*<function called from gdb>.*" \
> +       "backtrace while in called function"
> +
> +    # Return from the called function.  For remote targets, it's
> important to do
> +    # this before runto_main, which otherwise may silently stop on the
> dummy
> +    # breakpoint inserted by GDB at the program's entry point.
> +    #
> +    gdb_test_multiple "finish" "finish from called function" {
> +       -re "Run till exit from .*marker2.* at
> .*$::bp_location8\r\n.*function called from gdb.*$::gdb_prompt $" {
> +           pass "finish from called function"
> +       }
> +       -re "Run till exit from .*marker2.* at
> .*$::bp_location8\r\n.*Value returned.*$::gdb_prompt $" {
> +           pass "finish from called function"
> +       }
> +    }
> +}
> +
> +test_break_user_call
> +
> +# Verify that GDB responds gracefully to a "finish" command with
> +# arguments.
> +
> +proc_with_prefix test_finish_arguments {} {
> +    clean_restart break
> +
> +    if {![runto_main]} {
> +       return
> +    }
> +
> +    send_gdb "finish 123\n"
> +    gdb_expect {
> +      -re "The \"finish\" command does not take any
> arguments.\r\n$::gdb_prompt $"\
> +             {pass "finish with arguments disallowed"}
> +      -re "$::gdb_prompt $"\
> +             {fail "finish with arguments disallowed"}
> +      timeout {fail "(timeout) finish with arguments disallowed"}
> +    }
> +
> +    # Verify that GDB responds gracefully to a request to "finish" from
> +    # the outermost frame.  On a stub that never exits, this will just
> +    # run to the stubs routine, so we don't get this error...  Thus the
> +    # second condition.
> +    #
> +
> +    gdb_test_multiple "finish" "finish from outermost frame disallowed" {
> +       -re "\"finish\" not meaningful in the outermost
> frame.\r\n$::gdb_prompt $" {
> +           pass "finish from outermost frame disallowed"
> +       }
> +       -re "Run till exit from.*\r\n$::gdb_prompt $" {
> +           pass "finish from outermost frame disallowed"
> +       }
> +    }
> +}
> +
> +test_finish_arguments
> +
> +#********
> +
> +
> +#
> +# Test "next" over recursive function call.
> +#
> +
> +proc_with_prefix test_next_with_recursion {} {
> +    global gdb_prompt
> +    global decimal
> +    global binfile
> +
> +    gdb_test "kill" "" "kill program" "Kill the program being debugged.*y
> or n. $" "y"
> +    delete_breakpoints
> +
> +       set bp_location7a [gdb_get_line_number "set breakpoint 7a here"]
> +       gdb_test "break $bp_location7a" \
> +       "Breakpoint.*at.* file .*$::srcfile, line $bp_location7a\\." \
> +       "setting breakpoint at 7a"
> +
> +    # Run until we call factorial with 6
> +
> +    gdb_run_cmd
> +    gdb_test "" "Break.* factorial .value=6. .*" "run to factorial(6)"
> +
> +    # Continue until we call factorial recursively with 5.
> +
> +    gdb_test "continue" \
> +       "Continuing.*Break.* factorial .value=5. .*" \
> +       "continue to factorial(5)"
> +
> +    # Do a backtrace just to confirm how many levels deep we are.
> +
> +    gdb_test "backtrace" \
> +       "#0\[ \t\]+ factorial .value=5..*" \
> +       "backtrace from factorial(5)"
> +
> +    # Now a "next" should position us at the recursive call, which
> +    # we will be performing with 4.
> +
> +    gdb_test "next" \
> +       ".* factorial .value - 1.;.*" \
> +       "next to recursive call"
> +
> +    # Disable the breakpoint at the entry to factorial by deleting them
> all.
> +    # The "next" should run until we return to the next line from this
> +    # recursive call to factorial with 4.
> +    # Buggy versions of gdb will stop instead at the innermost frame on
> +    # the line where we are trying to "next" to.
> +
> +    delete_breakpoints
> +
> +    if [istarget "mips*tx39-*"] {
> +       set timeout 60
> +    }
> +    # We used to set timeout here for all other targets as well.  This
> +    # is almost certainly wrong.  The proper timeout depends on the
> +    # target system in use, and how we communicate with it, so there
> +    # is no single value appropriate for all targets.  The timeout
> +    # should be established by the Dejagnu config file(s) for the
> +    # board, and respected by the test suite.
> +    #
> +    # For example, if I'm running GDB over an SSH tunnel talking to a
> +    # portmaster in California talking to an ancient 68k board running
> +    # a crummy ROM monitor (a situation I can only wish were
> +    # hypothetical), then I need a large timeout.  But that's not the
> +    # kind of knowledge that belongs in this file.
> +
> +    gdb_test next "\[0-9\]*\[\t \]+return \\(value\\);.*" \
> +           "next over recursive call"
> +
> +    # OK, we should be back in the same stack frame we started from.
> +    # Do a backtrace just to confirm.
> +
> +    gdb_test "backtrace" \
> +           "#0\[ \t\]+ factorial .value=120.*\r\n#1\[ \t\]+ \[0-9a-fx\]+
> in factorial .value=6..*" \
> +           "backtrace from factorial(5.1)"
> +
> +    if { ![target_info exists gdb,noresults] } {
> +       gdb_continue_to_end "recursive next test"
> +    }
> +}
> +
> +test_next_with_recursion
> +
> +
> +#********
> +
> +# build a new file with optimization enabled so that we can try
> breakpoints
> +# on targets with optimized prologues
> +
> +if { [build_executable "failed to prepare" "breako2" {break.c break1.c}
> {debug nowarnings optimize=-O2}] } {
> +    return -1
> +}
> +
> +proc_with_prefix test_break_optimized_prologue {} {
> +    clean_restart breako2
> +
> +    # test break at function
> +    gdb_test "break -q main" \
> +       "Breakpoint.*at.* file .*, line.*" \
> +       "breakpoint function, optimized file"
> +
> +    # test break at function
> +    gdb_test "break marker4" \
> +       "Breakpoint.*at.* file .*$::srcfile1, line.*" \
> +       "breakpoint small function, optimized file"
> +
> +    # run until the breakpoint at main is hit. For non-stubs-using
> targets.
> +    gdb_run_cmd
> +
> +    set test "run until function breakpoint, optimized file"
> +    gdb_test_multiple "" $test {
> +       -re "Breakpoint \[0-9\]+,.*main .*argc.*argv.* at
> .*$::srcfile:$::bp_location6.*$::bp_location6\[\t \]+\{ if .argc.*
> \{.*$::gdb_prompt $" {
> +           pass $test
> +       }
> +       -re "Breakpoint \[0-9\]+,.*main .*argc.*argv.* at .*$::gdb_prompt
> $" {
> +           pass "$test (code motion)"
> +       }
> +    }
> +
> +    # run until the breakpoint at a small function
> +    #
> +    # Add a second pass pattern.  The behavior differs here between stabs
> +    # and dwarf for one-line functions.  Stabs preserves two line symbols
> +    # (one before the prologue and one after) with the same line number,
> +    # but dwarf regards these as duplicates and discards one of them.
> +    # Therefore the address after the prologue (where the breakpoint is)
> +    # has no exactly matching line symbol, and GDB reports the breakpoint
> +    # as if it were in the middle of a line rather than at the beginning.
> +
> +    set bp_location14 [gdb_get_line_number "set breakpoint 14 here"
> $::srcfile1]
> +
> +    gdb_test_multiple "continue" \
> +       "run until breakpoint set at small function, optimized file" {
> +           -re "Breakpoint $::decimal, marker4 \\(d=(d@entry=)?177601976\\)
> at .*$::srcfile1:$bp_location14\[\r\n\]+$bp_location14\[\t \]+void
> marker4.*" {
> +               pass "run until breakpoint set at small function,
> optimized file (line bp_location14)"
> +           }
> +           -re "Breakpoint $::decimal, factorial \\(.*\\)
> .*\{\r\n$::gdb_prompt" {
> +               # GCC 4.3 emits bad line number information - see
> gcc/36748.
> +               if { [test_compiler_info "gcc-4-3-*"] } {
> +                   setup_xfail *-*-*
> +               }
> +               fail "run until breakpoint set at small function,
> optimized file"
> +           }
> +       }
> +}
> +
> +test_break_optimized_prologue
> +
> +# test that 'rbreak' on a symbol that may be from a shared library doesn't
> +# cause a "Junk at end of arguments." error.
> +#
> +# On x86 GNU/Linux, this test will choke on e.g. __libc_start_main@plt.
> +#
> +# Note that this test won't necessarily choke on all targets even if
> +# all the rbreak issue is present.  rbreak needs to match and set a
> +# breakpoint on a symbol causes 'break' to choke.
> +
> +proc_with_prefix test_rbreak_shlib {} {
> +    clean_restart breako2
> +
> +    gdb_test_no_output "set breakpoint pending on" "rbreak junk pending
> setup"
> +
> +    # We expect at least one breakpoint to be set when we "rbreak main".
> +    gdb_test "rbreak main" \
> +       ".*Breakpoint.*at.* file .*$::srcfile, line.*"
> +
> +    # Run to a breakpoint.  Fail if we see "Junk at end of arguments".
> +    gdb_run_cmd
> +
> +    gdb_test_multiple "" "rbreak junk" {
> +       -re -wrap "Junk at end of arguments.*" {
> +           fail $gdb_test_name
> +       }
> +       -re -wrap ".*Breakpoint \[0-9\]+,.*" {
> +           pass $gdb_test_name
> +       }
> +    }
> +}
> +
> +test_rbreak_shlib
> +
> +# Test break via convenience variable with file name
> +
> +proc_with_prefix test_break_file_line_convenience_var {} {
> +    clean_restart breako2
> +
> +    set line [gdb_get_line_number "set breakpoint 1 here"]
> +    gdb_test_no_output "set \$l = $line"
> +
> +    set line_actual "-1"
> +    set test "break $::srcfile:\$l"
> +    gdb_test_multiple "$test" $test {
> +       -re "Breakpoint $::decimal at $::hex: file .*break\\.c, line
> ($::decimal)\\.\r\n$::gdb_prompt $" {
> +           # Save the actual line number on which the breakpoint was
> +           # actually set. On some systems (Eg: Ubuntu 16.04 with GCC
> +           # version 5.4.0), that line gets completely inlined, including
> +           # the call to printf, and so we end up inserting the breakpoint
> +           # on one of the following lines instead.
> +           set line_actual $expect_out(1,string)
> +           pass $test
> +       }
> +    }
> +
> +    gdb_test_no_output "set \$foo=81.5" \
> +       "set convenience variable \$foo to 81.5"
> +    gdb_test "break $::srcfile:\$foo" \
> +       "Convenience variables used in line specs must have integer
> values.*" \
> +       "non-integer convenience variable disallowed"
> +}
> +
> +test_break_file_line_convenience_var
> +
> +# Test that commands can be cleared without error.
> +
> +proc_with_prefix test_break_commands_clear {} {
> +    clean_restart breako2
> +
> +    set line [gdb_get_line_number "set breakpoint 1 here"]
> +    gdb_breakpoint $line
> +
> +    gdb_test "commands\nprint 232323\nend" ">end" "set some breakpoint
> commands"
> +    gdb_test "commands\nend" ">end" "clear breakpoint commands"
> +
> +    # We verify that the commands were cleared by ensuring that the last
> +    # breakpoint's location ends the output -- if there were commands,
> +    # they would have been printed after the location.
> +    gdb_test "info break" "$::srcfile:$::decimal" "verify that they were
> cleared"
> +}
> +
> +test_break_commands_clear
> diff --git a/gdb/testsuite/gdb.mdebug/break1.c
> b/gdb/testsuite/gdb.mdebug/break1.c
> new file mode 100644
> index 00000000000..5382ec06fa9
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mdebug/break1.c
> @@ -0,0 +1,59 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 1992-2023 Free Software Foundation, Inc.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.
> */
> +
> +/* The code for this file was extracted from the gdb testsuite
> +   testcase "break.c". */
> +
> +/* A structure we use for field name completion tests.  */
> +struct some_struct
> +{
> +  int a_field;
> +  int b_field;
> +  union { int z_field; };
> +};
> +
> +struct some_struct values[50];
> +
> +/* Some definitions for tag completion.  */
> +enum some_enum { VALUE };
> +
> +enum some_enum some_enum_global;
> +
> +union some_union
> +{
> +  int f1;
> +  double f2;
> +};
> +
> +union some_union some_union_global;
> +
> +/* A variable with a name "similar" to the above struct, to test that
> +   tag completion works ok.  */
> +int some_variable;
> +
> +/* The following functions do nothing useful.  They are included
> +   simply as places to try setting breakpoints at.  They are
> +   explicitly "one-line functions" to verify that this case works
> +   (some versions of gcc have or have had problems with this).
> +
> +   These functions are in a separate source file to prevent an
> +   optimizing compiler from inlining them and optimizing them away. */
> +
> +int marker1 (void) { return (0); }     /* set breakpoint 15 here */
> +int marker2 (int a) { return (1); }    /* set breakpoint 8 here */
> +void marker3 (char *a, char *b) {}     /* set breakpoint 17 here */
> +void marker4 (long d) { values[0].a_field = d; }       /* set breakpoint
> 14 here */
> diff --git a/gdb/testsuite/gdb.mdebug/info-types-c.exp
> b/gdb/testsuite/gdb.mdebug/info-types-c.exp
> new file mode 100644
> index 00000000000..d8c3abeade0
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mdebug/info-types-c.exp
> @@ -0,0 +1,67 @@
> +# Copyright 2019-2023 Free Software Foundation, Inc.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +require {info exists MDEBUG}
> +
> +global testfile
> +global srcfile
> +global binfile
> +global subdir
> +global srcdir
> +global compile_flags
> +
> +standard_testfile info-types.c
> +
> +if {[prepare_for_testing "failed to prepare" \
> +     "${testfile}" $srcfile "debug c"]} {
> +return -1
> +}
> +
> +gdb_test_no_output "set auto-solib-add off"
> +
> +if ![runto_main] then {
> +return 0
> +}
> +
> +set file_re "File .*[string_to_regexp $srcfile]:"\
> +
> +set output_lines \
> +    [list \
> +        "^All defined types:" \
> +        ".*" \
> +        $file_re \
> +        "\[\t \]+typedef enum {\\.\\.\\.} anon_enum_t;" \
> +        "\[\t \]+typedef struct {\\.\\.\\.} anon_struct_t;" \
> +        "\[\t \]+typedef union {\\.\\.\\.} anon_union_t;" \
> +        "\[\t \]+typedef struct baz_t baz;" \
> +        "\[\t \]+typedef struct baz_t \\* baz_ptr;" \
> +        "\[\t \]+typedef struct baz_t baz_t;" \
> +        "\[\t \]+enum enum_t;" \
> +        "\[\t \]+typedef enum enum_t my_enum_t;" \
> +        "\[\t \]+typedef float my_float_t;" \
> +        "\[\t \]+typedef int my_int_t;" \
> +        "\[\t \]+typedef enum {\\.\\.\\.} nested_anon_enum_t;" \
> +        "\[\t \]+typedef struct {\\.\\.\\.} nested_anon_struct_t;" \
> +        "\[\t \]+typedef union {\\.\\.\\.} nested_anon_union_t;" \
> +        "\[\t \]+typedef struct baz_t nested_baz;" \
> +        "\[\t \]+typedef struct baz_t nested_baz_t;" \
> +        "\[\t \]+typedef enum enum_t nested_enum_t;" \
> +        "\[\t \]+typedef float nested_float_t;" \
> +        "\[\t \]+typedef int nested_int_t;" \
> +        "\[\t \]+typedef union union_t nested_union_t;" \
> +        "\[\t \]+union union_t;" \
> +        "($|\r\n.*)"]
> +
> +gdb_test_lines "info types" "" [multi_line {*}$output_lines]
> diff --git a/gdb/testsuite/gdb.mdebug/info-types.c
> b/gdb/testsuite/gdb.mdebug/info-types.c
> new file mode 100644
> index 00000000000..15a74b3016a
> --- /dev/null
> +++ b/gdb/testsuite/gdb.mdebug/info-types.c
> @@ -0,0 +1,116 @@
> +/* Copyright 2019-2023 Free Software Foundation, Inc.
> +
> +   This program is free software; you can redistribute it and/or modify
> +   it under the terms of the GNU General Public License as published by
> +   the Free Software Foundation; either version 3 of the License, or
> +   (at your option) any later version.
> +
> +   This program is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +   GNU General Public License for more details.
> +
> +   You should have received a copy of the GNU General Public License
> +   along with this program.  If not, see <http://www.gnu.org/licenses/>.
> */
> +
> +typedef int my_int_t;
> +typedef float my_float_t;
> +typedef my_int_t nested_int_t;
> +typedef my_float_t nested_float_t;
> +
> +struct baz_t
> +{
> +  float f;
> +  double d;
> +};
> +
> +typedef struct baz_t baz_t;
> +typedef struct baz_t baz;
> +typedef baz_t nested_baz_t;
> +typedef baz nested_baz;
> +typedef struct baz_t *baz_ptr;
> +
> +enum enum_t
> +{
> + AA, BB, CC
> +};
> +
> +typedef enum enum_t my_enum_t;
> +typedef my_enum_t nested_enum_t;
> +
> +typedef struct
> +{
> +  double d;
> +  float f;
> +} anon_struct_t;
> +
> +typedef anon_struct_t nested_anon_struct_t;
> +
> +typedef enum
> +{
> + DD, EE, FF
> +} anon_enum_t;
> +
> +typedef anon_enum_t nested_anon_enum_t;
> +
> +union union_t
> +{
> +  int i;
> +  float f;
> +};
> +
> +typedef union union_t nested_union_t;
> +
> +typedef union
> +{
> +  int i;
> +  double d;
> +} anon_union_t;
> +
> +typedef anon_union_t nested_anon_union_t;
> +
> +volatile int var_a;
> +volatile float var_b;
> +volatile my_int_t var_c;
> +volatile my_float_t var_d;
> +volatile nested_int_t var_e;
> +volatile nested_float_t var_f;
> +volatile struct baz_t var_g;
> +volatile baz_t var_h;
> +volatile baz var_i;
> +volatile nested_baz_t var_j;
> +volatile nested_baz var_k;
> +volatile baz_ptr var_l;
> +volatile enum enum_t var_m;
> +volatile my_enum_t var_n;
> +volatile nested_enum_t var_o;
> +volatile anon_struct_t var_p;
> +volatile nested_anon_struct_t var_q;
> +volatile anon_enum_t var_r;
> +volatile nested_anon_enum_t var_s;
> +volatile union union_t var_t;
> +volatile nested_union_t var_u;
> +volatile anon_union_t var_v;
> +volatile nested_anon_union_t var_w;
> +
> +#ifdef __cplusplus
> +
> +class CL
> +{
> +  int a;
> +};
> +
> +typedef CL my_cl;
> +typedef CL *my_ptr;
> +
> +volatile CL var_cpp_a;
> +volatile my_cl var_cpp_b;
> +volatile my_ptr var_cpp_c;
> +
> +#endif /* __cplusplus */
> +
> +int
> +main ()
> +{
> +  return 0;
> +}
> --
> 2.41.0
>
>

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2024-03-13  2:09 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-10-22  8:13 [RFC][PATCH?] fixed some segfaults and bugs in mdebug support Zeck S
2023-10-23  9:40 ` Andrew Burgess
2023-10-24  0:25   ` Zeck S
2023-11-11  3:07     ` Zeck S
2023-12-04  3:36       ` Zeck S
2023-12-11 11:42         ` Zeck S
2023-12-11 14:03           ` Andrew Burgess
2023-12-11 14:48             ` Zeck S
2023-12-15 19:26               ` Tom Tromey
2023-12-18 15:50               ` Andrew Burgess
2023-12-25  5:42                 ` Zeck S
2024-02-07 13:33                   ` [PATCH] mdebug fix Zeck S
2024-02-16  2:45                   ` [RFC][PATCH?] fixed some segfaults and bugs in mdebug support Zeck S
2024-03-13  2:09                     ` Zeck S
2023-12-15 19:27             ` Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).