[PATCHv3,1/2] gdb/testsuite: fix fission support in the Dwarf assembler

Message ID 4b5fbc4b79d41fb803a3d02ead4a520ac72067d0.1616781092.git.andrew.burgess@embecosm.com
State New
Headers show
Series
  • Add directory containing executable to relative search paths for .dwo files.
Related show

Commit Message

Andrew Burgess March 26, 2021, 5:54 p.m.
This commit fixes fission support in the Dwarf assembler.  The new
test gdb.dwarf2/fission-absolute-dwo.exp which exercises this
feature.

To better support compiling the assembler files produced by the Dwarf
assembler I have added the new proc build_executable_and_dwo_files in
lib/dwarf.exp, this replaces build_executable_from_fission_assembler,
all the tests that used the old proc have been updated.  Where the old
proc assumed a single .S source file which contained the entire test,
the new proc allows for multiple source files.

The Dwarf assembler already had some fission support, however, this
was not actually used in any tests, and when I tried using it there
were a few issues.

The biggest change is that we now generate DW_FORM_GNU_addr_index
instead of DW_FORM_addr for the low and high pc in
_handle_macro_at_range, support for the DW_FORM_GNU_addr_index is new
in this commit.

gdb/testsuite/ChangeLog:

	* gdb.dwarf2/fission-absolute-dwo.c: New file.
	* gdb.dwarf2/fission-absolute-dwo.exp: New file.
	* gdb.dwarf2/fission-base.exp: Use build_executable_and_dwo_files
	instead of build_executable_from_fission_assembler.
	* gdb.dwarf2/fission-loclists-pie.exp: Likewise.
	* gdb.dwarf2/fission-loclists.exp: Likewise.
	* gdb.dwarf2/fission-multi-cu.exp: Likewise.
	* gdb.dwarf2/fission-reread.exp: Likewise.
	* lib/dwarf.exp (extract_dwo_information): New proc.
	(strip_dwo_information): New proc.
	(build_executable_and_dwo_files): New proc.
	(build_executable_from_fission_assembler): Delete.
	(Dwarf::_debug_addr_index): New variable.
	(Dwarf::_cu_is_fission): New variable.
	(Dwarf::_handle_DW_FORM): Handle DW_OP_GNU_addr_index.
	(Dwarf::_default_form): Supply a default for DW_AT_GNU_addr_base.
	(Dwarf::_handle_macro_at_range): Use form DW_FORM_GNU_addr_index
	if this is a fission CU.
	(Dwarf::_location): Handle DW_OP_GNU_addr_index.
	(Dwarf::debug_addr_index): New proc.
	(Dwarf::cu): Initialise _cu_is_fission.
	(Dwarf::tu): Likewise.
	(Dwarf::assemble): Initialise _debug_addr_index.
---
 gdb/testsuite/ChangeLog                       |  26 ++
 .../gdb.dwarf2/fission-absolute-dwo.c         |  28 ++
 .../gdb.dwarf2/fission-absolute-dwo.exp       | 132 ++++++++++
 gdb/testsuite/gdb.dwarf2/fission-base.exp     |  11 +-
 .../gdb.dwarf2/fission-loclists-pie.exp       |  11 +-
 gdb/testsuite/gdb.dwarf2/fission-loclists.exp |  11 +-
 gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp |  11 +-
 gdb/testsuite/gdb.dwarf2/fission-reread.exp   |  15 +-
 gdb/testsuite/lib/dwarf.exp                   | 248 ++++++++++++++----
 9 files changed, 423 insertions(+), 70 deletions(-)
 create mode 100644 gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.c
 create mode 100644 gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.exp

-- 
2.25.4

Comments

Mike Frysinger via Gdb-patches March 29, 2021, 4:30 p.m. | #1
On 2021-03-26 1:54 p.m., Andrew Burgess wrote:
> This commit fixes fission support in the Dwarf assembler.  The new

> test gdb.dwarf2/fission-absolute-dwo.exp which exercises this

> feature.

> 

> To better support compiling the assembler files produced by the Dwarf

> assembler I have added the new proc build_executable_and_dwo_files in

> lib/dwarf.exp, this replaces build_executable_from_fission_assembler,

> all the tests that used the old proc have been updated.  Where the old

> proc assumed a single .S source file which contained the entire test,

> the new proc allows for multiple source files.

> 

> The Dwarf assembler already had some fission support, however, this

> was not actually used in any tests, and when I tried using it there

> were a few issues.

> 

> The biggest change is that we now generate DW_FORM_GNU_addr_index

> instead of DW_FORM_addr for the low and high pc in

> _handle_macro_at_range, support for the DW_FORM_GNU_addr_index is new

> in this commit.


Is it possible to make tests that produce multiple CUs, and if so, is
the DW_AT_GNU_addr_base attribute correctly generated for both CUs?

> @@ -1050,6 +1197,14 @@ namespace eval Dwarf {

>  	}

>      }

>  

> +    # Return the current value of _DEBUG_ADDR_INDEX.  The return value

> +    # of this function can be used for the value of

> +    # DW_AT_GNU_addr_base.

> +    proc debug_addr_index {} {

> +	variable _debug_addr_index

> +	return ${_debug_addr_index}

> +    }


In the comment: do you really mean DW_AT_GNU_addr_base?  You probably
meant attributes for form DW_FORM_GNU_addr_index, or operator
DW_OP_GNU_addr_index.

Simon
Andrew Burgess March 30, 2021, 11:07 a.m. | #2
* Simon Marchi <simon.marchi@polymtl.ca> [2021-03-29 12:30:04 -0400]:

> On 2021-03-26 1:54 p.m., Andrew Burgess wrote:

> > This commit fixes fission support in the Dwarf assembler.  The new

> > test gdb.dwarf2/fission-absolute-dwo.exp which exercises this

> > feature.

> > 

> > To better support compiling the assembler files produced by the Dwarf

> > assembler I have added the new proc build_executable_and_dwo_files in

> > lib/dwarf.exp, this replaces build_executable_from_fission_assembler,

> > all the tests that used the old proc have been updated.  Where the old

> > proc assumed a single .S source file which contained the entire test,

> > the new proc allows for multiple source files.

> > 

> > The Dwarf assembler already had some fission support, however, this

> > was not actually used in any tests, and when I tried using it there

> > were a few issues.

> > 

> > The biggest change is that we now generate DW_FORM_GNU_addr_index

> > instead of DW_FORM_addr for the low and high pc in

> > _handle_macro_at_range, support for the DW_FORM_GNU_addr_index is new

> > in this commit.

> 

> Is it possible to make tests that produce multiple CUs, and if so, is

> the DW_AT_GNU_addr_base attribute correctly generated for both CUs?


I had thought it was, but, I hadn't really thought it through
properly, so it wouldn't have worked. I've now fixed this so that it
should work.

What we can't do is create multiple CUs in a single .S file, I just
can't see how that would work given that it's the CU that links to the
.dwo file.

But we can create multiple .S files, each is then compiled to a .o
from which we create a .dwo file.  Then all the .o files are linked
together to create one executable that references multiple .dwo
files.

The test gdb.dwarf2/fission-multi-cu.exp used to use a pre-generated
x86-64 assembler file.  As part of this patch I have rewritten this
test to generate the DWARF using the Dwarf assembler, this is now an
example of what I describe above.  I've kept the new test
gdb.dwarf2/fission-absolute-dwo.exp as this is a simpler example and
might be an easier starting point for anyone looking for something to
copy.

> 

> > @@ -1050,6 +1197,14 @@ namespace eval Dwarf {

> >  	}

> >      }

> >  

> > +    # Return the current value of _DEBUG_ADDR_INDEX.  The return value

> > +    # of this function can be used for the value of

> > +    # DW_AT_GNU_addr_base.

> > +    proc debug_addr_index {} {

> > +	variable _debug_addr_index

> > +	return ${_debug_addr_index}

> > +    }

> 

> In the comment: do you really mean DW_AT_GNU_addr_base?  You probably

> meant attributes for form DW_FORM_GNU_addr_index, or operator

> DW_OP_GNU_addr_index.


This is the proc I changed to fix the above issue, it's now called
debug_addr_label, and returns a label into the .debug_addr section.

I've updated the comment, but it mostly says the same as it did
before.

The point here is that the user will have code that looks like this:

  Dwarf::assemble $asm_file {
      cu {fission 1} {
        ...
      }
      cu {} {
        ...
     }
  }

This creates a single assembler file containing the dwo sections AND
the non-dwo sections.  Addresses in the dwo section are placed into
the .debug_addr section.  The non-fission CU will include a link to
the start of the .debug_addr section within its contained compilation
unit.

So, within 'cu {fission 1} { .... }' before we start adding things into
.debug_addr, we capture a label for the current start of .debug_addr.
Then in 'cu {} { ... }' we can use this label, like this:

  Dwarf::assemble $asm_file {
      cu {fission 1} {
  	set debug_addr_base [debug_addr_label]
          ...
      }
      cu {} {
  	compile_unit {
              ...
  	    {DW_AT_GNU_addr_base $debug_addr_base}
          }
     }
  }

I don't know if that actually answers your question, or maybe I've not
understood what you're asking me?

Anyway, revised patch below, let me know what you think.

[ NOTE: Patch #2 requires a minor change to call debug_addr_label
  instead of debug_addr_index.  I've not reposted patch #2 you would
  need to make that change manually if you're testing it. ]

Thanks,
Andrew

----

commit 081199e14bd799b3adc82f4758e2d2906e05d6f7
Author: Andrew Burgess <andrew.burgess@embecosm.com>
Date:   Fri Mar 26 12:06:37 2021 +0000

    gdb/testsuite: fix fission support in the Dwarf assembler
    
    This commit fixes fission support in the Dwarf assembler. I added the
    new test gdb.dwarf2/fission-absolute-dwo.exp which is a simple example
    of using the fission support.  I also rewrote the existing test
    gdb.dwarf2/fission-multi-cu.exp to use the new functionality (instead
    of using an x86-64 only assembler file).
    
    To better support compiling the assembler files produced by the Dwarf
    assembler I have added the new proc build_executable_and_dwo_files in
    lib/dwarf.exp, this replaces build_executable_from_fission_assembler,
    all the tests that used the old proc have been updated.  Where the old
    proc assumed a single .S source file which contained the entire test,
    the new proc allows for multiple source files.
    
    The Dwarf assembler already had some fission support, however, this
    was not actually used in any tests, and when I tried using it there
    were a few issues.
    
    The biggest change is that we now generate DW_FORM_GNU_addr_index
    instead of DW_FORM_addr for the low and high pc in
    _handle_macro_at_range, support for the DW_FORM_GNU_addr_index is new
    in this commit.
    
    gdb/testsuite/ChangeLog:
    
            * gdb.dwarf2/fission-absolute-dwo.c: New file.
            * gdb.dwarf2/fission-absolute-dwo.exp: New file.
            * gdb.dwarf2/fission-base.exp: Use build_executable_and_dwo_files
            instead of build_executable_from_fission_assembler.
            * gdb.dwarf2/fission-loclists-pie.exp: Likewise.
            * gdb.dwarf2/fission-loclists.exp: Likewise.

diff --git a/gdb/testsuite/gdb.dwarf2/fission-multi-cu2.c b/gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.c
similarity index 71%
rename from gdb/testsuite/gdb.dwarf2/fission-multi-cu2.c
rename to gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.c
index 6f0ed1f5ae9..27f7f0dfb4b 100644
--- a/gdb/testsuite/gdb.dwarf2/fission-multi-cu2.c
+++ b/gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.c
@@ -1,6 +1,6 @@
 /* This testcase is part of GDB, the GNU debugger.
 
-   Copyright 2012-2021 Free Software Foundation, Inc.
+   Copyright 2021 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
@@ -13,12 +13,16 @@
    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/>.  */
+   along with this program.  If not, see
+   <http://www.gnu.org/licenses/>.  */
 
-void func ();
+/* Our fake object.  */
+int global_var[100];
 
 int
-main ()
+main (int argc, char **argv)
 {
-  func (-1);
+  asm ("main_label: .globl main_label");
+
+  return 0;
 }
diff --git a/gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.exp b/gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.exp
new file mode 100644
index 00000000000..2483349bf78
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.exp
@@ -0,0 +1,133 @@
+# Copyright 2021 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 is a basic first test for using the testsuite's Dwarf assembler to
+# create split debug information.  There's not unique feature of GDB being
+# tested here, this exists only as a basic test for the testsuite
+# infrastructure.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+standard_testfile .c -dw.S
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
+    return -1
+}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcfile binfile objdir
+
+    set debug_addr_lbl ".unknown!!"
+
+    # The information that will be split out into the .dwo file.
+    cu {fission 1} {
+
+	# Capture a label to the current start of the .debug_addr
+	# section.  This will be passed to DW_AT_GNU_addr_base in the
+	# non-split CU later.
+	set debug_addr_lbl [debug_addr_label]
+
+	compile_unit {
+            {language @DW_LANG_C}
+            {name ${srcfile}}
+	    {DW_AT_comp_dir ${objdir}}
+	    {DW_AT_GNU_dwo_id 0x1234 DW_FORM_data8}
+        } {
+	    declare_labels int4_type struct_type
+
+	    int4_type: DW_TAG_base_type {
+		{DW_AT_byte_size 4 DW_FORM_sdata}
+		{DW_AT_encoding  @DW_ATE_signed}
+		{DW_AT_name      integer}
+	    }
+
+	    struct_type: DW_TAG_structure_type {
+		{DW_AT_name "foo_t"}
+		{DW_AT_byte_size 12 DW_FORM_sdata}
+	    } {
+		member {
+		    {name "aa"}
+		    {type :$int4_type}
+		    {data_member_location 0 data1}
+		}
+		member {
+		    {name "bb"}
+		    {type :$int4_type}
+		    {data_member_location 4 data1}
+		}
+		member {
+		    {name "cc"}
+		    {type :$int4_type}
+		    {data_member_location 8 data1}
+		}
+	    }
+
+	    DW_TAG_variable {
+		{DW_AT_name global_var}
+		{DW_AT_type :$struct_type}
+		{DW_AT_location {
+		    DW_OP_GNU_addr_index [gdb_target_symbol global_var]
+		} SPECIAL_expr}
+		{external 1 flag}
+	    }
+
+	    subprogram {
+		{external 1 flag}
+		{DW_AT_name main DW_FORM_string}
+		{MACRO_AT_func {main}}
+	    }
+	}
+    }
+
+    # The information that will remain in the .o file.
+    cu {} {
+	compile_unit {
+            {DW_AT_GNU_dwo_name ${binfile}.dwo DW_FORM_strp}
+	    {DW_AT_comp_dir ${objdir}}
+	    {DW_AT_GNU_dwo_id 0x1234 DW_FORM_data8}
+	    {DW_AT_GNU_addr_base $debug_addr_lbl}
+        } {
+	    # Nothing.
+	}
+    }
+}
+
+# Compile both source files to create the executable.  As we compile
+# ASM_FILE we split out the debug information into the dwo file.
+set object_file [standard_output_file ${testfile}.o]
+if { [build_executable_and_dwo_files "${testfile}.exp" ${binfile} {nodebug} \
+	  [list $asm_file {nodebug split-dwo} ${object_file}] \
+	  [list $srcfile {nodebug}]] } {
+    perror "failed to compile ${gdb_test_file_name}"
+    return -1
+}
+
+# Now we can start GDB.
+clean_restart ${testfile}
+
+if ![runto_main] {
+    return -1
+}
+
+# Print the type of global_var.  This type information is entirely
+# fictional, it only exists in the DWARF.  If we don't have the DWARF
+# information then there's no way we can print this.
+gdb_test "p global_var" " = \\{aa = 0, bb = 0, cc = 0\\}"
diff --git a/gdb/testsuite/gdb.dwarf2/fission-base.exp b/gdb/testsuite/gdb.dwarf2/fission-base.exp
index 40a7e2a4e3c..00eb7fec1b0 100644
--- a/gdb/testsuite/gdb.dwarf2/fission-base.exp
+++ b/gdb/testsuite/gdb.dwarf2/fission-base.exp
@@ -32,11 +32,12 @@ if {![istarget x86_64-*] || ![is_lp64_target]} {
 
 standard_testfile .S
 
-set dwo [standard_output_file "fission-base.dwo"]
-
-if [build_executable_from_fission_assembler \
-	"$testfile.exp" "$binfile" "$srcfile" \
-	[list nodebug additional_flags=-DDWO=$dwo]] {
+set obj [standard_output_file "${testfile}.o"]
+set dwo [standard_output_file "${testfile}.dwo"]
+if [build_executable_and_dwo_files "$testfile.exp" "${binfile}" {nodebug} \
+	[list $srcfile \
+	     [list nodebug split-dwo additional_flags=-DDWO=$dwo] \
+	     $obj]] {
     return -1
 }
 
diff --git a/gdb/testsuite/gdb.dwarf2/fission-loclists-pie.exp b/gdb/testsuite/gdb.dwarf2/fission-loclists-pie.exp
index c0a7785985b..21098a24624 100644
--- a/gdb/testsuite/gdb.dwarf2/fission-loclists-pie.exp
+++ b/gdb/testsuite/gdb.dwarf2/fission-loclists-pie.exp
@@ -37,11 +37,12 @@ if {![istarget x86_64-*] || ![is_lp64_target]} {
 
 standard_testfile .S
 
-set dwo [standard_output_file "fission-loclists-pie.dwo"]
-
-if [build_executable_from_fission_assembler \
-	"$testfile.exp" "$binfile" "$srcfile" \
-	[list "nodebug" "ldflags=-pie" additional_flags=-DDWO=$dwo]] {
+set obj [standard_output_file "${testfile}.o"]
+set dwo [standard_output_file "${testfile}.dwo"]
+if [build_executable_and_dwo_files "$testfile.exp" "${binfile}" \
+	{nodebug ldflags=-pie} \
+	[list $srcfile [list nodebug split-dwo additional_flags=-DDWO=$dwo] \
+	     $obj]] {
     return -1
 }
 
diff --git a/gdb/testsuite/gdb.dwarf2/fission-loclists.exp b/gdb/testsuite/gdb.dwarf2/fission-loclists.exp
index 54eace0496c..c490763982e 100644
--- a/gdb/testsuite/gdb.dwarf2/fission-loclists.exp
+++ b/gdb/testsuite/gdb.dwarf2/fission-loclists.exp
@@ -32,11 +32,12 @@ if {![istarget x86_64-*] || ![is_lp64_target]} {
 
 standard_testfile .S
 
-set dwo [standard_output_file "fission-loclists.dwo"]
-
-if [build_executable_from_fission_assembler \
-	"$testfile.exp" "$binfile" "$srcfile" \
-	[list nodebug additional_flags=-DDWO=$dwo]] {
+set obj [standard_output_file "${testfile}.o"]
+set dwo [standard_output_file "${testfile}.dwo"]
+if [build_executable_and_dwo_files "$testfile.exp" "${binfile}" {nodebug} \
+	[list $srcfile \
+	     [list nodebug split-dwo additional_flags=-DDWO=$dwo] \
+	     $obj]] {
     return -1
 }
 
diff --git a/gdb/testsuite/gdb.dwarf2/fission-multi-cu.S b/gdb/testsuite/gdb.dwarf2/fission-multi-cu.S
deleted file mode 100644
index df83613831e..00000000000
--- a/gdb/testsuite/gdb.dwarf2/fission-multi-cu.S
+++ /dev/null
@@ -1,364 +0,0 @@
-/* This testcase is part of GDB, the GNU debugger.
-
-   Copyright 2012-2021 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.
-*/
-
-#define XSTR(s) STR(s)
-#define STR(s) #s
-
-	.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	XSTR(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_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 a/gdb/testsuite/gdb.dwarf2/fission-multi-cu1.c b/gdb/testsuite/gdb.dwarf2/fission-multi-cu.c
similarity index 72%
rename from gdb/testsuite/gdb.dwarf2/fission-multi-cu1.c
rename to gdb/testsuite/gdb.dwarf2/fission-multi-cu.c
index 08674a9b4a9..6a177ad4e61 100644
--- a/gdb/testsuite/gdb.dwarf2/fission-multi-cu1.c
+++ b/gdb/testsuite/gdb.dwarf2/fission-multi-cu.c
@@ -15,8 +15,27 @@
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
+#define LL(N) asm ("line_label_" #N ": .globl line_label_" #N)
+
+/* Fake parameter location.  */
+int global_param = 0;
+
 int
 func (int arg)
 {
+  asm ("func_label: .globl func_label");
+  LL(4);
   return arg + 1;
 }
+
+int
+main ()
+{
+  asm ("main_label: .globl main_label");
+  LL(1);
+  global_param = -1;
+  LL(2);
+  func (-1);
+  LL(3);
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp b/gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp
index dec754798b6..0f7f796a541 100644
--- a/gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp
+++ b/gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp
@@ -13,6 +13,9 @@
 # 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 test checks that GDB can load DWARF information from two
+# separate split .DWO files.
+
 load_lib dwarf.exp
 
 # We run objcopy locally to split out the .dwo file.
@@ -25,18 +28,186 @@ if ![dwarf2_support] {
     return 0
 }
 
-# This test can only be run on x86-64 targets.
-if {![istarget x86_64-*] || ![is_lp64_target]} {
-    return 0
+# We place the entire source code for the test into a single .c file,
+# but we generate the DWARF in two separate .S files.  Each .S is
+# compiled to a .o, then the DWARF is split into a .dwo file.  Finally
+# the all three .o files are merged into a single executable that will
+# reference the two .dwo files.
+standard_testfile .c -1-dw.S -2-cw.S
+
+# Generate the first .S file.
+set asm_file_1 [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file_1 {
+    global srcfile binfile objdir srcdir subdir
+
+    get_func_info func
+
+    declare_labels int4_type lines_table
+
+    set debug_addr_lbl ".unknown!!"
+
+    # The information that will be split out into the .dwo file.
+    cu {fission 1} {
+
+	# Capture a label to the current start of the .debug_addr
+	# section.  This will be passed to DW_AT_GNU_addr_base in the
+	# non-split CU later.
+	set debug_addr_lbl [debug_addr_label]
+
+	compile_unit {
+            {language @DW_LANG_C}
+            {name ${srcfile}}
+	    {DW_AT_comp_dir ${objdir}}
+	    {DW_AT_GNU_dwo_id 0x1234 DW_FORM_data8}
+        } {
+	    int4_type: DW_TAG_base_type {
+		{DW_AT_byte_size 4 DW_FORM_sdata}
+		{DW_AT_encoding  @DW_ATE_signed}
+		{DW_AT_name      int}
+	    }
+
+	    subprogram {
+		{external 1 flag}
+		{DW_AT_name func DW_FORM_string}
+		{MACRO_AT_func {func}}
+		{DW_AT_type :$int4_type}
+	    } {
+		DW_TAG_formal_parameter {
+		    {DW_AT_name arg}
+		    {DW_AT_type :$int4_type}
+		    {DW_AT_location {
+			DW_OP_GNU_addr_index [gdb_target_symbol global_param]
+		    } SPECIAL_expr}
+		}
+	    }
+	}
+    }
+
+    lines {version 2} lines_table {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile" 1
+
+	program {
+	    {DW_LNE_set_address $func_start}
+	    {DW_LNS_advance_line 24}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_4}
+	    {DW_LNS_advance_line 3}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address $func_end}
+	    {DW_LNS_advance_line 1}
+	    {DW_LNS_copy}
+	    {DW_LNE_end_sequence}
+	}
+    }
+
+    # The information that will remain in the .o file.
+    cu {} {
+	compile_unit {
+            {DW_AT_GNU_dwo_name ${binfile}-1-dw.dwo DW_FORM_strp}
+	    {DW_AT_comp_dir ${objdir}}
+	    {DW_AT_GNU_dwo_id 0x1234 DW_FORM_data8}
+	    {DW_AT_GNU_addr_base $debug_addr_lbl}
+	    {stmt_list $lines_table DW_FORM_sec_offset}
+        } {
+	    # Nothing.
+	}
+    }
 }
 
-standard_testfile .S
-
-set dwo [standard_output_file "fission-multi-cu.dwo"]
+# Generate the second .S file.
+set asm_file_2 [standard_output_file $srcfile3]
+Dwarf::assemble $asm_file_2 {
+    global srcfile binfile objdir srcdir subdir
+
+    set debug_addr_lbl ".unknown!!"
+
+    declare_labels int4_type lines_table
+
+    get_func_info main
+
+    # The information that will be split out into the .dwo file.
+    cu {fission 1} {
+
+	# Capture a label to the current start of the .debug_addr
+	# section.  This will be passed to DW_AT_GNU_addr_base in the
+	# non-split CU later.
+	set debug_addr_lbl [debug_addr_label]
+
+	compile_unit {
+            {language @DW_LANG_C}
+            {name ${srcfile}}
+	    {DW_AT_comp_dir ${objdir}}
+	    {DW_AT_GNU_dwo_id 0x4567 DW_FORM_data8}
+        } {
+	    int4_type: DW_TAG_base_type {
+		{DW_AT_byte_size 4 DW_FORM_sdata}
+		{DW_AT_encoding  @DW_ATE_signed}
+		{DW_AT_name      int}
+	    }
+
+	    subprogram {
+		{external 1 flag}
+		{DW_AT_name main DW_FORM_string}
+		{MACRO_AT_func {main}}
+		{DW_AT_type :$int4_type}
+		{DW_AT_decl_file 1 data1}
+		{DW_AT_decl_line 29 data1}
+	    }
+	}
+    }
+
+    lines {version 2} lines_table {
+	include_dir "${srcdir}/${subdir}"
+	file_name "$srcfile" 1
+
+	program {
+	    {DW_LNE_set_address $main_start}
+	    {DW_LNS_advance_line 32}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_1}
+	    {DW_LNS_advance_line 3}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_2}
+	    {DW_LNS_advance_line 2}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address line_label_3}
+	    {DW_LNS_advance_line 2}
+	    {DW_LNS_copy}
+
+	    {DW_LNE_set_address $main_end}
+	    {DW_LNS_advance_line 2}
+	    {DW_LNS_copy}
+	    {DW_LNE_end_sequence}
+	}
+    }
+
+    # The information that will remain in the .o file.
+    cu {} {
+	compile_unit {
+            {DW_AT_GNU_dwo_name ${binfile}-2-dw.dwo DW_FORM_strp}
+	    {DW_AT_comp_dir ${objdir}}
+	    {DW_AT_GNU_dwo_id 0x4567 DW_FORM_data8}
+	    {DW_AT_GNU_addr_base $debug_addr_lbl}
+	    {stmt_list $lines_table DW_FORM_sec_offset}
+        } {
+	    # Nothing.
+	}
+    }
+}
 
-if [build_executable_from_fission_assembler \
-	"$testfile.exp" "$binfile" "$srcfile" \
-	[list nodebug additional_flags=-DDWO=$dwo]] {
+# Compile all of the input files, split the DWARF into the .dwo files.
+set obj1 [standard_output_file "${testfile}-1-dw.o"]
+set obj2 [standard_output_file "${testfile}-2-dw.o"]
+if [build_executable_and_dwo_files "$testfile.exp" "${binfile}" {nodebug} \
+	[list $asm_file_1 [list nodebug split-dwo] $obj1] \
+	[list $asm_file_2 [list nodebug split-dwo] $obj2] \
+	[list $srcfile  [list nodebug]]] {
     return -1
 }
 
@@ -51,13 +222,13 @@ if ![runto_main] {
 gdb_test "ptype main" "type = int \\(\\)"
 gdb_test "ptype func" "type = int \\(int\\)"
 
-gdb_test "frame" "#0 *main \\(\\) at ${testfile}2\\.c:$decimal.*" \
+gdb_test "frame" "#0 *main \\(\\) at \[^\r\n\]+${srcfile}:$decimal.*" \
     "frame in main"
 
-gdb_test "break func" "Breakpoint.*at.* file .*${testfile}1\\.c, line .*"
+gdb_test "break func" "Breakpoint.*at.* file .*${srcfile}, line .*"
 
 gdb_test "continue" "Breakpoint.* func \\(arg=-1\\).*" \
     "continue to func"
 
-gdb_test "frame" "#0 *func \\(arg=-1\\) at ${testfile}1\\.c:$decimal.*" \
+gdb_test "frame" "#0 *func \\(arg=-1\\) at \[^\r\n\]+${srcfile}:$decimal.*" \
     "frame in func"
diff --git a/gdb/testsuite/gdb.dwarf2/fission-reread.exp b/gdb/testsuite/gdb.dwarf2/fission-reread.exp
index 58132794ee7..16a139eb0cd 100644
--- a/gdb/testsuite/gdb.dwarf2/fission-reread.exp
+++ b/gdb/testsuite/gdb.dwarf2/fission-reread.exp
@@ -30,17 +30,20 @@ set additional_flags [gdb_target_symbol_prefix_flags_asm]
 
 standard_testfile .S
 
-set dwo [standard_output_file "fission-reread.dwo"]
+set obj [standard_output_file "${testfile}.o"]
+set dwo [standard_output_file "${testfile}.dwo"]
 
-set options [list]
-lappend options nodebug
+set options [list nodebug]
 if { $additional_flags != "" } {
     lappend options $additional_flags
 }
-lappend options additional_flags=-DDWO=$dwo
 
-if [build_executable_from_fission_assembler \
-	"$testfile.exp" "$binfile" "$srcfile" $options] {
+set dwo_options $options
+lappend dwo_options split-dwo
+lappend dwo_options additional_flags=-DDWO=$dwo
+
+if [build_executable_and_dwo_files "$testfile.exp" "${binfile}" $options \
+	[list $srcfile $dwo_options $obj]] {
     return -1
 }
 
diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
index 4cd5e16c604..558d0fd0139 100644
--- a/gdb/testsuite/lib/dwarf.exp
+++ b/gdb/testsuite/lib/dwarf.exp
@@ -28,39 +28,12 @@ proc dwarf2_support {} {
     return 0
 }
 
-# Build an executable from a fission-based .S file.
-# This handles the extra work of splitting the .o into non-dwo and dwo
-# pieces, making sure the .dwo is available if we're using cc-with-tweaks.sh
-# to build a .dwp file.
-# The arguments and results are the same as for build_executable.
+# Use 'objcopy --extract-dwo to extract DWO information from
+# OBJECT_FILE and place it into DWO_FILE.
 #
-# Current restrictions:
-# - only supports one source file
-# - cannot be run on remote hosts
-
-proc build_executable_from_fission_assembler { testname executable sources options } {
-    verbose -log "build_executable_from_fission_assembler $testname $executable $sources $options"
-    if { [llength $sources] != 1 } {
-	error "Only one source file supported."
-    }
-    if [is_remote host] {
-	error "Remote hosts are not supported."
-    }
-
-    global srcdir subdir
-    set source_file ${srcdir}/${subdir}/${sources}
-    set root_name [file rootname [file tail $source_file]]
-    set output_base [standard_output_file $root_name]
-    set object_file ${output_base}.o
-    set dwo_file ${output_base}.dwo
-    set object_options "object $options"
+# Return 0 on success, otherwise, return -1.
+proc extract_dwo_information { object_file dwo_file } {
     set objcopy [gdb_find_objcopy]
-
-    set result [gdb_compile $source_file $object_file object $options]
-    if { "$result" != "" } {
-	return -1
-    }
-
     set command "$objcopy --extract-dwo $object_file $dwo_file"
     verbose -log "Executing $command"
     set result [catch "exec $command" output]
@@ -68,19 +41,157 @@ proc build_executable_from_fission_assembler { testname executable sources optio
     if { $result == 1 } {
 	return -1
     }
+    return 0
+}
 
-    set command "$objcopy --strip-dwo $object_file"
+# Use 'objcopy --strip-dwo to remove DWO information from
+# FILENAME.
+#
+# Return 0 on success, otherwise, return -1.
+proc strip_dwo_information { filename } {
+    set objcopy [gdb_find_objcopy]
+    set command "$objcopy --strip-dwo $filename"
     verbose -log "Executing $command"
     set result [catch "exec $command" output]
     verbose -log "objcopy --strip-dwo output: $output"
     if { $result == 1 } {
 	return -1
     }
+    return 0
+}
 
-    set result [gdb_compile $object_file $executable executable $options]
-    if { "$result" != "" } {
+# Build an executable, with the debug information split out into a
+# separate .dwo file.
+#
+# This function is based on build_executable_from_specs in
+# lib/gdb.exp, but with threading support, and rust support removed.
+#
+# TESTNAME is the name of the test; this is passed to 'untested' if
+# something fails.
+#
+# EXECUTABLE is the executable to create, this can be an absolute
+# path, or a relative path, in which case the EXECUTABLE will be
+# created in the standard output directory.
+#
+# OPTIONS is passed to the final link, using gdb_compile.  If OPTIONS
+# contains any option that indicates threads is required, of if the
+# option rust is included, then this function will return failure.
+#
+# ARGS is a series of lists.  Each list is a spec for one source file
+# that will be compiled to make EXECUTABLE.  Each spec in ARGS has the
+# form:
+#	[ SOURCE OPTIONS ]
+# or:
+#       [ SOURCE OPTIONS OBJFILE ]
+#
+# Where SOURCE is the path to the source file to compile.  This can be
+# absolute, or relative to the standard global ${subdir}/${srcdir}/
+# path.
+#
+# OPTIONS are the options to use when compiling SOURCE into an object
+# file.
+#
+# OBJFILE is optional, if present this is the name of the object file
+# to create for SOURCE.  If this is not provided then a suitable name
+# will be auto-generated.
+#
+# If OPTIONS contains the option 'split-dwo' then the debug
+# information is extracted from the object file created by compiling
+# SOURCE and placed into a file with a dwo extension.  The name of
+# this file is generated based on the name of the object file that was
+# created (with the .o replaced with .dwo).
+proc build_executable_and_dwo_files { testname executable options args } {
+    global subdir
+    global srcdir
+
+    if { ! [regexp "^/" "$executable"] } then {
+	set binfile [standard_output_file $executable]
+    } else {
+	set binfile $executable
+    }
+
+    set info_options ""
+    if { [lsearch -exact $options "c++"] >= 0 } {
+	set info_options "c++"
+    }
+    if [get_compiler_info ${info_options}] {
+        return -1
+    }
+
+    set func gdb_compile
+    if {[lsearch -regexp $options \
+	     {^(pthreads|shlib|shlib_pthreads|openmp)$}] != -1} {
+	# Currently don't support compiling thread based tests here.
+	# If this is required then look to build_executable_from_specs
+	# for inspiration.
 	return -1
     }
+    if {[lsearch -exact $options rust] != -1} {
+	# Currently don't support compiling rust tests here.  If this
+	# is required then look to build_executable_from_specs for
+	# inspiration.
+	return -1
+    }
+
+    # Must be run on local host due to use of objcopy.
+    if [is_remote host] {
+	return -1
+    }
+
+    set objects {}
+    set i 0
+    foreach spec $args {
+	if {[llength $spec] < 2} {
+	    error "invalid spec length"
+	    return -1
+	}
+
+	verbose -log "APB: SPEC: $spec"
+
+	set s [lindex $spec 0]
+	set local_options [lindex $spec 1]
+
+	if { ! [regexp "^/" "$s"] } then {
+	    set s "$srcdir/$subdir/$s"
+	}
+
+	if {[llength $spec] > 2} {
+	    set objfile [lindex $spec 2]
+	} else {
+	    set objfile "${binfile}${i}.o"
+	    incr i
+	}
+
+	if  { [$func "${s}" "${objfile}" object $local_options] != "" } {
+	    untested $testname
+	    return -1
+	}
+
+	lappend objects "$objfile"
+
+	if {[lsearch -exact $local_options "split-dwo"] >= 0} {
+	    # Split out the DWO file.
+	    set dwo_file "[file rootname ${objfile}].dwo"
+
+	    if { [extract_dwo_information $objfile $dwo_file] == -1 } {
+		untested $testname
+		return -1
+	    }
+
+	    if { [strip_dwo_information $objfile] == -1 } {
+		untested $testname
+		return -1
+	    }
+	}
+    }
+
+    verbose -log "APB: OBJECTS = $objects"
+
+    set ret [$func $objects "${binfile}" executable $options]
+    if  { $ret != "" } {
+        untested $testname
+        return -1
+    }
 
     return 0
 }
@@ -329,6 +440,14 @@ namespace eval Dwarf {
     # The address size for debug ranges section.
     variable _debug_ranges_64_bit
 
+    # The index into the .debug_addr section (used for fission
+    # generation).
+    variable _debug_addr_index
+
+    # Flag, true if the current CU is contains fission information,
+    # otherwise false.
+    variable _cu_is_fission
+
     proc _process_one_constant {name value} {
 	variable _constants
 	variable _AT
@@ -486,6 +605,18 @@ namespace eval Dwarf {
 		_op .${_cu_addr_size}byte $value
 	    }
 
+	    DW_FORM_GNU_addr_index {
+		variable _debug_addr_index
+		variable _cu_addr_size
+
+		_op .uleb128 ${_debug_addr_index}
+		incr _debug_addr_index
+
+		_defer_output .debug_addr {
+		    _op .${_cu_addr_size}byte $value
+		}
+	    }
+
 	    DW_FORM_data2 -
 	    DW_FORM_ref2 {
 		_op .2byte $value
@@ -553,7 +684,6 @@ namespace eval Dwarf {
 	    DW_FORM_strx3 -
 	    DW_FORM_strx4 -
 
-	    DW_FORM_GNU_addr_index -
 	    DW_FORM_GNU_str_index -
 
 	    default {
@@ -609,6 +739,9 @@ namespace eval Dwarf {
 	    DW_AT_name {
 		return DW_FORM_string
 	    }
+	    DW_AT_GNU_addr_base {
+		return DW_FORM_sec_offset
+	    }
 	}
 	return ""
     }
@@ -649,6 +782,8 @@ namespace eval Dwarf {
     # Handle macro attribute MACRO_AT_range.
 
     proc _handle_macro_at_range { attr_value } {
+	variable _cu_is_fission
+
 	if {[llength $attr_value] != 1} {
 	    error "usage: MACRO_AT_range { func }"
 	}
@@ -658,10 +793,14 @@ namespace eval Dwarf {
 	set src ${srcdir}/${subdir}/${srcfile}
 	set result [function_range $func $src]
 
-	_handle_attribute DW_AT_low_pc [lindex $result 0] \
-	    DW_FORM_addr
+	set form DW_FORM_addr
+	if { $_cu_is_fission } {
+	    set form DW_FORM_GNU_addr_index
+	}
+
+	_handle_attribute DW_AT_low_pc [lindex $result 0] $form
 	_handle_attribute DW_AT_high_pc \
-	    "[lindex $result 0] + [lindex $result 1]" DW_FORM_addr
+	    "[lindex $result 0] + [lindex $result 1]" $form
     }
 
     # Handle macro attribute MACRO_AT_func.
@@ -929,6 +1068,18 @@ namespace eval Dwarf {
 		    _op .${addr_size}byte [lindex $line 1]
 		}
 
+		DW_OP_GNU_addr_index {
+		    variable _debug_addr_index
+		    variable _cu_addr_size
+
+		    _op .uleb128 ${_debug_addr_index}
+		    incr _debug_addr_index
+
+		    _defer_output .debug_addr {
+			_op .${_cu_addr_size}byte [lindex $line 1]
+		    }
+		}
+
 		DW_OP_regx {
 		    _op .uleb128 [lindex $line 1]
 		}
@@ -1050,6 +1201,23 @@ namespace eval Dwarf {
 	}
     }
 
+    # Return a label that references the current position in the
+    # .debug_addr table.  When a user is creating split DWARF they
+    # will define two CUs, the first will be the split DWARF content,
+    # and the second will be the non-split stub CU.  The split DWARF
+    # CU fills in the .debug_addr section, but the non-split CU
+    # includes a reference to the start of the section.  The label
+    # returned by this proc provides that reference.
+    proc debug_addr_label {} {
+	variable _debug_addr_index
+
+	set lbl [new_label "debug_addr_idx_${_debug_addr_index}_"]
+	_defer_output .debug_addr {
+	    define_label $lbl
+	}
+	return $lbl
+    }
+
     # Emit a DWARF CU.
     # OPTIONS is a list with an even number of elements containing
     # option-name and option-value pairs.
@@ -1073,12 +1241,13 @@ namespace eval Dwarf {
 	variable _cu_version
 	variable _cu_addr_size
 	variable _cu_offset_size
+	variable _cu_is_fission
 
 	# Establish the defaults.
 	set is_64 0
 	set _cu_version 4
 	set _cu_addr_size default
-	set fission 0
+	set _cu_is_fission 0
 	set section ".debug_info"
 	set _abbrev_section ".debug_abbrev"
 
@@ -1088,7 +1257,7 @@ namespace eval Dwarf {
 		is_64 { set is_64 $value }
 		version { set _cu_version $value }
 		addr_size { set _cu_addr_size $value }
-		fission { set fission $value }
+		fission { set _cu_is_fission $value }
 		default { error "unknown option $name" }
 	    }
 	}
@@ -1100,7 +1269,7 @@ namespace eval Dwarf {
 	    }
 	}
 	set _cu_offset_size [expr { $is_64 ? 8 : 4 }]
-	if { $fission } {
+	if { $_cu_is_fission } {
 	    set section ".debug_info.dwo"
 	    set _abbrev_section ".debug_abbrev.dwo"
 	}
@@ -1180,12 +1349,13 @@ namespace eval Dwarf {
 	variable _cu_version
 	variable _cu_addr_size
 	variable _cu_offset_size
+	variable _cu_is_fission
 
 	# Establish the defaults.
 	set is_64 0
 	set _cu_version 4
 	set _cu_addr_size default
-	set fission 0
+	set _cu_is_fission 0
 	set section ".debug_types"
 	set _abbrev_section ".debug_abbrev"
 
@@ -1194,7 +1364,7 @@ namespace eval Dwarf {
 		is_64 { set is_64 $value }
 		version { set _cu_version $value }
 		addr_size { set _cu_addr_size $value }
-		fission { set fission $value }
+		fission { set _cu_is_fission $value }
 		default { error "unknown option $name" }
 	    }
 	}
@@ -1206,7 +1376,7 @@ namespace eval Dwarf {
 	    }
 	}
 	set _cu_offset_size [expr { $is_64 ? 8 : 4 }]
-	if { $fission } {
+	if { $_cu_is_fission } {
 	    set section ".debug_types.dwo"
 	    set _abbrev_section ".debug_abbrev.dwo"
 	}
@@ -2056,6 +2226,7 @@ namespace eval Dwarf {
 	variable _line_saw_program
 	variable _line_header_end_label
 	variable _debug_ranges_64_bit
+	variable _debug_addr_index
 
 	if {!$_initialized} {
 	    _read_constants
@@ -2074,6 +2245,8 @@ namespace eval Dwarf {
 	set _line_saw_program 0
 	set _debug_ranges_64_bit [is_64_target]
 
+	set _debug_addr_index 0
+
 	# Not "uplevel" here, because we want to evaluate in this
 	# namespace.  This is somewhat bad because it means we can't
 	# readily refer to outer variables.
Tom Tromey April 1, 2021, 4:41 p.m. | #3
Andrew> commit 081199e14bd799b3adc82f4758e2d2906e05d6f7
Andrew> Author: Andrew Burgess <andrew.burgess@embecosm.com>
Andrew> Date:   Fri Mar 26 12:06:37 2021 +0000

Andrew>     gdb/testsuite: fix fission support in the Dwarf assembler
    
FWIW this looks reasonable to me.
Thank you for doing this.

Tom

Patch

diff --git a/gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.c b/gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.c
new file mode 100644
index 00000000000..27f7f0dfb4b
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.c
@@ -0,0 +1,28 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2021 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/>.  */
+
+/* Our fake object.  */
+int global_var[100];
+
+int
+main (int argc, char **argv)
+{
+  asm ("main_label: .globl main_label");
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.exp b/gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.exp
new file mode 100644
index 00000000000..78c04225144
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/fission-absolute-dwo.exp
@@ -0,0 +1,132 @@ 
+# Copyright 2021 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 is a basic first test for using the testsuite's Dwarf assembler to
+# create split debug information.  There's not unique feature of GDB being
+# tested here, this exists only as a basic test for the testsuite
+# infrastructure.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+if {![dwarf2_support]} {
+    return 0
+}
+
+standard_testfile .c -dw.S
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
+    return -1
+}
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    global srcfile binfile objdir
+
+    set debug_addr_base -1
+
+    # The information that will be split out into the .dwo file.
+    cu {fission 1} {
+
+	# Capture the current index into .debug_addr so we can fill in
+	# DW_AT_GNU_addr_base later.
+	set debug_addr_base [debug_addr_index]
+
+	compile_unit {
+            {language @DW_LANG_C}
+            {name ${srcfile}}
+	    {DW_AT_comp_dir ${objdir}}
+	    {DW_AT_GNU_dwo_id 0x1234 DW_FORM_data8}
+        } {
+	    declare_labels int4_type struct_type
+
+	    int4_type: DW_TAG_base_type {
+		{DW_AT_byte_size 4 DW_FORM_sdata}
+		{DW_AT_encoding  @DW_ATE_signed}
+		{DW_AT_name      integer}
+	    }
+
+	    struct_type: DW_TAG_structure_type {
+		{DW_AT_name "foo_t"}
+		{DW_AT_byte_size 12 DW_FORM_sdata}
+	    } {
+		member {
+		    {name "aa"}
+		    {type :$int4_type}
+		    {data_member_location 0 data1}
+		}
+		member {
+		    {name "bb"}
+		    {type :$int4_type}
+		    {data_member_location 4 data1}
+		}
+		member {
+		    {name "cc"}
+		    {type :$int4_type}
+		    {data_member_location 8 data1}
+		}
+	    }
+
+	    DW_TAG_variable {
+		{DW_AT_name global_var}
+		{DW_AT_type :$struct_type}
+		{DW_AT_location {
+		    DW_OP_GNU_addr_index [gdb_target_symbol global_var]
+		} SPECIAL_expr}
+		{external 1 flag}
+	    }
+
+	    subprogram {
+		{external 1 flag}
+		{DW_AT_name main DW_FORM_string}
+		{MACRO_AT_func {main}}
+	    }
+	}
+    }
+
+    # The information that will remain in the .o file.
+    cu {} {
+	compile_unit {
+            {DW_AT_GNU_dwo_name ${binfile}.dwo DW_FORM_strp}
+	    {DW_AT_comp_dir ${objdir}}
+	    {DW_AT_GNU_dwo_id 0x1234 DW_FORM_data8}
+	    {DW_AT_GNU_addr_base $debug_addr_base}
+        } {
+	    # Nothing.
+	}
+    }
+}
+
+# Compile both source files to create the executable.  As we compile
+# ASM_FILE we split out the debug information into the dwo file.
+set object_file [standard_output_file ${testfile}.o]
+if { [build_executable_and_dwo_files "${testfile}.exp" ${binfile} {nodebug} \
+	  [list $asm_file {nodebug split-dwo} ${object_file}] \
+	  [list $srcfile {nodebug}]] } {
+    perror "failed to compile ${gdb_test_file_name}"
+    return -1
+}
+
+# Now we can start GDB.
+clean_restart ${testfile}
+
+if ![runto_main] {
+    return -1
+}
+
+# Print the type of global_var.  This type information is entirely
+# fictional, it only exists in the DWARF.  If we don't have the DWARF
+# information then there's no way we can print this.
+gdb_test "p global_var" " = \\{aa = 0, bb = 0, cc = 0\\}"
diff --git a/gdb/testsuite/gdb.dwarf2/fission-base.exp b/gdb/testsuite/gdb.dwarf2/fission-base.exp
index 40a7e2a4e3c..00eb7fec1b0 100644
--- a/gdb/testsuite/gdb.dwarf2/fission-base.exp
+++ b/gdb/testsuite/gdb.dwarf2/fission-base.exp
@@ -32,11 +32,12 @@  if {![istarget x86_64-*] || ![is_lp64_target]} {
 
 standard_testfile .S
 
-set dwo [standard_output_file "fission-base.dwo"]
-
-if [build_executable_from_fission_assembler \
-	"$testfile.exp" "$binfile" "$srcfile" \
-	[list nodebug additional_flags=-DDWO=$dwo]] {
+set obj [standard_output_file "${testfile}.o"]
+set dwo [standard_output_file "${testfile}.dwo"]
+if [build_executable_and_dwo_files "$testfile.exp" "${binfile}" {nodebug} \
+	[list $srcfile \
+	     [list nodebug split-dwo additional_flags=-DDWO=$dwo] \
+	     $obj]] {
     return -1
 }
 
diff --git a/gdb/testsuite/gdb.dwarf2/fission-loclists-pie.exp b/gdb/testsuite/gdb.dwarf2/fission-loclists-pie.exp
index c0a7785985b..21098a24624 100644
--- a/gdb/testsuite/gdb.dwarf2/fission-loclists-pie.exp
+++ b/gdb/testsuite/gdb.dwarf2/fission-loclists-pie.exp
@@ -37,11 +37,12 @@  if {![istarget x86_64-*] || ![is_lp64_target]} {
 
 standard_testfile .S
 
-set dwo [standard_output_file "fission-loclists-pie.dwo"]
-
-if [build_executable_from_fission_assembler \
-	"$testfile.exp" "$binfile" "$srcfile" \
-	[list "nodebug" "ldflags=-pie" additional_flags=-DDWO=$dwo]] {
+set obj [standard_output_file "${testfile}.o"]
+set dwo [standard_output_file "${testfile}.dwo"]
+if [build_executable_and_dwo_files "$testfile.exp" "${binfile}" \
+	{nodebug ldflags=-pie} \
+	[list $srcfile [list nodebug split-dwo additional_flags=-DDWO=$dwo] \
+	     $obj]] {
     return -1
 }
 
diff --git a/gdb/testsuite/gdb.dwarf2/fission-loclists.exp b/gdb/testsuite/gdb.dwarf2/fission-loclists.exp
index 54eace0496c..c490763982e 100644
--- a/gdb/testsuite/gdb.dwarf2/fission-loclists.exp
+++ b/gdb/testsuite/gdb.dwarf2/fission-loclists.exp
@@ -32,11 +32,12 @@  if {![istarget x86_64-*] || ![is_lp64_target]} {
 
 standard_testfile .S
 
-set dwo [standard_output_file "fission-loclists.dwo"]
-
-if [build_executable_from_fission_assembler \
-	"$testfile.exp" "$binfile" "$srcfile" \
-	[list nodebug additional_flags=-DDWO=$dwo]] {
+set obj [standard_output_file "${testfile}.o"]
+set dwo [standard_output_file "${testfile}.dwo"]
+if [build_executable_and_dwo_files "$testfile.exp" "${binfile}" {nodebug} \
+	[list $srcfile \
+	     [list nodebug split-dwo additional_flags=-DDWO=$dwo] \
+	     $obj]] {
     return -1
 }
 
diff --git a/gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp b/gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp
index dec754798b6..d3685f883dc 100644
--- a/gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp
+++ b/gdb/testsuite/gdb.dwarf2/fission-multi-cu.exp
@@ -32,11 +32,12 @@  if {![istarget x86_64-*] || ![is_lp64_target]} {
 
 standard_testfile .S
 
-set dwo [standard_output_file "fission-multi-cu.dwo"]
-
-if [build_executable_from_fission_assembler \
-	"$testfile.exp" "$binfile" "$srcfile" \
-	[list nodebug additional_flags=-DDWO=$dwo]] {
+set obj [standard_output_file "${testfile}.o"]
+set dwo [standard_output_file "${testfile}.dwo"]
+if [build_executable_and_dwo_files "$testfile.exp" "${binfile}" {nodebug} \
+	[list $srcfile \
+	     [list nodebug split-dwo additional_flags=-DDWO=$dwo] \
+	     $obj]] {
     return -1
 }
 
diff --git a/gdb/testsuite/gdb.dwarf2/fission-reread.exp b/gdb/testsuite/gdb.dwarf2/fission-reread.exp
index 58132794ee7..16a139eb0cd 100644
--- a/gdb/testsuite/gdb.dwarf2/fission-reread.exp
+++ b/gdb/testsuite/gdb.dwarf2/fission-reread.exp
@@ -30,17 +30,20 @@  set additional_flags [gdb_target_symbol_prefix_flags_asm]
 
 standard_testfile .S
 
-set dwo [standard_output_file "fission-reread.dwo"]
+set obj [standard_output_file "${testfile}.o"]
+set dwo [standard_output_file "${testfile}.dwo"]
 
-set options [list]
-lappend options nodebug
+set options [list nodebug]
 if { $additional_flags != "" } {
     lappend options $additional_flags
 }
-lappend options additional_flags=-DDWO=$dwo
 
-if [build_executable_from_fission_assembler \
-	"$testfile.exp" "$binfile" "$srcfile" $options] {
+set dwo_options $options
+lappend dwo_options split-dwo
+lappend dwo_options additional_flags=-DDWO=$dwo
+
+if [build_executable_and_dwo_files "$testfile.exp" "${binfile}" $options \
+	[list $srcfile $dwo_options $obj]] {
     return -1
 }
 
diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
index 4cd5e16c604..d30a6c965d0 100644
--- a/gdb/testsuite/lib/dwarf.exp
+++ b/gdb/testsuite/lib/dwarf.exp
@@ -28,39 +28,12 @@  proc dwarf2_support {} {
     return 0
 }
 
-# Build an executable from a fission-based .S file.
-# This handles the extra work of splitting the .o into non-dwo and dwo
-# pieces, making sure the .dwo is available if we're using cc-with-tweaks.sh
-# to build a .dwp file.
-# The arguments and results are the same as for build_executable.
+# Use 'objcopy --extract-dwo to extract DWO information from
+# OBJECT_FILE and place it into DWO_FILE.
 #
-# Current restrictions:
-# - only supports one source file
-# - cannot be run on remote hosts
-
-proc build_executable_from_fission_assembler { testname executable sources options } {
-    verbose -log "build_executable_from_fission_assembler $testname $executable $sources $options"
-    if { [llength $sources] != 1 } {
-	error "Only one source file supported."
-    }
-    if [is_remote host] {
-	error "Remote hosts are not supported."
-    }
-
-    global srcdir subdir
-    set source_file ${srcdir}/${subdir}/${sources}
-    set root_name [file rootname [file tail $source_file]]
-    set output_base [standard_output_file $root_name]
-    set object_file ${output_base}.o
-    set dwo_file ${output_base}.dwo
-    set object_options "object $options"
+# Return 0 on success, otherwise, return -1.
+proc extract_dwo_information { object_file dwo_file } {
     set objcopy [gdb_find_objcopy]
-
-    set result [gdb_compile $source_file $object_file object $options]
-    if { "$result" != "" } {
-	return -1
-    }
-
     set command "$objcopy --extract-dwo $object_file $dwo_file"
     verbose -log "Executing $command"
     set result [catch "exec $command" output]
@@ -68,20 +41,154 @@  proc build_executable_from_fission_assembler { testname executable sources optio
     if { $result == 1 } {
 	return -1
     }
+    return 0
+}
 
-    set command "$objcopy --strip-dwo $object_file"
+# Use 'objcopy --strip-dwo to remove DWO information from
+# FILENAME.
+#
+# Return 0 on success, otherwise, return -1.
+proc strip_dwo_information { filename } {
+    set objcopy [gdb_find_objcopy]
+    set command "$objcopy --strip-dwo $filename"
     verbose -log "Executing $command"
     set result [catch "exec $command" output]
     verbose -log "objcopy --strip-dwo output: $output"
     if { $result == 1 } {
 	return -1
     }
+    return 0
+}
+
+# Build an executable, with the debug information split out into a
+# separate .dwo file.
+#
+# This function is based on build_executable_from_specs in
+# lib/gdb.exp, but with threading support, and rust support removed.
+#
+# TESTNAME is the name of the test; this is passed to 'untested' if
+# something fails.
+#
+# EXECUTABLE is the executable to create, this can be an absolute
+# path, or a relative path, in which case the EXECUTABLE will be
+# created in the standard output directory.
+#
+# OPTIONS is passed to the final link, using gdb_compile.  If OPTIONS
+# contains any option that indicates threads is required, of if the
+# option rust is included, then this function will return failure.
+#
+# ARGS is a series of lists.  Each list is a spec for one source file
+# that will be compiled to make EXECUTABLE.  Each spec in ARGS has the
+# form:
+#	[ SOURCE OPTIONS ]
+# or:
+#       [ SOURCE OPTIONS OBJFILE ]
+#
+# Where SOURCE is the path to the source file to compile.  This can be
+# absolute, or relative to the standard global ${subdir}/${srcdir}/
+# path.
+#
+# OPTIONS are the options to use when compiling SOURCE into an object
+# file.
+#
+# OBJFILE is optional, if present this is the name of the object file
+# to create for SOURCE.  If this is not provided then a suitable name
+# will be auto-generated.
+#
+# If OPTIONS contains the option 'split-dwo' then the debug
+# information is extracted from the object file created by compiling
+# SOURCE and placed into a file with a dwo extension.  The name of
+# this file is generated based on the name of the object file that was
+# created (with the .o replaced with .dwo).
+proc build_executable_and_dwo_files { testname executable options args } {
+    global subdir
+    global srcdir
+
+    if { ! [regexp "^/" "$executable"] } then {
+	set binfile [standard_output_file $executable]
+    } else {
+	set binfile $executable
+    }
+
+    set info_options ""
+    if { [lsearch -exact $options "c++"] >= 0 } {
+	set info_options "c++"
+    }
+    if [get_compiler_info ${info_options}] {
+        return -1
+    }
+
+    set func gdb_compile
+    if {[lsearch -regexp $options \
+	     {^(pthreads|shlib|shlib_pthreads|openmp)$}] != -1} {
+	# Currently don't support compiling thread based tests here.
+	# If this is required then look to build_executable_from_specs
+	# for inspiration.
+	return -1
+    }
+    if {[lsearch -exact $options rust] != -1} {
+	# Currently don't support compiling rust tests here.  If this
+	# is required then look to build_executable_from_specs for
+	# inspiration.
+	return -1
+    }
 
-    set result [gdb_compile $object_file $executable executable $options]
-    if { "$result" != "" } {
+    # Must be run on local host due to use of objcopy.
+    if [is_remote host] {
 	return -1
     }
 
+    set objects {}
+    set i 0
+    foreach spec $args {
+	if {[llength $spec] < 2} {
+	    error "invalid spec length"
+	    return -1
+	}
+
+	set s [lindex $spec 0]
+	set local_options [lindex $spec 1]
+
+	if { ! [regexp "^/" "$s"] } then {
+	    set s "$srcdir/$subdir/$s"
+	}
+
+	if {[llength $spec] > 2} {
+	    set objfile [lindex $spec 2]
+	} else {
+	    set objfile "${binfile}${i}.o"
+	    incr i
+	}
+
+	if  { [$func "${s}" "${objfile}" object $local_options] != "" } {
+	    untested $testname
+	    return -1
+	}
+
+	lappend objects "$objfile"
+
+	if {[lsearch -exact $local_options "split-dwo"] >= 0} {
+	    # Split out the DWO file.
+	    set dwo_file "[file rootname ${objfile}].dwo"
+
+	    if { [extract_dwo_information $objfile $dwo_file] == -1 } {
+		untested $testname
+		return -1
+	    }
+
+	    if { [strip_dwo_information $objfile] == -1 } {
+		untested $testname
+		return -1
+	    }
+	}
+    }
+
+    set ret [$func $objects "${binfile}" executable $options]
+    if  { $ret != "" } {
+        untested $testname
+        return -1
+    }
+
     return 0
 }
 
@@ -329,6 +436,14 @@  namespace eval Dwarf {
     # The address size for debug ranges section.
     variable _debug_ranges_64_bit
 
+    # The index into the .debug_addr section (used for fission
+    # generation).
+    variable _debug_addr_index
+
+    # Flag, true if the current CU is contains fission information,
+    # otherwise false.
+    variable _cu_is_fission
+
     proc _process_one_constant {name value} {
 	variable _constants
 	variable _AT
@@ -486,6 +601,18 @@  namespace eval Dwarf {
 		_op .${_cu_addr_size}byte $value
 	    }
 
+	    DW_FORM_GNU_addr_index {
+		variable _debug_addr_index
+		variable _cu_addr_size
+
+		_op .uleb128 ${_debug_addr_index}
+		incr _debug_addr_index
+
+		_defer_output .debug_addr {
+		    _op .${_cu_addr_size}byte $value
+		}
+	    }
+
 	    DW_FORM_data2 -
 	    DW_FORM_ref2 {
 		_op .2byte $value
@@ -553,7 +680,6 @@  namespace eval Dwarf {
 	    DW_FORM_strx3 -
 	    DW_FORM_strx4 -
 
-	    DW_FORM_GNU_addr_index -
 	    DW_FORM_GNU_str_index -
 
 	    default {
@@ -609,6 +735,9 @@  namespace eval Dwarf {
 	    DW_AT_name {
 		return DW_FORM_string
 	    }
+	    DW_AT_GNU_addr_base {
+		return DW_FORM_sec_offset
+	    }
 	}
 	return ""
     }
@@ -649,6 +778,8 @@  namespace eval Dwarf {
     # Handle macro attribute MACRO_AT_range.
 
     proc _handle_macro_at_range { attr_value } {
+	variable _cu_is_fission
+
 	if {[llength $attr_value] != 1} {
 	    error "usage: MACRO_AT_range { func }"
 	}
@@ -658,10 +789,14 @@  namespace eval Dwarf {
 	set src ${srcdir}/${subdir}/${srcfile}
 	set result [function_range $func $src]
 
-	_handle_attribute DW_AT_low_pc [lindex $result 0] \
-	    DW_FORM_addr
+	set form DW_FORM_addr
+	if { $_cu_is_fission } {
+	    set form DW_FORM_GNU_addr_index
+	}
+
+	_handle_attribute DW_AT_low_pc [lindex $result 0] $form
 	_handle_attribute DW_AT_high_pc \
-	    "[lindex $result 0] + [lindex $result 1]" DW_FORM_addr
+	    "[lindex $result 0] + [lindex $result 1]" $form
     }
 
     # Handle macro attribute MACRO_AT_func.
@@ -929,6 +1064,18 @@  namespace eval Dwarf {
 		    _op .${addr_size}byte [lindex $line 1]
 		}
 
+		DW_OP_GNU_addr_index {
+		    variable _debug_addr_index
+		    variable _cu_addr_size
+
+		    _op .uleb128 ${_debug_addr_index}
+		    incr _debug_addr_index
+
+		    _defer_output .debug_addr {
+			_op .${_cu_addr_size}byte [lindex $line 1]
+		    }
+		}
+
 		DW_OP_regx {
 		    _op .uleb128 [lindex $line 1]
 		}
@@ -1050,6 +1197,14 @@  namespace eval Dwarf {
 	}
     }
 
+    # Return the current value of _DEBUG_ADDR_INDEX.  The return value
+    # of this function can be used for the value of
+    # DW_AT_GNU_addr_base.
+    proc debug_addr_index {} {
+	variable _debug_addr_index
+	return ${_debug_addr_index}
+    }
+
     # Emit a DWARF CU.
     # OPTIONS is a list with an even number of elements containing
     # option-name and option-value pairs.
@@ -1073,12 +1228,13 @@  namespace eval Dwarf {
 	variable _cu_version
 	variable _cu_addr_size
 	variable _cu_offset_size
+	variable _cu_is_fission
 
 	# Establish the defaults.
 	set is_64 0
 	set _cu_version 4
 	set _cu_addr_size default
-	set fission 0
+	set _cu_is_fission 0
 	set section ".debug_info"
 	set _abbrev_section ".debug_abbrev"
 
@@ -1088,7 +1244,7 @@  namespace eval Dwarf {
 		is_64 { set is_64 $value }
 		version { set _cu_version $value }
 		addr_size { set _cu_addr_size $value }
-		fission { set fission $value }
+		fission { set _cu_is_fission $value }
 		default { error "unknown option $name" }
 	    }
 	}
@@ -1100,7 +1256,7 @@  namespace eval Dwarf {
 	    }
 	}
 	set _cu_offset_size [expr { $is_64 ? 8 : 4 }]
-	if { $fission } {
+	if { $_cu_is_fission } {
 	    set section ".debug_info.dwo"
 	    set _abbrev_section ".debug_abbrev.dwo"
 	}
@@ -1180,12 +1336,13 @@  namespace eval Dwarf {
 	variable _cu_version
 	variable _cu_addr_size
 	variable _cu_offset_size
+	variable _cu_is_fission
 
 	# Establish the defaults.
 	set is_64 0
 	set _cu_version 4
 	set _cu_addr_size default
-	set fission 0
+	set _cu_is_fission 0
 	set section ".debug_types"
 	set _abbrev_section ".debug_abbrev"
 
@@ -1194,7 +1351,7 @@  namespace eval Dwarf {
 		is_64 { set is_64 $value }
 		version { set _cu_version $value }
 		addr_size { set _cu_addr_size $value }
-		fission { set fission $value }
+		fission { set _cu_is_fission $value }
 		default { error "unknown option $name" }
 	    }
 	}
@@ -1206,7 +1363,7 @@  namespace eval Dwarf {
 	    }
 	}
 	set _cu_offset_size [expr { $is_64 ? 8 : 4 }]
-	if { $fission } {
+	if { $_cu_is_fission } {
 	    set section ".debug_types.dwo"
 	    set _abbrev_section ".debug_abbrev.dwo"
 	}
@@ -2056,6 +2213,7 @@  namespace eval Dwarf {
 	variable _line_saw_program
 	variable _line_header_end_label
 	variable _debug_ranges_64_bit
+	variable _debug_addr_index
 
 	if {!$_initialized} {
 	    _read_constants
@@ -2074,6 +2232,8 @@  namespace eval Dwarf {
 	set _line_saw_program 0
 	set _debug_ranges_64_bit [is_64_target]
 
+	set _debug_addr_index 0
+
 	# Not "uplevel" here, because we want to evaluate in this
 	# namespace.  This is somewhat bad because it means we can't
 	# readily refer to outer variables.