diff mbox

Fission support for multiple CUs per DWO file

Message ID CAENS6Ev+Wsfc7Y3fWxwgCns5y3eaT8TLvjyy=0_0R3wgD8Z4Ag@mail.gmail.com
State New
Headers show

Commit Message

David Blaikie June 1, 2017, 11:33 p.m. UTC
gdb/ChangeLog:

2017-05-12  David Blaikie  <dblaikie@gmail.com>

* dwarf2read.c (struct dwo_file): Use a htab of dwo_unit* (rather than
a singular dwo_unit*) to support multiple CUs in the same way that
multiple TUs are supported.
(static void create_cus_hash_table): Replace create_dwo_cu with a
function for parsing multiple CUs from a DWO file.
(open_and_init_dwo_file): Use create_cus_hash_table rather than create_dwo_cu.
(lookup_dwo_cutu): Lookup CU in the hash table in the dwo_file with
htab_find, rather than comparing the signature to a singleton CU in
the dwo_file.

gdb/testsuite/ChangeLog:

2017-05-12  David Blaikie  <dblaikie@gmail.com>

* gdb.dwarf2/fission-multi-cu.S: Test containing multiple CUs in a
DWO, built from fission-multi-cu{1,2}.c.
* gdb.dwarf2/fission-multi-cu.exp: Test similar to fission-base.exp,
except putting 'main' and 'func' in separate CUs while in the same DWO
file.
* gdb.dwarf2/fission-multi-cu1.c: First CU for the multi-CU-single-DWO test.
* gdb.dwarf2/fission-multi-cu2.c: Second CU for the multi-CU-single-DWO test.

On Mon, May 22, 2017 at 11:01 AM, Doug Evans <dje@google.com> wrote:
> David Blaikie writes:
>  > ...
>
> Hi. Review comments inline.

Hi - thanks for the review!

(responses inline, patch addressing the issues is attached)

Do let me know if there's other things that'd be good to address or if
this looks good as-is.

Thanks,
- Dave

> All nits.
>
> First one: Tab instead of spaces throughout (including ChangeLog entries).

Think I got all that addressed - though GMail seems to be getting in
my way of adding tabs into the ChangeLog entries inline in the email.
I'll be sure they're there when I commit it.

>
> The testcase is fine with me.
>
>  > gdb/ChangeLog:
>  >
>  > 2017-05-12  David Blaikie  <dblaikie@gmail.com>
>  >
>  >         * dwarf2read.c (struct dwo_file): Use a htab of dwo_unit*
>  > (rather than a singular dwo_unit*) to support multiple CUs in the same
>  > way that multiple TUs are supported.
>  >         (static void create_cus_hash_table): Replace create_dwo_cu
>  > with a function for parsing multiple CUs from a DWO file.
>  >         (open_and_init_dwo_file): Use create_cus_hash_table rather
>  > than create_dwo_cu.
>  >         (lookup_dwo_cutu): Lookup CU in the hash table in the dwo_file
>  > with htab_find, rather than comparing the signature to a singleton CU
>  > in the dwo_file.
>  >
>  > gdb/testsuite/ChangeLog:
>  >
>  > 2017-05-12  David Blaikie  <dblaikie@gmail.com>
>  >
>  >         * gdb.dwarf2/fission-multi-cu.S: Test containing multiple CUs
>  > in a DWO, built from fission-multi-cu{1,2}.c.
>  >         * gdb.dwarf2/fission-multi-cu.exp: Test similar to
>  > fission-base.exp, except putting 'main' and 'func' in separate CUs
>  > while in the same DWO file.
>  >         * gdb.dwarf2/fission-multi-cu1.c: First CU for the
>  > multi-CU-single-DWO test.
>  >         * gdb.dwarf2/fission-multi-cu2.c: Second CU for the
>  > multi-CU-single-DWO test.
>  > diff --git gdb/dwarf2read.c gdb/dwarf2read.c
>  > index b58d0fc16e..29eb5a14b2 100644
>  > --- gdb/dwarf2read.c
>  > +++ gdb/dwarf2read.c
>  > @@ -852,12 +852,9 @@ struct dwo_file
>  >       sections (for lack of a better name).  */
>  >    struct dwo_sections sections;
>  >
>  > -  /* The CU in the file.
>  > -     We only support one because having more than one requires hacking the
>  > -     dwo_name of each to match, which is highly unlikely to happen.
>  > -     Doing this means all TUs can share comp_dir: We also assume that
>  > -     DW_AT_comp_dir across all TUs in a DWO file will be identical.  */
>  > -  struct dwo_unit *cu;
>  > +  /* The CUs in the file.
>  > +     Each element is a struct dwo_unit. */
>
> Since this is currently non-standard, I think it will help some readers
> to elaborate on the Why of things here. IOW, add a comment explaining why
> we're now supporting multi-CUs in one DWO file.

Commented about the use case here & that it's supported as an extension.

>
>  > +  htab_t cus;
>  >
>  >    /* Table of TUs in the file.
>  >       Each element is a struct dwo_unit.  */
>  > @@ -9702,72 +9699,75 @@ create_dwo_cu_reader (const struct die_reader_specs *reader,
>  >                      hex_string (dwo_unit->signature));
>  >  }
>  >
>  > -/* Create the dwo_unit for the lone CU in DWO_FILE.
>  > -   Note: This function processes DWO files only, not DWP files.  */
>
> Need to keep the function comment (just reword it).
> And please keep the note about only being used for DWO files, not DWP files.

Kept & reworded.

>
>  > -
>  > -static struct dwo_unit *
>  > -create_dwo_cu (struct dwo_file *dwo_file)
>  > +static void create_cus_hash_table (struct dwo_file &dwo_file,
>  > +                                   dwarf2_section_info &section,
>  > +                                   htab_t &cus_htab)
>  >  {
>  >    struct objfile *objfile = dwarf2_per_objfile->objfile;
>  > -  struct dwarf2_section_info *section = &dwo_file->sections.info;
>  > +  const struct dwarf2_section_info *abbrev_section = &dwo_file.sections.abbrev;
>  >    const gdb_byte *info_ptr, *end_ptr;
>  > -  struct create_dwo_cu_data create_dwo_cu_data;
>  > -  struct dwo_unit *dwo_unit;
>  >
>  > -  dwarf2_read_section (objfile, section);
>  > -  info_ptr = section->buffer;
>  > +  dwarf2_read_section (objfile, &section);
>  > +  info_ptr = section.buffer;
>  >
>  >    if (info_ptr == NULL)
>  > -    return NULL;
>  > +    return;
>  >
>  >    if (dwarf_read_debug)
>  >      {
>  >        fprintf_unfiltered (gdb_stdlog, "Reading %s for %s:\n",
>  > -                      get_section_name (section),
>  > -                      get_section_file_name (section));
>  > +                      get_section_name (&section),
>  > +                      get_section_file_name (&section));
>  >      }
>  >
>  > -  create_dwo_cu_data.dwo_file = dwo_file;
>  > -  dwo_unit = NULL;
>  > -
>  > -  end_ptr = info_ptr + section->size;
>  > +  end_ptr = info_ptr  + section.size;
>
> extra space

Removed.

>
>  >    while (info_ptr < end_ptr)
>  >      {
>  >        struct dwarf2_per_cu_data per_cu;
>  > +      struct create_dwo_cu_data create_dwo_cu_data;
>  > +      struct dwo_unit *dwo_unit;
>  > +      void **slot;
>  > +      sect_offset sect_off = (sect_offset) (info_ptr - section.buffer);
>  >
>  >        memset (&create_dwo_cu_data.dwo_unit, 0,
>  >            sizeof (create_dwo_cu_data.dwo_unit));
>  >        memset (&per_cu, 0, sizeof (per_cu));
>  >        per_cu.objfile = objfile;
>  >        per_cu.is_debug_types = 0;
>  > -      per_cu.sect_off = sect_offset (info_ptr - section->buffer);
>  > -      per_cu.section = section;
>  > +      per_cu.sect_off = sect_offset (info_ptr - section.buffer);
>  > +      per_cu.section = &section;
>  > +      create_dwo_cu_data.dwo_file = &dwo_file;
>  >
>  > -      init_cutu_and_read_dies_no_follow (&per_cu, dwo_file,
>  > +      init_cutu_and_read_dies_no_follow (&per_cu, &dwo_file,
>  >                                       create_dwo_cu_reader,
>  >                                       &create_dwo_cu_data);
>  > +      info_ptr += per_cu.length;
>  >
>  > -      if (create_dwo_cu_data.dwo_unit.dwo_file != NULL)
>  > -    {
>  > -      /* If we've already found one, complain.  We only support one
>  > -         because having more than one requires hacking the dwo_name of
>  > -         each to match, which is highly unlikely to happen.  */
>  > -      if (dwo_unit != NULL)
>  > -        {
>  > -          complaint (&symfile_complaints,
>  > -                     _("Multiple CUs in DWO file %s [in module %s]"),
>  > -                     dwo_file->dwo_name, objfile_name (objfile));
>  > -          break;
>  > -        }
>
> Add a comment explaining why this test is present:
>
>  > +      if (create_dwo_cu_data.dwo_unit.dwo_file == NULL)
>  > +        continue;

Added a comment & fixed a mistake I introduced by initializing
dwo_file before this call rather than letting the call initialize it.
Looks like the original code was using this for error detection & I
attempted to keep that the same but broke it by initializing the value
early.

>  >
>  > -      dwo_unit = OBSTACK_ZALLOC (&objfile->objfile_obstack, struct dwo_unit);
>  > -      *dwo_unit = create_dwo_cu_data.dwo_unit;
>  > -    }
>  > +      if (cus_htab == NULL)
>
> Remove the surrounding braces.
>
>  > +        {
>  > +          cus_htab = allocate_dwo_unit_table (objfile);
>  > +        }

Removed & reduced indentation.

Comments

David Blaikie June 27, 2017, 6:52 p.m. UTC | #1
Ping (no rush, but trying not to forget it myself (: )

On Thu, Jun 1, 2017 at 4:33 PM, David Blaikie <dblaikie@gmail.com> wrote:
> gdb/ChangeLog:
>
> 2017-05-12  David Blaikie  <dblaikie@gmail.com>
>
> * dwarf2read.c (struct dwo_file): Use a htab of dwo_unit* (rather than
> a singular dwo_unit*) to support multiple CUs in the same way that
> multiple TUs are supported.
> (static void create_cus_hash_table): Replace create_dwo_cu with a
> function for parsing multiple CUs from a DWO file.
> (open_and_init_dwo_file): Use create_cus_hash_table rather than create_dwo_cu.
> (lookup_dwo_cutu): Lookup CU in the hash table in the dwo_file with
> htab_find, rather than comparing the signature to a singleton CU in
> the dwo_file.
>
> gdb/testsuite/ChangeLog:
>
> 2017-05-12  David Blaikie  <dblaikie@gmail.com>
>
> * gdb.dwarf2/fission-multi-cu.S: Test containing multiple CUs in a
> DWO, built from fission-multi-cu{1,2}.c.
> * gdb.dwarf2/fission-multi-cu.exp: Test similar to fission-base.exp,
> except putting 'main' and 'func' in separate CUs while in the same DWO
> file.
> * gdb.dwarf2/fission-multi-cu1.c: First CU for the multi-CU-single-DWO test.
> * gdb.dwarf2/fission-multi-cu2.c: Second CU for the multi-CU-single-DWO test.
>
> On Mon, May 22, 2017 at 11:01 AM, Doug Evans <dje@google.com> wrote:
>> David Blaikie writes:
>>  > ...
>>
>> Hi. Review comments inline.
>
> Hi - thanks for the review!
>
> (responses inline, patch addressing the issues is attached)
>
> Do let me know if there's other things that'd be good to address or if
> this looks good as-is.
>
> Thanks,
> - Dave
>
>> All nits.
>>
>> First one: Tab instead of spaces throughout (including ChangeLog entries).
>
> Think I got all that addressed - though GMail seems to be getting in
> my way of adding tabs into the ChangeLog entries inline in the email.
> I'll be sure they're there when I commit it.
>
>>
>> The testcase is fine with me.
>>
>>  > gdb/ChangeLog:
>>  >
>>  > 2017-05-12  David Blaikie  <dblaikie@gmail.com>
>>  >
>>  >         * dwarf2read.c (struct dwo_file): Use a htab of dwo_unit*
>>  > (rather than a singular dwo_unit*) to support multiple CUs in the same
>>  > way that multiple TUs are supported.
>>  >         (static void create_cus_hash_table): Replace create_dwo_cu
>>  > with a function for parsing multiple CUs from a DWO file.
>>  >         (open_and_init_dwo_file): Use create_cus_hash_table rather
>>  > than create_dwo_cu.
>>  >         (lookup_dwo_cutu): Lookup CU in the hash table in the dwo_file
>>  > with htab_find, rather than comparing the signature to a singleton CU
>>  > in the dwo_file.
>>  >
>>  > gdb/testsuite/ChangeLog:
>>  >
>>  > 2017-05-12  David Blaikie  <dblaikie@gmail.com>
>>  >
>>  >         * gdb.dwarf2/fission-multi-cu.S: Test containing multiple CUs
>>  > in a DWO, built from fission-multi-cu{1,2}.c.
>>  >         * gdb.dwarf2/fission-multi-cu.exp: Test similar to
>>  > fission-base.exp, except putting 'main' and 'func' in separate CUs
>>  > while in the same DWO file.
>>  >         * gdb.dwarf2/fission-multi-cu1.c: First CU for the
>>  > multi-CU-single-DWO test.
>>  >         * gdb.dwarf2/fission-multi-cu2.c: Second CU for the
>>  > multi-CU-single-DWO test.
>>  > diff --git gdb/dwarf2read.c gdb/dwarf2read.c
>>  > index b58d0fc16e..29eb5a14b2 100644
>>  > --- gdb/dwarf2read.c
>>  > +++ gdb/dwarf2read.c
>>  > @@ -852,12 +852,9 @@ struct dwo_file
>>  >       sections (for lack of a better name).  */
>>  >    struct dwo_sections sections;
>>  >
>>  > -  /* The CU in the file.
>>  > -     We only support one because having more than one requires hacking the
>>  > -     dwo_name of each to match, which is highly unlikely to happen.
>>  > -     Doing this means all TUs can share comp_dir: We also assume that
>>  > -     DW_AT_comp_dir across all TUs in a DWO file will be identical.  */
>>  > -  struct dwo_unit *cu;
>>  > +  /* The CUs in the file.
>>  > +     Each element is a struct dwo_unit. */
>>
>> Since this is currently non-standard, I think it will help some readers
>> to elaborate on the Why of things here. IOW, add a comment explaining why
>> we're now supporting multi-CUs in one DWO file.
>
> Commented about the use case here & that it's supported as an extension.
>
>>
>>  > +  htab_t cus;
>>  >
>>  >    /* Table of TUs in the file.
>>  >       Each element is a struct dwo_unit.  */
>>  > @@ -9702,72 +9699,75 @@ create_dwo_cu_reader (const struct die_reader_specs *reader,
>>  >                      hex_string (dwo_unit->signature));
>>  >  }
>>  >
>>  > -/* Create the dwo_unit for the lone CU in DWO_FILE.
>>  > -   Note: This function processes DWO files only, not DWP files.  */
>>
>> Need to keep the function comment (just reword it).
>> And please keep the note about only being used for DWO files, not DWP files.
>
> Kept & reworded.
>
>>
>>  > -
>>  > -static struct dwo_unit *
>>  > -create_dwo_cu (struct dwo_file *dwo_file)
>>  > +static void create_cus_hash_table (struct dwo_file &dwo_file,
>>  > +                                   dwarf2_section_info &section,
>>  > +                                   htab_t &cus_htab)
>>  >  {
>>  >    struct objfile *objfile = dwarf2_per_objfile->objfile;
>>  > -  struct dwarf2_section_info *section = &dwo_file->sections.info;
>>  > +  const struct dwarf2_section_info *abbrev_section = &dwo_file.sections.abbrev;
>>  >    const gdb_byte *info_ptr, *end_ptr;
>>  > -  struct create_dwo_cu_data create_dwo_cu_data;
>>  > -  struct dwo_unit *dwo_unit;
>>  >
>>  > -  dwarf2_read_section (objfile, section);
>>  > -  info_ptr = section->buffer;
>>  > +  dwarf2_read_section (objfile, &section);
>>  > +  info_ptr = section.buffer;
>>  >
>>  >    if (info_ptr == NULL)
>>  > -    return NULL;
>>  > +    return;
>>  >
>>  >    if (dwarf_read_debug)
>>  >      {
>>  >        fprintf_unfiltered (gdb_stdlog, "Reading %s for %s:\n",
>>  > -                      get_section_name (section),
>>  > -                      get_section_file_name (section));
>>  > +                      get_section_name (&section),
>>  > +                      get_section_file_name (&section));
>>  >      }
>>  >
>>  > -  create_dwo_cu_data.dwo_file = dwo_file;
>>  > -  dwo_unit = NULL;
>>  > -
>>  > -  end_ptr = info_ptr + section->size;
>>  > +  end_ptr = info_ptr  + section.size;
>>
>> extra space
>
> Removed.
>
>>
>>  >    while (info_ptr < end_ptr)
>>  >      {
>>  >        struct dwarf2_per_cu_data per_cu;
>>  > +      struct create_dwo_cu_data create_dwo_cu_data;
>>  > +      struct dwo_unit *dwo_unit;
>>  > +      void **slot;
>>  > +      sect_offset sect_off = (sect_offset) (info_ptr - section.buffer);
>>  >
>>  >        memset (&create_dwo_cu_data.dwo_unit, 0,
>>  >            sizeof (create_dwo_cu_data.dwo_unit));
>>  >        memset (&per_cu, 0, sizeof (per_cu));
>>  >        per_cu.objfile = objfile;
>>  >        per_cu.is_debug_types = 0;
>>  > -      per_cu.sect_off = sect_offset (info_ptr - section->buffer);
>>  > -      per_cu.section = section;
>>  > +      per_cu.sect_off = sect_offset (info_ptr - section.buffer);
>>  > +      per_cu.section = &section;
>>  > +      create_dwo_cu_data.dwo_file = &dwo_file;
>>  >
>>  > -      init_cutu_and_read_dies_no_follow (&per_cu, dwo_file,
>>  > +      init_cutu_and_read_dies_no_follow (&per_cu, &dwo_file,
>>  >                                       create_dwo_cu_reader,
>>  >                                       &create_dwo_cu_data);
>>  > +      info_ptr += per_cu.length;
>>  >
>>  > -      if (create_dwo_cu_data.dwo_unit.dwo_file != NULL)
>>  > -    {
>>  > -      /* If we've already found one, complain.  We only support one
>>  > -         because having more than one requires hacking the dwo_name of
>>  > -         each to match, which is highly unlikely to happen.  */
>>  > -      if (dwo_unit != NULL)
>>  > -        {
>>  > -          complaint (&symfile_complaints,
>>  > -                     _("Multiple CUs in DWO file %s [in module %s]"),
>>  > -                     dwo_file->dwo_name, objfile_name (objfile));
>>  > -          break;
>>  > -        }
>>
>> Add a comment explaining why this test is present:
>>
>>  > +      if (create_dwo_cu_data.dwo_unit.dwo_file == NULL)
>>  > +        continue;
>
> Added a comment & fixed a mistake I introduced by initializing
> dwo_file before this call rather than letting the call initialize it.
> Looks like the original code was using this for error detection & I
> attempted to keep that the same but broke it by initializing the value
> early.
>
>>  >
>>  > -      dwo_unit = OBSTACK_ZALLOC (&objfile->objfile_obstack, struct dwo_unit);
>>  > -      *dwo_unit = create_dwo_cu_data.dwo_unit;
>>  > -    }
>>  > +      if (cus_htab == NULL)
>>
>> Remove the surrounding braces.
>>
>>  > +        {
>>  > +          cus_htab = allocate_dwo_unit_table (objfile);
>>  > +        }
>
> Removed & reduced indentation.
Doug Evans via gdb-patches June 28, 2017, 12:55 a.m. UTC | #2
Once more with feeling (err, not text/html ...).

On Tue, Jun 27, 2017 at 11:52 AM, David Blaikie <dblaikie@gmail.com> wrote:
>
> Ping (no rush, but trying not to forget it myself (: )
>
> On Thu, Jun 1, 2017 at 4:33 PM, David Blaikie <dblaikie@gmail.com> wrote:
> > gdb/ChangeLog:
> >
> > 2017-05-12  David Blaikie  <dblaikie@gmail.com>
> >
> > * dwarf2read.c (struct dwo_file): Use a htab of dwo_unit* (rather than
> > a singular dwo_unit*) to support multiple CUs in the same way that
> > multiple TUs are supported.
> > (static void create_cus_hash_table): Replace create_dwo_cu with a
> > function for parsing multiple CUs from a DWO file.
> > (open_and_init_dwo_file): Use create_cus_hash_table rather than create_dwo_cu.
> > (lookup_dwo_cutu): Lookup CU in the hash table in the dwo_file with
> > htab_find, rather than comparing the signature to a singleton CU in
> > the dwo_file.
> >
> > gdb/testsuite/ChangeLog:
> >
> > 2017-05-12  David Blaikie  <dblaikie@gmail.com>
> >
> > * gdb.dwarf2/fission-multi-cu.S: Test containing multiple CUs in a
> > DWO, built from fission-multi-cu{1,2}.c.
> > * gdb.dwarf2/fission-multi-cu.exp: Test similar to fission-base.exp,
> > except putting 'main' and 'func' in separate CUs while in the same DWO
> > file.
> > * gdb.dwarf2/fission-multi-cu1.c: First CU for the multi-CU-single-DWO test.
> > * gdb.dwarf2/fission-multi-cu2.c: Second CU for the multi-CU-single-DWO test.
> >
> > On Mon, May 22, 2017 at 11:01 AM, Doug Evans <dje@google.com> wrote:
> >> David Blaikie writes:
> >>  > ...
> >>
> >> Hi. Review comments inline.
> >
> > Hi - thanks for the review!
> >
> > (responses inline, patch addressing the issues is attached)
> >
> > Do let me know if there's other things that'd be good to address or if
> > this looks good as-is.

LGTM
Yao Qi July 14, 2017, 5:10 p.m. UTC | #3
"Doug Evans via gdb-patches" <gdb-patches@sourceware.org> writes:

Hi David,
This patch causes some regressions, found by buildbot,
https://sourceware.org/ml/gdb-testers/2017-q3/msg00186.html
I can reproduce the fail on my machine too.

(gdb) file /home/yao.qi/SourceCode/gnu/build/gdb/testsuite/outputs/gdb.dwarf2/fission-reread/fission-reread^M
Reading symbols from /home/yao.qi/SourceCode/gnu/build/gdb/testsuite/outputs/gdb.dwarf2/fission-reread/fission-reread...Dwarf Error: bad offset (0x0) in compilation unit header (offset 0x0 + 6) [in module /home/yao.qi/SourceCode/gnu/build/gdb/testsuite/outputs/gdb.dwarf2/fission-reread/fission-reread.dwo]^M
(no debugging symbols found)...done.^M
(gdb) break main^M
Breakpoint 1 at 0x4004d6^M
(gdb) PASS: gdb.dwarf2/fission-reread.exp: break main
ptype baz^M
type = <data variable, no debug info>^M
(gdb) FAIL: gdb.dwarf2/fission-reread.exp: ptype baz
diff mbox

Patch

diff --git gdb/dwarf2read.c gdb/dwarf2read.c
index b58d0fc16e..965d74c44f 100644
--- gdb/dwarf2read.c
+++ gdb/dwarf2read.c
@@ -852,12 +852,11 @@  struct dwo_file
      sections (for lack of a better name).  */
   struct dwo_sections sections;
 
-  /* The CU in the file.
-     We only support one because having more than one requires hacking the
-     dwo_name of each to match, which is highly unlikely to happen.
-     Doing this means all TUs can share comp_dir: We also assume that
-     DW_AT_comp_dir across all TUs in a DWO file will be identical.  */
-  struct dwo_unit *cu;
+  /* The CUs in the file.
+     Each element is a struct dwo_unit. Multiple CUs per DWO are supported as
+     an extension to handle LLVM's Link Time Optimization output (where
+     multiple source files may be compiled into a single object/dwo pair). */
+  htab_t cus;
 
   /* Table of TUs in the file.
      Each element is a struct dwo_unit.  */
@@ -9702,72 +9701,75 @@  create_dwo_cu_reader (const struct die_reader_specs *reader,
 			hex_string (dwo_unit->signature));
 }
 
-/* Create the dwo_unit for the lone CU in DWO_FILE.
+/* Create the dwo_units for the CUs in a DWO_FILE.
    Note: This function processes DWO files only, not DWP files.  */
 
-static struct dwo_unit *
-create_dwo_cu (struct dwo_file *dwo_file)
+static void
+create_cus_hash_table (struct dwo_file &dwo_file, dwarf2_section_info &section,
+		       htab_t &cus_htab)
 {
   struct objfile *objfile = dwarf2_per_objfile->objfile;
-  struct dwarf2_section_info *section = &dwo_file->sections.info;
+  const struct dwarf2_section_info *abbrev_section = &dwo_file.sections.abbrev;
   const gdb_byte *info_ptr, *end_ptr;
-  struct create_dwo_cu_data create_dwo_cu_data;
-  struct dwo_unit *dwo_unit;
 
-  dwarf2_read_section (objfile, section);
-  info_ptr = section->buffer;
+  dwarf2_read_section (objfile, &section);
+  info_ptr = section.buffer;
 
   if (info_ptr == NULL)
-    return NULL;
+    return;
 
   if (dwarf_read_debug)
     {
       fprintf_unfiltered (gdb_stdlog, "Reading %s for %s:\n",
-			  get_section_name (section),
-			  get_section_file_name (section));
+			  get_section_name (&section),
+			  get_section_file_name (&section));
     }
 
-  create_dwo_cu_data.dwo_file = dwo_file;
-  dwo_unit = NULL;
-
-  end_ptr = info_ptr + section->size;
+  end_ptr = info_ptr + section.size;
   while (info_ptr < end_ptr)
     {
       struct dwarf2_per_cu_data per_cu;
+      struct create_dwo_cu_data create_dwo_cu_data;
+      struct dwo_unit *dwo_unit;
+      void **slot;
+      sect_offset sect_off = (sect_offset) (info_ptr - section.buffer);
 
       memset (&create_dwo_cu_data.dwo_unit, 0,
 	      sizeof (create_dwo_cu_data.dwo_unit));
       memset (&per_cu, 0, sizeof (per_cu));
       per_cu.objfile = objfile;
       per_cu.is_debug_types = 0;
-      per_cu.sect_off = sect_offset (info_ptr - section->buffer);
-      per_cu.section = section;
+      per_cu.sect_off = sect_offset (info_ptr - section.buffer);
+      per_cu.section = &section;
+
+      init_cutu_and_read_dies_no_follow (
+	  &per_cu, &dwo_file, create_dwo_cu_reader, &create_dwo_cu_data);
+      info_ptr += per_cu.length;
+
+      // If the unit could not be parsed, skip it.
+      if (create_dwo_cu_data.dwo_unit.dwo_file == NULL)
+	continue;
 
-      init_cutu_and_read_dies_no_follow (&per_cu, dwo_file,
-					 create_dwo_cu_reader,
-					 &create_dwo_cu_data);
+      if (cus_htab == NULL)
+	cus_htab = allocate_dwo_unit_table (objfile);
 
-      if (create_dwo_cu_data.dwo_unit.dwo_file != NULL)
+      dwo_unit = OBSTACK_ZALLOC (&objfile->objfile_obstack, struct dwo_unit);
+      *dwo_unit = create_dwo_cu_data.dwo_unit;
+      slot = htab_find_slot (cus_htab, dwo_unit, INSERT);
+      gdb_assert (slot != NULL);
+      if (*slot != NULL)
 	{
-	  /* If we've already found one, complain.  We only support one
-	     because having more than one requires hacking the dwo_name of
-	     each to match, which is highly unlikely to happen.  */
-	  if (dwo_unit != NULL)
-	    {
-	      complaint (&symfile_complaints,
-			 _("Multiple CUs in DWO file %s [in module %s]"),
-			 dwo_file->dwo_name, objfile_name (objfile));
-	      break;
-	    }
+	  const struct dwo_unit *dup_cu = (const struct dwo_unit *)*slot;
+	  sect_offset dup_sect_off = dup_cu->sect_off;
 
-	  dwo_unit = OBSTACK_ZALLOC (&objfile->objfile_obstack, struct dwo_unit);
-	  *dwo_unit = create_dwo_cu_data.dwo_unit;
+	  complaint (&symfile_complaints,
+		     _("debug cu entry at offset 0x%x is duplicate to"
+		       " the entry at offset 0x%x, signature %s"),
+		     to_underlying (sect_off), to_underlying (dup_sect_off),
+		     hex_string (dwo_unit->signature));
 	}
-
-      info_ptr += per_cu.length;
+      *slot = (void *)dwo_unit;
     }
-
-  return dwo_unit;
 }
 
 /* DWP file .debug_{cu,tu}_index section format:
@@ -10772,7 +10774,7 @@  open_and_init_dwo_file (struct dwarf2_per_cu_data *per_cu,
   bfd_map_over_sections (dwo_file->dbfd, dwarf2_locate_dwo_sections,
 			 &dwo_file->sections);
 
-  dwo_file->cu = create_dwo_cu (dwo_file);
+  create_cus_hash_table (*dwo_file, dwo_file->sections.info, dwo_file->cus);
 
   create_debug_types_hash_table (dwo_file, dwo_file->sections.types,
 				 dwo_file->tus);
@@ -11139,10 +11141,14 @@  lookup_dwo_cutu (struct dwarf2_per_cu_data *this_unit,
 	      dwo_cutu
 		= (struct dwo_unit *) htab_find (dwo_file->tus, &find_dwo_cutu);
 	    }
-	  else if (!is_debug_types && dwo_file->cu)
+	  else if (!is_debug_types && dwo_file->cus)
 	    {
-	      if (signature == dwo_file->cu->signature)
-		dwo_cutu = dwo_file->cu;
+	      struct dwo_unit find_dwo_cutu;
+
+	      memset (&find_dwo_cutu, 0, sizeof (find_dwo_cutu));
+	      find_dwo_cutu.signature = signature;
+	      dwo_cutu = (struct dwo_unit *)htab_find (dwo_file->cus,
+						       &find_dwo_cutu);
 	    }
 
 	  if (dwo_cutu != NULL)
diff --git gdb/testsuite/gdb.dwarf2/fission-multi-cu.S gdb/testsuite/gdb.dwarf2/fission-multi-cu.S
new file mode 100644
index 0000000000..d09a7e543d
--- /dev/null
+++ gdb/testsuite/gdb.dwarf2/fission-multi-cu.S
@@ -0,0 +1,374 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2012-2017 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 created by doing:
+
+   clang fission-multi-cu*.c -g -fno-split-dwarf-inlining -emit-llvm -S -c
+   llvm-link fission-multi-cu*.ll -S -o fission-multi-cu.ll
+   clang-tot fission-multi-cu.ll -gsplit-dwarf -S
+
+   and then massaging the output.
+*/
+	.text
+	.file	"llvm-link"
+	.globl	func
+	.p2align	4, 0x90
+	.type	func,@function
+func:                                   # @func
+.Lfunc_begin0:
+	.file	1 "fission-multi-cu1.c"
+	.loc	1 20 0                  # fission-multi-cu1.c:20:0
+	.cfi_startproc
+# BB#0:                                 # %entry
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	%edi, -4(%rbp)
+	.loc	1 21 10 prologue_end    # fission-multi-cu1.c:21:10
+	movl	-4(%rbp), %edi
+	.loc	1 21 14 is_stmt 0       # fission-multi-cu1.c:21:14
+	addl	$1, %edi
+	.loc	1 21 3                  # fission-multi-cu1.c:21:3
+	movl	%edi, %eax
+	popq	%rbp
+	retq
+.Lfunc_end0:
+	.size	func, .Lfunc_end0-func
+	.cfi_endproc
+
+	.globl	main
+	.p2align	4, 0x90
+	.type	main,@function
+main:                                   # @main
+.Lfunc_begin1:
+	.file	2 "fission-multi-cu2.c"
+	.loc	2 23 0 is_stmt 1        # fission-multi-cu2.c:23:0
+	.cfi_startproc
+# BB#0:                                 # %entry
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movl	$4294967295, %edi       # imm = 0xFFFFFFFF
+	.loc	2 24 3 prologue_end     # fission-multi-cu2.c:24:3
+	movb	$0, %al
+	callq	func
+	xorl	%eax, %eax
+	.loc	2 25 1                  # fission-multi-cu2.c:25:1
+	popq	%rbp
+	retq
+.Lfunc_end1:
+	.size	main, .Lfunc_end1-main
+	.cfi_endproc
+
+	.section	.debug_str,"MS",@progbits,1
+.Lskel_string0:
+	.asciz	"fission-multi-cu.dwo"  # string offset=0
+.Lskel_string1:
+	.asciz	"/tmp/src/gdb/testsuite" # string offset=21
+	.section	.debug_loc.dwo,"",@progbits
+	.section	.debug_abbrev,"",@progbits
+	.byte	1                       # Abbreviation Code
+	.byte	17                      # DW_TAG_compile_unit
+	.byte	0                       # DW_CHILDREN_no
+	.byte	16                      # DW_AT_stmt_list
+	.byte	23                      # DW_FORM_sec_offset
+	.ascii	"\260B"                 # DW_AT_GNU_dwo_name
+	.byte	14                      # DW_FORM_strp
+	.byte	27                      # DW_AT_comp_dir
+	.byte	14                      # DW_FORM_strp
+	.ascii	"\261B"                 # DW_AT_GNU_dwo_id
+	.byte	7                       # DW_FORM_data8
+	.ascii	"\263B"                 # DW_AT_GNU_addr_base
+	.byte	23                      # DW_FORM_sec_offset
+	.byte	17                      # DW_AT_low_pc
+	.byte	1                       # DW_FORM_addr
+	.byte	18                      # DW_AT_high_pc
+	.byte	6                       # DW_FORM_data4
+	.byte	0                       # EOM(1)
+	.byte	0                       # EOM(2)
+	.byte	0                       # EOM(3)
+	.section	.debug_info,"",@progbits
+.Lcu_begin0:
+	.long	44                      # Length of Unit
+	.short	4                       # DWARF version number
+	.long	.debug_abbrev           # Offset Into Abbrev. Section
+	.byte	8                       # Address Size (in bytes)
+	.byte	1                       # Abbrev [1] 0xb:0x25 DW_TAG_compile_unit
+	.long	.Lline_table_start0     # DW_AT_stmt_list
+	.long	.Lskel_string0          # DW_AT_GNU_dwo_name
+	.long	.Lskel_string1          # DW_AT_comp_dir
+	.quad	7615852067747431413     # DW_AT_GNU_dwo_id
+	.long	.debug_addr             # DW_AT_GNU_addr_base
+	.quad	.Lfunc_begin0           # DW_AT_low_pc
+	.long	.Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+.Lcu_begin1:
+	.long	44                      # Length of Unit
+	.short	4                       # DWARF version number
+	.long	.debug_abbrev           # Offset Into Abbrev. Section
+	.byte	8                       # Address Size (in bytes)
+	.byte	1                       # Abbrev [1] 0xb:0x25 DW_TAG_compile_unit
+	.long	.Lline_table_start0     # DW_AT_stmt_list
+	.long	.Lskel_string0          # DW_AT_GNU_dwo_name
+	.long	.Lskel_string1          # DW_AT_comp_dir
+	.quad	2037650261599692324     # DW_AT_GNU_dwo_id
+	.long	.debug_addr             # DW_AT_GNU_addr_base
+	.quad	.Lfunc_begin1           # DW_AT_low_pc
+	.long	.Lfunc_end1-.Lfunc_begin1 # DW_AT_high_pc
+	.section	.debug_ranges,"",@progbits
+	.section	.debug_macinfo,"",@progbits
+.Lcu_macro_begin1:
+.Lcu_macro_begin3:
+	.byte	0                       # End Of Macro List Mark
+	.section	.debug_str.dwo,"MS",@progbits,1
+.Linfo_string0:
+	.asciz	"fission-multi-cu.dwo"  # string offset=0
+.Linfo_string1:
+	.asciz	"clang version 5.0.0 (trunk 302855) (llvm/trunk 302853)" # string offset=21
+.Linfo_string2:
+	.asciz	"fission-multi-cu1.c" # string offset=76
+.Linfo_string3:
+	.asciz	"fission-multi-cu2.c" # string offset=96
+.Linfo_string4:
+	.asciz	"func"                  # string offset=116
+.Linfo_string5:
+	.asciz	"int"                   # string offset=121
+.Linfo_string6:
+	.asciz	"main"                  # string offset=125
+.Linfo_string7:
+	.asciz	"arg"                   # string offset=130
+	.section	.debug_str_offsets.dwo,"",@progbits
+	.long	0
+	.long	21
+	.long	76
+	.long	96
+	.long	116
+	.long	121
+	.long	125
+	.long	130
+	.section	.debug_info.dwo,"",@progbits
+	.long	53                      # Length of Unit
+	.short	4                       # DWARF version number
+	.long	0                       # Offset Into Abbrev. Section
+	.byte	8                       # Address Size (in bytes)
+	.byte	1                       # Abbrev [1] 0xb:0x2e DW_TAG_compile_unit
+	.byte	0                       # DW_AT_GNU_dwo_name
+	.byte	1                       # DW_AT_producer
+	.short	12                      # DW_AT_language
+	.byte	2                       # DW_AT_name
+	.quad	7615852067747431413     # DW_AT_GNU_dwo_id
+	.byte	2                       # Abbrev [2] 0x19:0x1b DW_TAG_subprogram
+	.byte	0                       # DW_AT_low_pc
+	.long	.Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+	.byte	1                       # DW_AT_frame_base
+	.byte	86
+	.byte	4                       # DW_AT_name
+	.byte	1                       # DW_AT_decl_file
+	.byte	19                      # DW_AT_decl_line
+                                        # DW_AT_prototyped
+	.long	52                      # DW_AT_type
+                                        # DW_AT_external
+	.byte	3                       # Abbrev [3] 0x28:0xb DW_TAG_formal_parameter
+	.byte	2                       # DW_AT_location
+	.byte	145
+	.byte	124
+	.byte	7                       # DW_AT_name
+	.byte	1                       # DW_AT_decl_file
+	.byte	19                      # DW_AT_decl_line
+	.long	52                      # DW_AT_type
+	.byte	0                       # End Of Children Mark
+	.byte	4                       # Abbrev [4] 0x34:0x4 DW_TAG_base_type
+	.byte	5                       # DW_AT_name
+	.byte	5                       # DW_AT_encoding
+	.byte	4                       # DW_AT_byte_size
+	.byte	0                       # End Of Children Mark
+	.long	41                      # Length of Unit
+	.short	4                       # DWARF version number
+	.long	0                       # Offset Into Abbrev. Section
+	.byte	8                       # Address Size (in bytes)
+	.byte	1                       # Abbrev [1] 0xb:0x22 DW_TAG_compile_unit
+	.byte	0                       # DW_AT_GNU_dwo_name
+	.byte	1                       # DW_AT_producer
+	.short	12                      # DW_AT_language
+	.byte	3                       # DW_AT_name
+	.quad	2037650261599692324     # DW_AT_GNU_dwo_id
+	.byte	5                       # Abbrev [5] 0x19:0xf DW_TAG_subprogram
+	.byte	1                       # DW_AT_low_pc
+	.long	.Lfunc_end1-.Lfunc_begin1 # DW_AT_high_pc
+	.byte	1                       # DW_AT_frame_base
+	.byte	86
+	.byte	6                       # DW_AT_name
+	.byte	2                       # DW_AT_decl_file
+	.byte	22                      # DW_AT_decl_line
+	.long	40                      # DW_AT_type
+                                        # DW_AT_external
+	.byte	4                       # Abbrev [4] 0x28:0x4 DW_TAG_base_type
+	.byte	5                       # DW_AT_name
+	.byte	5                       # DW_AT_encoding
+	.byte	4                       # DW_AT_byte_size
+	.byte	0                       # End Of Children Mark
+	.section	.debug_abbrev.dwo,"",@progbits
+	.byte	1                       # Abbreviation Code
+	.byte	17                      # DW_TAG_compile_unit
+	.byte	1                       # DW_CHILDREN_yes
+	.ascii	"\260B"                 # DW_AT_GNU_dwo_name
+	.ascii	"\202>"                 # DW_FORM_GNU_str_index
+	.byte	37                      # DW_AT_producer
+	.ascii	"\202>"                 # DW_FORM_GNU_str_index
+	.byte	19                      # DW_AT_language
+	.byte	5                       # DW_FORM_data2
+	.byte	3                       # DW_AT_name
+	.ascii	"\202>"                 # DW_FORM_GNU_str_index
+	.ascii	"\261B"                 # DW_AT_GNU_dwo_id
+	.byte	7                       # DW_FORM_data8
+	.byte	0                       # EOM(1)
+	.byte	0                       # EOM(2)
+	.byte	2                       # Abbreviation Code
+	.byte	46                      # DW_TAG_subprogram
+	.byte	1                       # DW_CHILDREN_yes
+	.byte	17                      # DW_AT_low_pc
+	.ascii	"\201>"                 # DW_FORM_GNU_addr_index
+	.byte	18                      # DW_AT_high_pc
+	.byte	6                       # DW_FORM_data4
+	.byte	64                      # DW_AT_frame_base
+	.byte	24                      # DW_FORM_exprloc
+	.byte	3                       # DW_AT_name
+	.ascii	"\202>"                 # DW_FORM_GNU_str_index
+	.byte	58                      # DW_AT_decl_file
+	.byte	11                      # DW_FORM_data1
+	.byte	59                      # DW_AT_decl_line
+	.byte	11                      # DW_FORM_data1
+	.byte	39                      # DW_AT_prototyped
+	.byte	25                      # DW_FORM_flag_present
+	.byte	73                      # DW_AT_type
+	.byte	19                      # DW_FORM_ref4
+	.byte	63                      # DW_AT_external
+	.byte	25                      # DW_FORM_flag_present
+	.byte	0                       # EOM(1)
+	.byte	0                       # EOM(2)
+	.byte	3                       # Abbreviation Code
+	.byte	5                       # DW_TAG_formal_parameter
+	.byte	0                       # DW_CHILDREN_no
+	.byte	2                       # DW_AT_location
+	.byte	24                      # DW_FORM_exprloc
+	.byte	3                       # DW_AT_name
+	.ascii	"\202>"                 # DW_FORM_GNU_str_index
+	.byte	58                      # DW_AT_decl_file
+	.byte	11                      # DW_FORM_data1
+	.byte	59                      # DW_AT_decl_line
+	.byte	11                      # DW_FORM_data1
+	.byte	73                      # DW_AT_type
+	.byte	19                      # DW_FORM_ref4
+	.byte	0                       # EOM(1)
+	.byte	0                       # EOM(2)
+	.byte	4                       # Abbreviation Code
+	.byte	36                      # DW_TAG_base_type
+	.byte	0                       # DW_CHILDREN_no
+	.byte	3                       # DW_AT_name
+	.ascii	"\202>"                 # DW_FORM_GNU_str_index
+	.byte	62                      # DW_AT_encoding
+	.byte	11                      # DW_FORM_data1
+	.byte	11                      # DW_AT_byte_size
+	.byte	11                      # DW_FORM_data1
+	.byte	0                       # EOM(1)
+	.byte	0                       # EOM(2)
+	.byte	5                       # Abbreviation Code
+	.byte	46                      # DW_TAG_subprogram
+	.byte	0                       # DW_CHILDREN_no
+	.byte	17                      # DW_AT_low_pc
+	.ascii	"\201>"                 # DW_FORM_GNU_addr_index
+	.byte	18                      # DW_AT_high_pc
+	.byte	6                       # DW_FORM_data4
+	.byte	64                      # DW_AT_frame_base
+	.byte	24                      # DW_FORM_exprloc
+	.byte	3                       # DW_AT_name
+	.ascii	"\202>"                 # DW_FORM_GNU_str_index
+	.byte	58                      # DW_AT_decl_file
+	.byte	11                      # DW_FORM_data1
+	.byte	59                      # DW_AT_decl_line
+	.byte	11                      # DW_FORM_data1
+	.byte	73                      # DW_AT_type
+	.byte	19                      # DW_FORM_ref4
+	.byte	63                      # DW_AT_external
+	.byte	25                      # DW_FORM_flag_present
+	.byte	0                       # EOM(1)
+	.byte	0                       # EOM(2)
+	.byte	0                       # EOM(3)
+	.section	.debug_line.dwo,"",@progbits
+.Ltmp4:
+	.long	(.Ltmp5-.Ltmp4)-4
+	.short	2
+	.long	(.Ltmp6-.Ltmp4)-10
+	.byte	1
+	.byte	1
+	.byte	-5
+	.byte	14
+	.byte	1
+	.byte	0
+	.byte	0
+.Ltmp6:
+.Ltmp5:
+	.section	.debug_addr,"",@progbits
+	.quad	.Lfunc_begin0
+	.quad	.Lfunc_begin1
+	.section	.debug_pubnames,"",@progbits
+	.long	.LpubNames_end0-.LpubNames_begin0 # Length of Public Names Info
+.LpubNames_begin0:
+	.short	2                       # DWARF Version
+	.long	.Lcu_begin0             # Offset of Compilation Unit Info
+	.long	48                      # Compilation Unit Length
+	.long	25                      # DIE offset
+	.asciz	"func"                  # External Name
+	.long	0                       # End Mark
+.LpubNames_end0:
+	.long	.LpubNames_end1-.LpubNames_begin1 # Length of Public Names Info
+.LpubNames_begin1:
+	.short	2                       # DWARF Version
+	.long	.Lcu_begin1             # Offset of Compilation Unit Info
+	.long	48                      # Compilation Unit Length
+	.long	25                      # DIE offset
+	.asciz	"main"                  # External Name
+	.long	0                       # End Mark
+.LpubNames_end1:
+	.section	.debug_pubtypes,"",@progbits
+	.long	.LpubTypes_end0-.LpubTypes_begin0 # Length of Public Types Info
+.LpubTypes_begin0:
+	.short	2                       # DWARF Version
+	.long	.Lcu_begin0             # Offset of Compilation Unit Info
+	.long	48                      # Compilation Unit Length
+	.long	52                      # DIE offset
+	.asciz	"int"                   # External Name
+	.long	0                       # End Mark
+.LpubTypes_end0:
+	.long	.LpubTypes_end1-.LpubTypes_begin1 # Length of Public Types Info
+.LpubTypes_begin1:
+	.short	2                       # DWARF Version
+	.long	.Lcu_begin1             # Offset of Compilation Unit Info
+	.long	48                      # Compilation Unit Length
+	.long	40                      # DIE offset
+	.asciz	"int"                   # External Name
+	.long	0                       # End Mark
+.LpubTypes_end1:
+
+	.ident	"clang version 5.0.0 (trunk 302855) (llvm/trunk 302853)"
+	.ident	"clang version 5.0.0 (trunk 302855) (llvm/trunk 302853)"
+	.section	".note.GNU-stack","",@progbits
+	.section	.debug_line,"",@progbits
+.Lline_table_start0:
diff --git gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp
new file mode 100644
index 0000000000..1f23c5b6ee
--- /dev/null
+++ gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp
@@ -0,0 +1,67 @@ 
+# Copyright 2012-2017 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/>.
+
+load_lib dwarf.exp
+
+# We run objcopy locally to split out the .dwo file.
+if [is_remote host] {
+    return 0
+}
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if ![dwarf2_support] {
+    return 0
+}
+
+# This test can only be run on x86-64 targets.
+if {![istarget x86_64-*] || ![is_lp64_target]} {
+    return 0
+}
+
+standard_testfile .S
+
+if [build_executable_from_fission_assembler \
+	"$testfile.exp" "$binfile" "$srcfile" {nodebug}] {
+    return -1
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+# Make sure we can find the .dwo file, regardless of whether we're
+# running in parallel mode.
+gdb_test_no_output "set debug-file-directory [file dirname $binfile]" \
+    "set debug-file-directory"
+gdb_load $binfile
+
+if ![runto_main] {
+    return -1
+}
+
+# Do a few basic things to verify we're finding the DWO debug info.
+
+gdb_test "ptype main" "type = int \\(\\)"
+gdb_test "ptype func" "type = int \\(int\\)"
+
+gdb_test "frame" "#0 *main \\(\\) at ${testfile}2\\.c:$decimal.*" \
+    "frame in main"
+
+gdb_test "break func" "Breakpoint.*at.* file .*${testfile}1\\.c, line .*"
+
+gdb_test "continue" "Breakpoint.* func \\(arg=-1\\).*" \
+    "continue to func"
+
+gdb_test "frame" "#0 *func \\(arg=-1\\) at ${testfile}1\\.c:$decimal.*" \
+    "frame in func"
diff --git gdb/testsuite/gdb.dwarf2/fission-multi-cu1.c gdb/testsuite/gdb.dwarf2/fission-multi-cu1.c
new file mode 100644
index 0000000000..d93e2f912e
--- /dev/null
+++ gdb/testsuite/gdb.dwarf2/fission-multi-cu1.c
@@ -0,0 +1,22 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2012-2017 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
+func (int arg)
+{
+  return arg + 1;
+}
diff --git gdb/testsuite/gdb.dwarf2/fission-multi-cu2.c gdb/testsuite/gdb.dwarf2/fission-multi-cu2.c
new file mode 100644
index 0000000000..053b3ea141
--- /dev/null
+++ gdb/testsuite/gdb.dwarf2/fission-multi-cu2.c
@@ -0,0 +1,24 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2012-2017 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/>.  */
+
+void func ();
+
+int
+main ()
+{
+  func (-1);
+}