[llvm] 4539b44 - [Reland][Debuginfo][llvm-dwarfutil] llvm-dwarfutil dsymutil-like tool for ELF.
Alexey Lapshin via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 19 05:16:33 PDT 2022
Author: Alexey Lapshin
Date: 2022-07-19T15:11:36+03:00
New Revision: 4539b44148918c332ff966a2a955aca0ab93dde4
URL: https://github.com/llvm/llvm-project/commit/4539b44148918c332ff966a2a955aca0ab93dde4
DIFF: https://github.com/llvm/llvm-project/commit/4539b44148918c332ff966a2a955aca0ab93dde4.diff
LOG: [Reland][Debuginfo][llvm-dwarfutil] llvm-dwarfutil dsymutil-like tool for ELF.
This patch implements proposal https://lists.llvm.org/pipermail/llvm-dev/2020-August/144579.html
llvm-dwarfutil - is a tool that is used for processing debug info(DWARF) located in built binary files to improve debug info quality, reduce debug info size. The patch currently implements smaller set of command-line options(comparing to the proposal):
```
./llvm-dwarfutil [options] <input file> <output file>
--garbage-collection Do garbage collection for debug info(default)
-j <value> Alias for --num-threads
--no-garbage-collection Don`t do garbage collection for debug info
--no-odr-deduplication Don`t do ODR deduplication for debug types
--no-odr Alias for --no-odr-deduplication
--no-separate-debug-file
Create single output file, containing debug tables(default)
--num-threads <threads> Number of available threads for multi-threaded execution. Defaults to the number of cores on the current machine
--odr-deduplication Do ODR deduplication for debug types(default)
--odr Alias for --odr-deduplication
--separate-debug-file Create two output files: file w/o debug tables and file with debug tables
--tombstone [bfd,maxpc,exec,universal]
Tombstone value used as a marker of invalid address(default: universal)
=bfd - Zero for all addresses and [1,1] for DWARF v4 (or less) address ranges and exec
=maxpc - Minus 1 for all addresses and minus 2 for DWARF v4 (or less) address ranges
=exec - Match with address ranges of executable sections
=universal - Both: bfd and maxpc
```
Reviewed By: clayborg
Differential Revision: https://reviews.llvm.org/D86539
Added:
llvm/docs/CommandGuide/llvm-dwarfutil.rst
llvm/test/tools/llvm-dwarfutil/ELF/X86/Inputs/common.yaml
llvm/test/tools/llvm-dwarfutil/ELF/X86/copy-itself.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/copy.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/error-separate-file-stdout.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-bfd.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-class.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-default.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-exec.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-maxpc.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-no-garbage.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/lit.local.cfg
llvm/test/tools/llvm-dwarfutil/ELF/X86/odr-fwd-declaration.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/separate-debug-file.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/verbose.test
llvm/test/tools/llvm-dwarfutil/ELF/X86/verify.test
llvm/test/tools/llvm-dwarfutil/error-invalid-format.test
llvm/test/tools/llvm-dwarfutil/error-no-gc-odr.test
llvm/test/tools/llvm-dwarfutil/error-no-input-file.test
llvm/test/tools/llvm-dwarfutil/error-positional-args.test
llvm/test/tools/llvm-dwarfutil/error-unknown-option.test
llvm/test/tools/llvm-dwarfutil/error-unknown-tombstone.test
llvm/test/tools/llvm-dwarfutil/error-unsupported-input-file.test
llvm/test/tools/llvm-dwarfutil/help.test
llvm/tools/llvm-dwarfutil/CMakeLists.txt
llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp
llvm/tools/llvm-dwarfutil/DebugInfoLinker.h
llvm/tools/llvm-dwarfutil/Error.h
llvm/tools/llvm-dwarfutil/Options.h
llvm/tools/llvm-dwarfutil/Options.td
llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp
Modified:
llvm/docs/CommandGuide/index.rst
llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h
llvm/lib/DWARFLinker/DWARFLinker.cpp
llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp
llvm/test/CMakeLists.txt
llvm/test/lit.cfg.py
Removed:
################################################################################
diff --git a/llvm/docs/CommandGuide/index.rst b/llvm/docs/CommandGuide/index.rst
index ec5d6921386b1..da9995a5a3e4f 100644
--- a/llvm/docs/CommandGuide/index.rst
+++ b/llvm/docs/CommandGuide/index.rst
@@ -24,6 +24,7 @@ Basic Commands
llvm-
diff
llvm-dis
llvm-dwarfdump
+ llvm-dwarfutil
llvm-lib
llvm-libtool-darwin
llvm-link
diff --git a/llvm/docs/CommandGuide/llvm-dwarfutil.rst b/llvm/docs/CommandGuide/llvm-dwarfutil.rst
new file mode 100644
index 0000000000000..98858deccf742
--- /dev/null
+++ b/llvm/docs/CommandGuide/llvm-dwarfutil.rst
@@ -0,0 +1,122 @@
+llvm-dwarfutil - A tool to copy and manipulate debug info
+=========================================================
+
+.. program:: llvm-dwarfutil
+
+SYNOPSIS
+--------
+
+:program:`llvm-dwarfutil` [*options*] *input* *output*
+
+DESCRIPTION
+-----------
+
+:program:`llvm-dwarfutil` is a tool to copy and manipulate debug info.
+
+In basic usage, it makes a semantic copy of the input to the output. If any
+options are specified, the output may be modified along the way, e.g.
+by removing unused debug info.
+
+If "-" is specified for the input file, the input is read from the program's
+standard input stream. If "-" is specified for the output file, the output
+is written to the standard output stream of the program.
+
+The tool is still in active development.
+
+COMMAND-LINE OPTIONS
+--------------------
+
+.. option:: --garbage-collection
+
+ Removes pieces of debug information related to discarded sections.
+ When the linker does section garbage collection the abandoned debug info
+ is left behind. Such abandoned debug info references address ranges using
+ tombstone values. Thus, when this option is specified, the tool removes
+ debug info which is marked with the tombstone value.
+
+ That option is enabled by default.
+
+.. option:: --odr-deduplication
+
+ Remove duplicated types (if "One Definition Rule" is supported by source
+ language). Keeps first type definition and removes other definitions,
+ potentially significantly reducing the size of output debug info.
+
+ That option is enabled by default.
+
+.. option:: --help, -h
+
+ Print a summary of command line options.
+
+.. option:: --no-garbage-collection
+
+ Disable :option:`--garbage-collection`.
+
+.. option:: --no-odr-deduplication
+
+ Disable :option:`--odr-deduplication`.
+
+.. option:: --no-separate-debug-file
+
+ Disable :option:`--separate-debug-file`.
+
+.. option:: --num-threads=<n>, -j
+
+ Specifies the maximum number (`n`) of simultaneous threads to use
+ for processing.
+
+.. option:: --separate-debug-file
+
+ Generate separate file containing output debug info. Using
+ :program:`llvm-dwarfutil` with that option equals to the
+ following set of commands:
+
+.. code-block:: console
+
+ :program:`llvm-objcopy` --only-keep-debug in-file out-file.debug
+ :program:`llvm-objcopy` --strip-debug in-file out-file
+ :program:`llvm-objcopy` --add-gnu-debuglink=out-file.debug out-file
+
+.. option:: --tombstone=<value>
+
+ <value> can be one of the following values:
+
+ - `bfd`: zero for all addresses and [1,1] for DWARF v4 (or less) address ranges and exec.
+
+ - `maxpc`: -1 for all addresses and -2 for DWARF v4 (or less) address ranges.
+
+ - `universal`: both `bfd` and `maxpc`.
+
+ - `exec`: match with address ranges of executable sections.
+
+ The value `universal` is used by default.
+
+.. option:: --verbose
+
+ Enable verbose logging. This option disables multi-thread mode.
+
+.. option:: --verify
+
+ Run the DWARF verifier on the output DWARF debug info.
+
+.. option:: --version
+
+ Print the version of this program.
+
+SUPPORTED FORMATS
+-----------------
+
+The following formats are currently supported by :program:`llvm-dwarfutil`:
+
+ELF
+
+EXIT STATUS
+-----------
+
+:program:`llvm-dwarfutil` exits with a non-zero exit code if there is an error.
+Otherwise, it exits with code 0.
+
+BUGS
+----
+
+To report bugs, please visit <https://github.com/llvm/llvm-project/labels/tools:llvm-dwarfutil/>.
diff --git a/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h b/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h
index 788275782235c..930db0913226a 100644
--- a/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h
+++ b/llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h
@@ -182,6 +182,10 @@ class CompileUnit {
/// offset \p PCOffset.
void addFunctionRange(uint64_t LowPC, uint64_t HighPC, int64_t PCOffset);
+ /// Check whether specified address range \p LowPC \p HighPC
+ /// overlaps with existing function ranges.
+ bool overlapsWithFunctionRanges(uint64_t LowPC, uint64_t HighPC);
+
/// Keep track of a DW_AT_range attribute that we will need to patch up later.
void noteRangeAttribute(const DIE &Die, PatchLocation Attr);
diff --git a/llvm/lib/DWARFLinker/DWARFLinker.cpp b/llvm/lib/DWARFLinker/DWARFLinker.cpp
index 298359dea9af5..47cce9ed67778 100644
--- a/llvm/lib/DWARFLinker/DWARFLinker.cpp
+++ b/llvm/lib/DWARFLinker/DWARFLinker.cpp
@@ -505,6 +505,19 @@ unsigned DWARFLinker::shouldKeepSubprogramDIE(
return Flags;
}
+ // TODO: Following check is a workaround for overlapping address ranges.
+ // ELF binaries built with LTO might contain overlapping address
+ // ranges. The better fix would be to combine such ranges. Following
+ // is a workaround that should be removed when a good fix is done.
+ if (Unit.overlapsWithFunctionRanges(*LowPc, *HighPc)) {
+ reportWarning(
+ formatv("Overlapping address range [{0:X}, {1:X}]. Range will "
+ "be discarded.\n",
+ *LowPc, *HighPc),
+ File, &DIE);
+ return Flags;
+ }
+
// Replace the debug map range with a more accurate one.
Ranges[*LowPc] = ObjFileAddressRange(*HighPc, MyInfo.AddrAdjust);
Unit.addFunctionRange(*LowPc, *HighPc, MyInfo.AddrAdjust);
diff --git a/llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp b/llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp
index e9e8be7fd0083..ebb1106521cc6 100644
--- a/llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp
+++ b/llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp
@@ -114,6 +114,10 @@ void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc,
this->HighPc = std::max(HighPc, FuncHighPc + PcOffset);
}
+bool CompileUnit::overlapsWithFunctionRanges(uint64_t LowPC, uint64_t HighPC) {
+ return Ranges.overlaps(LowPC, HighPC);
+}
+
void CompileUnit::noteRangeAttribute(const DIE &Die, PatchLocation Attr) {
if (Die.getTag() != dwarf::DW_TAG_compile_unit)
RangeAttributes.push_back(Attr);
diff --git a/llvm/test/CMakeLists.txt b/llvm/test/CMakeLists.txt
index b94681f54b7a7..86ca20ada7b80 100644
--- a/llvm/test/CMakeLists.txt
+++ b/llvm/test/CMakeLists.txt
@@ -83,6 +83,7 @@ set(LLVM_TEST_DEPENDS
llvm-dlltool
dsymutil
llvm-dwarfdump
+ llvm-dwarfutil
llvm-dwp
llvm-exegesis
llvm-extract
diff --git a/llvm/test/lit.cfg.py b/llvm/test/lit.cfg.py
index 97079af853a56..93db320610dcb 100644
--- a/llvm/test/lit.cfg.py
+++ b/llvm/test/lit.cfg.py
@@ -160,8 +160,8 @@ def get_asan_rtlib():
'dsymutil', 'lli', 'lli-child-target', 'llvm-ar', 'llvm-as',
'llvm-addr2line', 'llvm-bcanalyzer', 'llvm-bitcode-strip', 'llvm-config',
'llvm-cov', 'llvm-cxxdump', 'llvm-cvtres', 'llvm-debuginfod-find', 'llvm-debuginfod',
- 'llvm-
diff ', 'llvm-dis', 'llvm-dwarfdump', 'llvm-dlltool', 'llvm-exegesis',
- 'llvm-extract', 'llvm-isel-fuzzer', 'llvm-ifs',
+ 'llvm-
diff ', 'llvm-dis', 'llvm-dwarfdump', 'llvm-dwarfutil', 'llvm-dlltool',
+ 'llvm-exegesis', 'llvm-extract', 'llvm-isel-fuzzer', 'llvm-ifs',
'llvm-install-name-tool', 'llvm-jitlink', 'llvm-opt-fuzzer', 'llvm-lib',
'llvm-link', 'llvm-lto', 'llvm-lto2', 'llvm-mc', 'llvm-mca',
'llvm-modextract', 'llvm-nm', 'llvm-objcopy', 'llvm-objdump', 'llvm-otool',
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/Inputs/common.yaml b/llvm/test/tools/llvm-dwarfutil/ELF/X86/Inputs/common.yaml
new file mode 100644
index 0000000000000..0bfe09056ed50
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/Inputs/common.yaml
@@ -0,0 +1,137 @@
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x1000
+ Size: 0x1b
+DWARF:
+ debug_abbrev:
+ - Table:
+ - Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_producer
+ Form: DW_FORM_string
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_member
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_template_type_parameter
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_pointer_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_variable
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_const_value
+ Form: DW_FORM_data4
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ debug_info:
+ - Version: 4
+ Entries:
+ - AbbrCode: 1
+ Values:
+ - CStr: by_hand
+ - Value: 0x04
+ - CStr: CU1
+ - Value: 0x1000
+ - Value: 0x1b
+ - AbbrCode: 2
+ Values:
+ - CStr: class1
+ - AbbrCode: 3
+ Values:
+ - Value: 0x00000052
+ - CStr: member1
+ - AbbrCode: 3
+ Values:
+ - Value: 0x00000058
+ - CStr: member2
+ - AbbrCode: 0
+ - AbbrCode: 7
+ Values:
+ - CStr: int
+ - AbbrCode: 7
+ Values:
+ - CStr: char
+ - AbbrCode: 7
+ Values:
+ - CStr: float
+ - AbbrCode: 8
+ Values:
+ - Value: 0x0000002a
+ - AbbrCode: 9
+ Values:
+ - CStr: var1
+ - Value: 0x00000000
+ - Value: 0x0000005f
+ - AbbrCode: 10
+ Values:
+ - CStr: foo1
+ - Value: 0x1000
+ - Value: 0x10
+ - Value: 0x0000002a
+ - AbbrCode: 0
+ - AbbrCode: 0
+...
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/copy-itself.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/copy-itself.test
new file mode 100644
index 0000000000000..c1934f9d14618
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/copy-itself.test
@@ -0,0 +1,37 @@
+## This test checks that the debug info contained in the source file
+## is fully copied to the destination file when the source and
+## destination are the same file.
+
+## Check --no-separate-debug-file.
+# RUN: yaml2obj %p/Inputs/common.yaml -o %t1
+# RUN: llvm-dwarfutil --no-separate-debug-file %t1 %t1
+# RUN: llvm-dwarfdump -a %t1 | FileCheck --check-prefix=CHECK-DEBUG %s
+
+## Check --separate-debug-file.
+# RUN: yaml2obj %p/Inputs/common.yaml -o %t1
+# RUN: llvm-dwarfutil --separate-debug-file %t1 %t1
+# RUN: llvm-objdump --headers %t1 | FileCheck --check-prefix=CHECK-NON-DEBUG %s
+# RUN: llvm-dwarfdump -a %t1.debug | FileCheck --check-prefix=CHECK-DEBUG %s
+
+# CHECK-NON-DEBUG-NOT: .debug_abbrev
+# CHECK-NON-DEBUG-NOT: .debug_info
+# CHECK-NON-DEBUG: .gnu_debuglink
+# CHECK-NON-DEBUG-NOT: .debug_abbrev
+# CHECK-NON-DEBUG-NOT: .debug_info
+
+# CHECK-DEBUG: .debug_abbrev
+# CHECK-DEBUG: DW_TAG_compile_unit
+# CHECK-DEBUG: .debug_info
+# CHECK-DEBUG: DW_TAG_compile_unit
+# CHECK-DEBUG: DW_AT_producer{{.*}}"by_hand"
+# CHECK-DEBUG: DW_AT_language{{.*}}DW_LANG_C_plus_plus
+# CHECK-DEBUG: DW_AT_name{{.*}}"CU1"
+# CHECK-DEBUG: DW_TAG_class_type
+# CHECK-DEBUG: DW_AT_name{{.*}}"class1"
+# CHECK-DEBUG: DW_TAG_base_type
+# CHECK-DEBUG: DW_AT_name{{.*}}"int"
+# CHECK-DEBUG: DW_AT_name{{.*}}"char"
+# CHECK-DEBUG: DW_AT_name{{.*}}"float"
+# CHECK-DEBUG: DW_TAG_pointer_type
+# CHECK-DEBUG: DW_TAG_variable
+# CHECK-DEBUG: DW_AT_name{{.*}}"var1"
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/copy.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/copy.test
new file mode 100644
index 0000000000000..8385813bbe549
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/copy.test
@@ -0,0 +1,34 @@
+## This test checks that debug info contained in the source file
+## is fully copied to the destination file in case --no-garbage-collection
+## is specified.
+
+# RUN: yaml2obj %p/Inputs/common.yaml -o %t.o
+
+## Check that the resulting file contains debug info from source file.
+# RUN: llvm-dwarfutil --no-garbage-collection %t.o %t1
+# RUN: llvm-dwarfdump -a %t1 | FileCheck %s
+
+## Check that the second copy matches with the first.
+# RUN: llvm-dwarfutil --no-garbage-collection %t1 %t2
+# RUN: cmp %t1 %t2
+
+## Check that input file passed through <stdin> is correctly processesed.
+# RUN: llvm-dwarfutil --no-garbage-collection - %t2 < %t1
+# RUN: cmp %t1 %t2
+
+# CHECK: .debug_abbrev
+# CHECK: DW_TAG_compile_unit
+# CHECK: .debug_info
+# CHECK: DW_TAG_compile_unit
+# CHECK: DW_AT_producer{{.*}}"by_hand"
+# CHECK: DW_AT_language{{.*}}DW_LANG_C_plus_plus
+# CHECK: DW_AT_name{{.*}}"CU1"
+# CHECK: DW_TAG_class_type
+# CHECK: DW_AT_name{{.*}}"class1"
+# CHECK: DW_TAG_base_type
+# CHECK: DW_AT_name{{.*}}"int"
+# CHECK: DW_AT_name{{.*}}"char"
+# CHECK: DW_AT_name{{.*}}"float"
+# CHECK: DW_TAG_pointer_type
+# CHECK: DW_TAG_variable
+# CHECK: DW_AT_name{{.*}}"var1"
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/error-separate-file-stdout.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/error-separate-file-stdout.test
new file mode 100644
index 0000000000000..07492a495a9e3
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/error-separate-file-stdout.test
@@ -0,0 +1,7 @@
+## This test checks the error message displayed if the destination
+## is stdout and separate debug info file is requested.
+
+# RUN: yaml2obj %p/Inputs/common.yaml -o %t.o
+# RUN: not llvm-dwarfutil --separate-debug-file %t.o - 2>&1 | FileCheck --check-prefix CHECK %s
+
+# CHECK: error: unable to write to stdout when --separate-debug-file specified
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-bfd.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-bfd.test
new file mode 100644
index 0000000000000..7e61036ae27de
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-bfd.test
@@ -0,0 +1,150 @@
+## This test checks that debug info related to deleted code (marked with
+## tombstone=bfd) is removed.
+
+# RUN: yaml2obj %s -o %t.o
+
+# RUN: llvm-dwarfutil --tombstone=bfd --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s
+# RUN: llvm-dwarfutil --tombstone=universal --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s
+
+# CHECK: DW_TAG_compile_unit
+# CHECK: DW_AT_name{{.*}}"CU1"
+# CHECK: DW_TAG_class_type
+# CHECK: DW_AT_name{{.*}}"class1"
+# CHECK-NOT: DW_TAG_class_type
+# CHECK-NOT: "class2"
+# CHECK-NOT: "class3"
+# CHECK: DW_TAG_subprogram
+# CHECK: DW_AT_name{{.*}}"foo1"
+# CHECK: DW_AT_low_pc{{.*}}0x0000000000001000
+# CHECK: DW_AT_high_pc{{.*}}0x0000000000001010
+# CHECK: DW_AT_type{{.*}}"class1"
+# CHECK-NOT: DW_TAG_subprogram
+# CHECK-NOT: "foo2"
+
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x1000
+ Size: 0x1b
+DWARF:
+ debug_abbrev:
+ - Table:
+ - Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_producer
+ Form: DW_FORM_string
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_member
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_template_type_parameter
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ debug_info:
+ - Version: 4
+ Entries:
+ - AbbrCode: 1
+ Values:
+ - CStr: by_hand
+ - Value: 0x04
+ - CStr: CU1
+ - Value: 0x1000
+ - Value: 0x1b
+ - AbbrCode: 3
+ Values:
+ - CStr: class1
+ - AbbrCode: 4
+ Values:
+ - Value: 0x0000006c
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 3
+ Values:
+ - CStr: class2
+ - AbbrCode: 4
+ Values:
+ - Value: 0x0000006c
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 3
+ Values:
+ - CStr: class3
+ - AbbrCode: 4
+ Values:
+ - Value: 0x0000006c
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 8
+ Values:
+ - CStr: int
+ - AbbrCode: 2
+ Values:
+ - CStr: foo1
+ - Value: 0x1000
+ - Value: 0x10
+ - Value: 0x0000002a
+ - AbbrCode: 2
+ Values:
+ - CStr: foo2
+ - Value: 0x0
+ - Value: 0x100
+ - Value: 0x00000040
+ - AbbrCode: 0
+...
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-class.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-class.test
new file mode 100644
index 0000000000000..92797e972597e
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-class.test
@@ -0,0 +1,344 @@
+## This test checks debug info for the case when one compilation unit
+## contains a definition of a type and another compilation unit
+## contains a definition of the same type. When --garbage-collection
+## is enabled the result should contain the original definition in CU1
+## and the definition from CU2 should have been removed.
+
+# RUN: yaml2obj %s -o %t.o
+
+## Check --odr-deduplication.
+# RUN: llvm-dwarfutil %t.o %t1
+# RUN: llvm-dwarfdump -a %t1 | FileCheck %s --check-prefixes=CHECK,CHECK-ODR
+
+## Check --odr alias.
+# RUN: llvm-dwarfutil --odr %t.o %t2
+# RUN: cmp %t1 %t2
+
+## Check --no-odr alias.
+# RUN: llvm-dwarfutil --no-odr %t.o %t3
+# RUN: llvm-dwarfdump -a %t3 | FileCheck %s --check-prefixes=CHECK,CHECK-NOODR
+
+## Check --no-odr-deduplication wins if last.
+# RUN: llvm-dwarfutil --odr-deduplication --no-odr-deduplication %t.o %t4
+# RUN: cmp %t3 %t4
+
+## Check --odr-deduplication wins if last.
+# RUN: llvm-dwarfutil --no-odr-deduplication --odr-deduplication %t.o %t6
+# RUN: cmp %t1 %t6
+
+## CU1:
+##
+## class class1 {
+## char member1;
+## float member2;
+## };
+##
+## class1 *var1;
+##
+## CU2:
+##
+## class class1 {
+## char member1;
+## float member2;
+## };
+##
+## class1 *var1;
+
+# CHECK: file format elf64-x86-64
+# CHECK: .debug_info contents:
+
+# CHECK: DW_TAG_compile_unit
+# CHECK: DW_AT_name{{.*}}"CU1"
+
+# CHECK: 0x[[CU1_CLASS1:[0-9a-f]*]]: DW_TAG_class_type{{.*[[:space:]].*}}DW_AT_name{{.*}}"class1"
+# CHECK: DW_TAG_member
+# CHECK-ODR: DW_AT_type{{.*}}0x00000000[[CU1_CHAR:[0-9a-f]*]] "char"
+# CHECK-NOODR: DW_AT_type{{.*}}0x[[CU1_CHAR:[0-9a-f]*]] "char"
+# CHECK: DW_AT_name{{.*}}"member1"
+
+# CHECK: DW_TAG_member
+# CHECK-ODR: DW_AT_type{{.*}}0x00000000[[CU1_FLOAT:[0-9a-f]*]] "float"
+# CHECK-NOODR: DW_AT_type{{.*}}0x[[CU1_FLOAT:[0-9a-f]*]] "float"
+# CHECK: DW_AT_name{{.*}}"member2"
+
+# CHECK: 0x[[CU1_INT:[0-9a-f]*]]: DW_TAG_base_type
+# CHECK: DW_AT_name{{.*}}"int"
+
+# CHECK: 0x[[CU1_CHAR]]: DW_TAG_base_type
+# CHECK: DW_AT_name{{.*}}"char"
+
+# CHECK: 0x[[CU1_FLOAT]]: DW_TAG_base_type
+# CHECK: DW_AT_name{{.*}}"float"
+
+# CHECK-ODR: 0x[[CU1_PTR_CLASS1:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU1_CLASS1]] "class1"
+# CHECK-NOODR: 0x[[CU1_PTR_CLASS1:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x[[CU1_CLASS1]] "class1"
+
+# CHECK: DW_TAG_variable
+# CHECK: DW_AT_name{{.*}}"var1"
+# CHECK-ODR: DW_AT_type{{.*}}0x00000000[[CU1_PTR_CLASS1]] "class1 *"
+# CHECK-NOODR: DW_AT_type{{.*}}0x[[CU1_PTR_CLASS1]] "class1 *"
+
+# CHECK: DW_TAG_compile_unit
+# CHECK: DW_AT_name{{.*}}"CU2"
+
+# CHECK-ODR-NOT: DW_TAG_class_type
+# CHECK-ODR-NOT: "class1"
+
+# CHECK-ODR: 0x[[CU2_PTR_CLASS1:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU1_CLASS1]] "class1"
+
+# CHECK-ODR: DW_TAG_variable
+# CHECK-ODR: DW_AT_name{{.*}}"var1"
+# CHECK-ODR: DW_AT_type{{.*}}0x00000000[[CU2_PTR_CLASS1]] "class1 *"
+
+
+# CHECK-NOODR: 0x[[CU2_CLASS1:[0-9a-f]*]]: DW_TAG_class_type{{.*[[:space:]].*}}DW_AT_name{{.*}}"class1"
+# CHECK-NOODR: DW_TAG_member
+# CHECK-NOODR: DW_AT_type{{.*}}0x[[CU2_CHAR:[0-9a-f]*]] "char"
+# CHECK-NOODR: DW_AT_name{{.*}}"member1"
+
+# CHECK-NOODR: DW_TAG_member
+# CHECK-NOODR: DW_AT_type{{.*}}0x[[CU2_FLOAT:[0-9a-f]*]] "float"
+# CHECK-NOODR: DW_AT_name{{.*}}"member2"
+
+# CHECK-NOODR: 0x[[CU2_INT:[0-9a-f]*]]: DW_TAG_base_type
+# CHECK-NOODR: DW_AT_name{{.*}}"int"
+
+# CHECK-NOODR: 0x[[CU2_CHAR]]: DW_TAG_base_type
+# CHECK-NOODR: DW_AT_name{{.*}}"char"
+
+# CHECK-NOODR: 0x[[CU2_FLOAT]]: DW_TAG_base_type
+# CHECK-NOODR: DW_AT_name{{.*}}"float"
+
+# CHECK-NOODR: 0x[[CU2_PTR_CLASS1:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x[[CU2_CLASS1]] "class1"
+
+# CHECK-NOODR: DW_TAG_variable
+# CHECK-NOODR: DW_AT_name{{.*}}"var1"
+# CHECK-NOODR: DW_AT_type{{.*}}0x[[CU2_PTR_CLASS1]] "class1 *"
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x1000
+ Size: 0x1b
+ - Name: .text2
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x2000
+ Size: 0x1b
+DWARF:
+ debug_abbrev:
+ - Table:
+ - Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_producer
+ Form: DW_FORM_string
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_member
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_template_type_parameter
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_pointer_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_variable
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_const_value
+ Form: DW_FORM_data4
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Table:
+ - Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_producer
+ Form: DW_FORM_string
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_member
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_template_type_parameter
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_pointer_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_variable
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_const_value
+ Form: DW_FORM_data4
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ debug_info:
+ - Version: 4
+ Entries:
+ - AbbrCode: 1
+ Values:
+ - CStr: by_hand
+ - Value: 0x04
+ - CStr: CU1
+ - Value: 0x1000
+ - Value: 0x1b
+ - AbbrCode: 2
+ Values:
+ - CStr: class1
+ - AbbrCode: 3
+ Values:
+ - Value: 0x00000052
+ - CStr: member1
+ - AbbrCode: 3
+ Values:
+ - Value: 0x00000058
+ - CStr: member2
+ - AbbrCode: 0
+ - AbbrCode: 7
+ Values:
+ - CStr: int
+ - AbbrCode: 7
+ Values:
+ - CStr: char
+ - AbbrCode: 7
+ Values:
+ - CStr: float
+ - AbbrCode: 8
+ Values:
+ - Value: 0x0000002a
+ - AbbrCode: 9
+ Values:
+ - CStr: var1
+ - Value: 0x00000000
+ - Value: 0x0000005f
+ - AbbrCode: 0
+ - Version: 4
+ Entries:
+ - AbbrCode: 1
+ Values:
+ - CStr: by_hand
+ - Value: 0x04
+ - CStr: CU2
+ - Value: 0x2000
+ - Value: 0x1b
+ - AbbrCode: 2
+ Values:
+ - CStr: class1
+ - AbbrCode: 3
+ Values:
+ - Value: 0x00000052
+ - CStr: member1
+ - AbbrCode: 3
+ Values:
+ - Value: 0x00000058
+ - CStr: member2
+ - AbbrCode: 0
+ - AbbrCode: 7
+ Values:
+ - CStr: int
+ - AbbrCode: 7
+ Values:
+ - CStr: char
+ - AbbrCode: 7
+ Values:
+ - CStr: float
+ - AbbrCode: 8
+ Values:
+ - Value: 0x0000002a
+ - AbbrCode: 9
+ Values:
+ - CStr: var1
+ - Value: 0x00000000
+ - Value: 0x0000005f
+ - AbbrCode: 0
+...
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-default.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-default.test
new file mode 100644
index 0000000000000..4cac7c5849dbe
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-default.test
@@ -0,0 +1,162 @@
+## This test checks that debug info related to deleted code (marked with
+## default tombstone value) is removed.
+
+# RUN: yaml2obj %s -o %t.o
+
+# RUN: llvm-dwarfutil %t.o - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-GC
+
+# RUN: llvm-dwarfutil --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-GC
+
+# RUN: llvm-dwarfutil --no-garbage-collection --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-GC
+
+# RUN: llvm-dwarfutil --garbage-collection --no-garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-NOGC
+
+# RUN: llvm-dwarfutil %t.o --tombstone=universal - | llvm-dwarfdump -a - | FileCheck %s --check-prefixes=CHECK,CHECK-GC
+
+# CHECK: DW_TAG_compile_unit
+# CHECK: DW_AT_name{{.*}}"CU1"
+# CHECK: DW_TAG_class_type
+# CHECK: DW_AT_name{{.*}}"class1"
+# CHECK-GC-NOT: DW_TAG_class_type
+# CHECK-GC-NOT: "class2"
+# CHECK-GC-NOT: "class3"
+# CHECK-NOGC: DW_TAG_class_type
+# CHECK-NOGC: "class2"
+# CHECK-NOGC: "class3"
+# CHECK: DW_TAG_subprogram
+# CHECK: DW_AT_name{{.*}}"foo1"
+# CHECK: DW_AT_low_pc{{.*}}0x0000000000001000
+# CHECK: DW_AT_high_pc{{.*}}0x0000000000001010
+# CHECK: DW_AT_type{{.*}}"class1"
+# CHECK-GC-NOT: DW_TAG_subprogram
+# CHECK-GC-NOT: "foo2"
+# CHECK-NOGC: DW_TAG_subprogram
+# CHECK-NOGC: "foo2"
+
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x1000
+ Size: 0x1b
+DWARF:
+ debug_abbrev:
+ - Table:
+ - Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_producer
+ Form: DW_FORM_string
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_member
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_template_type_parameter
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ debug_info:
+ - Version: 4
+ Entries:
+ - AbbrCode: 1
+ Values:
+ - CStr: by_hand
+ - Value: 0x04
+ - CStr: CU1
+ - Value: 0x1000
+ - Value: 0x1b
+ - AbbrCode: 3
+ Values:
+ - CStr: class1
+ - AbbrCode: 4
+ Values:
+ - Value: 0x0000006c
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 3
+ Values:
+ - CStr: class2
+ - AbbrCode: 4
+ Values:
+ - Value: 0x0000006c
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 3
+ Values:
+ - CStr: class3
+ - AbbrCode: 4
+ Values:
+ - Value: 0x0000006c
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 8
+ Values:
+ - CStr: int
+ - AbbrCode: 2
+ Values:
+ - CStr: foo1
+ - Value: 0x1000
+ - Value: 0x10
+ - Value: 0x0000002a
+ - AbbrCode: 2
+ Values:
+ - CStr: foo2
+ - Value: 0x0
+ - Value: 0x100
+ - Value: 0x00000040
+ - AbbrCode: 0
+...
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-exec.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-exec.test
new file mode 100644
index 0000000000000..e7a6eed1748b1
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-exec.test
@@ -0,0 +1,150 @@
+## This test checks that debug info related to deleted code (marked with
+## tombstone=exec) is removed.
+
+# RUN: yaml2obj %s -o %t.o
+
+# RUN: llvm-dwarfutil --tombstone=exec --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s
+# RUN: llvm-dwarfutil --tombstone=universal --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s
+
+# CHECK: DW_TAG_compile_unit
+# CHECK: DW_AT_name{{.*}}"CU1"
+# CHECK: DW_TAG_class_type
+# CHECK: DW_AT_name{{.*}}"class1"
+# CHECK-NOT: DW_TAG_class_type
+# CHECK-NOT: "class2"
+# CHECK-NOT: "class3"
+# CHECK: DW_TAG_subprogram
+# CHECK: DW_AT_name{{.*}}"foo1"
+# CHECK: DW_AT_low_pc{{.*}}0x0000000000001000
+# CHECK: DW_AT_high_pc{{.*}}0x0000000000001010
+# CHECK: DW_AT_type{{.*}}"class1"
+# CHECK-NOT: DW_TAG_subprogram
+# CHECK-NOT: "foo2"
+
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x1000
+ Size: 0x1b
+DWARF:
+ debug_abbrev:
+ - Table:
+ - Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_producer
+ Form: DW_FORM_string
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_member
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_template_type_parameter
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ debug_info:
+ - Version: 4
+ Entries:
+ - AbbrCode: 1
+ Values:
+ - CStr: by_hand
+ - Value: 0x04
+ - CStr: CU1
+ - Value: 0x1000
+ - Value: 0x1b
+ - AbbrCode: 3
+ Values:
+ - CStr: class1
+ - AbbrCode: 4
+ Values:
+ - Value: 0x0000006c
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 3
+ Values:
+ - CStr: class2
+ - AbbrCode: 4
+ Values:
+ - Value: 0x0000006c
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 3
+ Values:
+ - CStr: class3
+ - AbbrCode: 4
+ Values:
+ - Value: 0x0000006c
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 8
+ Values:
+ - CStr: int
+ - AbbrCode: 2
+ Values:
+ - CStr: foo1
+ - Value: 0x1000
+ - Value: 0x10
+ - Value: 0x0000002a
+ - AbbrCode: 2
+ Values:
+ - CStr: foo2
+ - Value: 0x0
+ - Value: 0x100
+ - Value: 0x00000040
+ - AbbrCode: 0
+...
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-maxpc.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-maxpc.test
new file mode 100644
index 0000000000000..6184f72496129
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-maxpc.test
@@ -0,0 +1,150 @@
+## This test checks that debug info related to deleted code (marked with
+## tombstone=maxpc) is removed.
+
+## RUN: yaml2obj %s -o %t.o
+
+# RUN: llvm-dwarfutil --tombstone=maxpc --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s
+# RUN: llvm-dwarfutil --tombstone=universal --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s
+
+# CHECK: DW_TAG_compile_unit
+# CHECK: DW_AT_name{{.*}}"CU1"
+# CHECK: DW_TAG_class_type
+# CHECK: DW_AT_name{{.*}}"class1"
+# CHECK-NOT: DW_TAG_class_type
+# CHECK-NOT: "class2"
+# CHECK-NOT: "class3"
+# CHECK: DW_TAG_subprogram
+# CHECK: DW_AT_name{{.*}}"foo1"
+# CHECK: DW_AT_low_pc{{.*}}0x0000000000001000
+# CHECK: DW_AT_high_pc{{.*}}0x0000000000001010
+# CHECK: DW_AT_type{{.*}}"class1"
+# CHECK-NOT: DW_TAG_subprogram
+# CHECK-NOT: "foo2"
+
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x1000
+ Size: 0x1b
+DWARF:
+ debug_abbrev:
+ - Table:
+ - Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_producer
+ Form: DW_FORM_string
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_member
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_template_type_parameter
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ debug_info:
+ - Version: 4
+ Entries:
+ - AbbrCode: 1
+ Values:
+ - CStr: by_hand
+ - Value: 0x04
+ - CStr: CU1
+ - Value: 0x1000
+ - Value: 0x1b
+ - AbbrCode: 3
+ Values:
+ - CStr: class1
+ - AbbrCode: 4
+ Values:
+ - Value: 0x0000006c
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 3
+ Values:
+ - CStr: class2
+ - AbbrCode: 4
+ Values:
+ - Value: 0x0000006c
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 3
+ Values:
+ - CStr: class3
+ - AbbrCode: 4
+ Values:
+ - Value: 0x0000006c
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 8
+ Values:
+ - CStr: int
+ - AbbrCode: 2
+ Values:
+ - CStr: foo1
+ - Value: 0x1000
+ - Value: 0x10
+ - Value: 0x0000002a
+ - AbbrCode: 2
+ Values:
+ - CStr: foo2
+ - Value: 0xFFFFFFFFFFFFFFFF
+ - Value: 0x100
+ - Value: 0x00000040
+ - AbbrCode: 0
+...
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-no-garbage.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-no-garbage.test
new file mode 100644
index 0000000000000..11252295617d7
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/gc-no-garbage.test
@@ -0,0 +1,142 @@
+## This test checks that debug info removal optimization (--garbage-collection)
+## does not affect files which do not have dead debug info.
+
+# RUN: yaml2obj %s -o %t.o
+# RUN: llvm-dwarfutil --tombstone=maxpc --garbage-collection %t.o - | llvm-dwarfdump -a - | FileCheck %s
+
+# CHECK: DW_TAG_compile_unit
+# CHECK: DW_AT_name{{.*}}"CU1"
+# CHECK: DW_TAG_class_type
+# CHECK: DW_AT_name{{.*}}"class1"
+# CHECK: DW_TAG_class_type
+# CHECK: "class2"
+# CHECK: DW_TAG_subprogram
+# CHECK: DW_AT_name{{.*}}"foo1"
+# CHECK: DW_AT_low_pc{{.*}}0x0000000000001000
+# CHECK: DW_AT_high_pc{{.*}}0x0000000000001010
+# CHECK: DW_AT_type{{.*}}"class1"
+# CHECK: DW_TAG_subprogram
+# CHECK: "foo2"
+# CHECK: DW_AT_low_pc{{.*}}0x0000000000001010
+# CHECK: DW_AT_high_pc{{.*}}0x000000000000101b
+# CHECK: DW_AT_type{{.*}}"class2"
+
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x1000
+ Size: 0x1b
+DWARF:
+ debug_abbrev:
+ - Table:
+ - Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_producer
+ Form: DW_FORM_string
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_member
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_template_type_parameter
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ debug_info:
+ - Version: 4
+ Entries:
+ - AbbrCode: 1
+ Values:
+ - CStr: by_hand
+ - Value: 0x04
+ - CStr: CU1
+ - Value: 0x1000
+ - Value: 0x1b
+ - AbbrCode: 3
+ Values:
+ - CStr: class1
+ - AbbrCode: 4
+ Values:
+ - Value: 0x00000056
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 3
+ Values:
+ - CStr: class2
+ - AbbrCode: 4
+ Values:
+ - Value: 0x00000056
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 8
+ Values:
+ - CStr: int
+ - AbbrCode: 2
+ Values:
+ - CStr: foo1
+ - Value: 0x1000
+ - Value: 0x10
+ - Value: 0x0000002a
+ - AbbrCode: 2
+ Values:
+ - CStr: foo2
+ - Value: 0x1010
+ - Value: 0xb
+ - Value: 0x00000040
+ - AbbrCode: 0
+...
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/lit.local.cfg b/llvm/test/tools/llvm-dwarfutil/ELF/X86/lit.local.cfg
new file mode 100644
index 0000000000000..c8625f4d9d248
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/lit.local.cfg
@@ -0,0 +1,2 @@
+if not 'X86' in config.root.targets:
+ config.unsupported = True
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/odr-fwd-declaration.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/odr-fwd-declaration.test
new file mode 100644
index 0000000000000..224d668870b44
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/odr-fwd-declaration.test
@@ -0,0 +1,341 @@
+## This test checks debug info for the case when one compilation unit
+## contains a forward declaration of a type and another compilation unit
+## contains a definition for that type. The result should contain
+## the original definition and the declaration without modifications.
+
+# RUN: yaml2obj %s -o %t.o
+# RUN: llvm-dwarfutil %t.o - | llvm-dwarfdump -a - | FileCheck %s
+
+## CU1:
+##
+## class class1;
+## template<int> class class2;
+##
+## class1 *var1;
+## class2<int> *var2;
+##
+## CU2:
+##
+## class class1 {
+## char member1;
+## float member2;
+## };
+##
+## template<int> class class2 {
+## char member1;
+## };
+##
+## class1 *var1;
+## class2<int> *var2;
+
+# CHECK: file format elf64-x86-64
+# CHECK: .debug_info contents:
+
+# CHECK: DW_TAG_compile_unit
+# CHECK: DW_AT_name{{.*}}"CU1"
+
+# CHECK: 0x[[CU1_CLASS1:[0-9a-f]*]]: DW_TAG_class_type{{.*[[:space:]].*}}DW_AT_name{{.*}}"class1"
+# CHECK: DW_AT_declaration (true)
+
+# CHECK: 0x[[CU1_CLASS2:[0-9a-f]*]]: DW_TAG_class_type{{.*[[:space:]].*}}DW_AT_name{{.*}}"class2"
+# CHECK: DW_AT_declaration (true)
+# CHECK: DW_TAG_template_type_parameter{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU1_INT:[0-9a-f]*]] "int"
+
+# CHECK: 0x[[CU1_INT]]: DW_TAG_base_type
+# CHECK: DW_AT_name{{.*}}"int"
+
+# CHECK: 0x[[CU1_PTR_CLASS1:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU1_CLASS1]] "class1"
+
+# CHECK: 0x[[CU1_PTR_CLASS2:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU1_CLASS2]] "class2<int>"
+
+# CHECK: DW_TAG_variable
+# CHECK: DW_AT_name{{.*}}"var1"
+# CHECK: DW_AT_type{{.*}}0x00000000[[CU1_PTR_CLASS1]] "class1 *"
+# CHECK: DW_TAG_variable
+# CHECK: DW_AT_name{{.*}}"var2"
+# CHECK: DW_AT_type{{.*}}0x00000000[[CU1_PTR_CLASS2]] "class2<int> *"
+
+# CHECK: DW_TAG_compile_unit
+# CHECK: DW_AT_name{{.*}}"CU2"
+
+# CHECK: 0x[[CU2_CLASS1:[0-9a-f]*]]: DW_TAG_class_type{{.*[[:space:]].*}}DW_AT_name{{.*}}"class1"
+# CHECK: DW_TAG_member
+# CHECK: DW_AT_type{{.*}}0x00000000[[CU2_CHAR:[0-9a-f]*]] "char"
+# CHECK: DW_AT_name{{.*}}"member1"
+
+# CHECK: DW_TAG_member
+# CHECK: DW_AT_type{{.*}}0x00000000[[CU2_FLOAT:[0-9a-f]*]] "float"
+# CHECK: DW_AT_name{{.*}}"member2"
+
+# CHECK: 0x[[CU2_CLASS2:[0-9a-f]*]]: DW_TAG_class_type{{.*[[:space:]].*}}DW_AT_name{{.*}}"class2"
+# CHECK: DW_TAG_template_type_parameter{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU2_INT:[0-9a-f]*]] "int"
+# CHECK: DW_TAG_member
+# CHECK: DW_AT_type{{.*}}0x00000000[[CU2_CHAR]] "char"
+# CHECK: DW_AT_name{{.*}}"member1"
+
+# CHECK: 0x[[CU2_INT]]: DW_TAG_base_type
+# CHECK: DW_AT_name{{.*}}"int"
+
+# CHECK: 0x[[CU2_CHAR]]: DW_TAG_base_type
+# CHECK: DW_AT_name{{.*}}"char"
+
+# CHECK: 0x[[CU2_FLOAT]]: DW_TAG_base_type
+# CHECK: DW_AT_name{{.*}}"float"
+
+# CHECK: 0x[[CU2_PTR_CLASS1:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU2_CLASS1]] "class1"
+
+# CHECK: 0x[[CU2_PTR_CLASS2:[0-9a-f]*]]: DW_TAG_pointer_type{{.*[[:space:]].*}}DW_AT_type{{.*}}0x00000000[[CU2_CLASS2]] "class2<int>"
+
+# CHECK: DW_TAG_variable
+# CHECK: DW_AT_name{{.*}}"var1"
+# CHECK: DW_AT_type{{.*}}0x00000000[[CU2_PTR_CLASS1]] "class1 *"
+# CHECK: DW_TAG_variable
+# CHECK: DW_AT_name{{.*}}"var2"
+# CHECK: DW_AT_type{{.*}}0x00000000[[CU2_PTR_CLASS2]] "class2<int> *"
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x1000
+ Size: 0x1b
+ - Name: .text2
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x2000
+ Size: 0x1b
+DWARF:
+ debug_abbrev:
+ - Table:
+ - Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_producer
+ Form: DW_FORM_string
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_member
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref_addr
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_template_type_parameter
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref_addr
+ - Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_pointer_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref_addr
+ - Tag: DW_TAG_variable
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_const_value
+ Form: DW_FORM_data4
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref_addr
+ - Table:
+ - Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_producer
+ Form: DW_FORM_string
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_member
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref_addr
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_template_type_parameter
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref_addr
+ - Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_pointer_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref_addr
+ - Tag: DW_TAG_variable
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_const_value
+ Form: DW_FORM_data4
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref_addr
+ debug_info:
+ - Version: 4
+ Entries:
+ - AbbrCode: 1
+ Values:
+ - CStr: by_hand
+ - Value: 0x04
+ - CStr: CU1
+ - Value: 0x1000
+ - Value: 0x1b
+ - AbbrCode: 4
+ Values:
+ - CStr: class1
+ - AbbrCode: 5
+ Values:
+ - CStr: class2
+ - AbbrCode: 6
+ Values:
+ - Value: 0x00000040
+ - AbbrCode: 0
+ - AbbrCode: 7
+ Values:
+ - CStr: int
+ - AbbrCode: 8
+ Values:
+ - Value: 0x0000002a
+ - AbbrCode: 8
+ Values:
+ - Value: 0x00000032
+ - AbbrCode: 9
+ Values:
+ - CStr: var1
+ - Value: 0x00000000
+ - Value: 0x00000045
+ - AbbrCode: 9
+ Values:
+ - CStr: var2
+ - Value: 0x00000000
+ - Value: 0x0000004a
+ - AbbrCode: 0
+ - Version: 4
+ Entries:
+ - AbbrCode: 1
+ Values:
+ - CStr: by_hand
+ - Value: 0x04
+ - CStr: CU2
+ - Value: 0x2000
+ - Value: 0x1b
+ - AbbrCode: 2
+ Values:
+ - CStr: class1
+ - AbbrCode: 3
+ Values:
+ - Value: 0x000000d9
+ - CStr: member1
+ - AbbrCode: 3
+ Values:
+ - Value: 0x000000df
+ - CStr: member2
+ - AbbrCode: 0
+ - AbbrCode: 2
+ Values:
+ - CStr: class2
+ - AbbrCode: 6
+ Values:
+ - Value: 0x000000d4
+ - AbbrCode: 3
+ Values:
+ - Value: 0x000000d9
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 7
+ Values:
+ - CStr: int
+ - AbbrCode: 7
+ Values:
+ - CStr: char
+ - AbbrCode: 7
+ Values:
+ - CStr: float
+ - AbbrCode: 8
+ Values:
+ - Value: 0x00000096
+ - AbbrCode: 8
+ Values:
+ - Value: 0x000000b9
+ - AbbrCode: 9
+ Values:
+ - CStr: var1
+ - Value: 0x00000000
+ - Value: 0x000000e6
+ - AbbrCode: 9
+ Values:
+ - CStr: var2
+ - Value: 0x00000000
+ - Value: 0x000000eb
+ - AbbrCode: 0
+...
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/separate-debug-file.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/separate-debug-file.test
new file mode 100644
index 0000000000000..d54cb3a182cdc
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/separate-debug-file.test
@@ -0,0 +1,56 @@
+## This test checks the --[no-]separate-debug-file option.
+
+# RUN: yaml2obj %p/Inputs/common.yaml -o %t.o
+
+## Check --garbage-collection --no-separate-debug-file.
+# RUN: llvm-dwarfutil --garbage-collection --no-separate-debug-file %t.o %t2
+# RUN: llvm-dwarfdump -a %t2 | FileCheck --check-prefix=CHECK-DEBUG %s
+
+## Check --garbage-collection --separate-debug-file.
+# RUN: llvm-dwarfutil --garbage-collection --separate-debug-file %t.o %t2
+# RUN: llvm-objdump --headers %t2 | FileCheck --check-prefix=CHECK-NON-DEBUG %s
+# RUN: llvm-dwarfdump -a %t2.debug | FileCheck --check-prefix=CHECK-DEBUG %s
+
+## Check --no-garbage-collection --no-separate-debug-file.
+# RUN: llvm-dwarfutil --no-garbage-collection --no-separate-debug-file %t.o %t1
+# RUN: llvm-dwarfdump -a %t1 | FileCheck --check-prefix=CHECK-DEBUG %s
+
+## Check --no-garbage-collection --separate-debug-file.
+# RUN: llvm-dwarfutil --no-garbage-collection %t.o --separate-debug-file %t3
+# RUN: llvm-objdump --headers %t3 | FileCheck --check-prefix=CHECK-NON-DEBUG %s
+# RUN: llvm-dwarfdump -a %t3.debug | FileCheck --check-prefix=CHECK-DEBUG %s
+## Copy result to compare it later.
+# RUN: cp %t3 %t4
+# RUN: cp %t3.debug %t4.debug
+
+## Check that --separate-debug-file wins if last.
+# RUN: llvm-dwarfutil --no-garbage-collection --no-separate-debug-file --separate-debug-file %t.o %t3
+# RUN: cmp %t4 %t3
+# RUN: cmp %t4.debug %t3.debug
+
+## Check that --no-separate-debug-file wins if last.
+# RUN: llvm-dwarfutil --no-garbage-collection --separate-debug-file --no-separate-debug-file %t.o %t5
+# RUN: cmp %t1 %t5
+
+# CHECK-NON-DEBUG-NOT: .debug_abbrev
+# CHECK-NON-DEBUG-NOT: .debug_info
+# CHECK-NON-DEBUG: .gnu_debuglink
+# CHECK-NON-DEBUG-NOT: .debug_abbrev
+# CHECK-NON-DEBUG-NOT: .debug_info
+
+# CHECK-DEBUG: .debug_abbrev
+# CHECK-DEBUG: DW_TAG_compile_unit
+# CHECK-DEBUG: .debug_info
+# CHECK-DEBUG: DW_TAG_compile_unit
+# CHECK-DEBUG: DW_AT_producer{{.*}}"by_hand"
+# CHECK-DEBUG: DW_AT_language{{.*}}DW_LANG_C_plus_plus
+# CHECK-DEBUG: DW_AT_name{{.*}}"CU1"
+# CHECK-DEBUG: DW_TAG_class_type
+# CHECK-DEBUG: DW_AT_name{{.*}}"class1"
+# CHECK-DEBUG: DW_TAG_base_type
+# CHECK-DEBUG: DW_AT_name{{.*}}"int"
+# CHECK-DEBUG: DW_AT_name{{.*}}"char"
+# CHECK-DEBUG: DW_AT_name{{.*}}"float"
+# CHECK-DEBUG: DW_TAG_pointer_type
+# CHECK-DEBUG: DW_TAG_variable
+# CHECK-DEBUG: DW_AT_name{{.*}}"var1"
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/verbose.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/verbose.test
new file mode 100644
index 0000000000000..6675d7cd8a3bb
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/verbose.test
@@ -0,0 +1,25 @@
+## This test checks output of llvm-dwarfutil when verbose mode
+## is enabled.
+
+# RUN: yaml2obj %p/Inputs/common.yaml -o %t.o
+
+## Check verbose output.
+# RUN: llvm-dwarfutil %t.o %t1 --verbose 2>&1 | FileCheck %s
+
+## Check warning displayed in multi-thread mode (--num-threads set explicitly).
+# RUN: llvm-dwarfutil %t.o %t1 --verbose --num-threads 10 2>&1 | FileCheck %s --check-prefix=CHECK-THREAD-WARNING
+
+## Check -j alias.
+# RUN: llvm-dwarfutil %t.o %t1 --verbose -j 10 2>&1 | FileCheck %s --check-prefix=CHECK-THREAD-WARNING
+
+## Check verbose output for --verify.
+# RUN: llvm-dwarfutil %t.o %t1 -j 1 --verbose --verify 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-VERIFY
+
+# CHECK-NOT: warning: --num-threads set to 1 because verbose mode is specified
+# CHECK: Do garbage collection for debug info ...
+# CHECK: Input compilation unit:
+# CHECK: DW_TAG_compile_unit
+# CHECK: Keeping subprogram DIE
+# CHECK: DW_TAG_subprogram
+# CHECK-THREAD-WARNING: warning: --num-threads set to 1 because verbose mode is specified
+# CHECK-VERIFY: Verifying DWARF...
diff --git a/llvm/test/tools/llvm-dwarfutil/ELF/X86/verify.test b/llvm/test/tools/llvm-dwarfutil/ELF/X86/verify.test
new file mode 100644
index 0000000000000..844e8fb671af3
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/ELF/X86/verify.test
@@ -0,0 +1,151 @@
+## This test checks that debug info contained in the source file is properly
+## verified by --verify after copying. If --no-garbage-collection is
+## specified then the verification should fail, otherwise the verification
+## should succeed.
+
+# RUN: yaml2obj %s -o %t.o
+
+## Verify resulting debug info after --garbage-collection optimisation.
+# RUN: llvm-dwarfutil %t.o %t1 --verify
+
+## Verify separate debug file after --garbage-collection optimisation.
+# RUN: llvm-dwarfutil %t.o --separate-debug-file %t1 --verify
+
+## Verify not optimised resulting debug info.
+# RUN: not llvm-dwarfutil --no-garbage-collection %t.o %t1 --verify 2>&1 | FileCheck %s -DFILE=%t1
+
+## Verify not optimised resulting separate debug file.
+# RUN: not llvm-dwarfutil --no-garbage-collection %t.o --separate-debug-file %t1 --verify 2>&1 | FileCheck %s -DFILE=%t1.debug
+
+## Check that verification is disabled when destination is stdout.
+# RUN: llvm-dwarfutil %t.o - --verify 2>&1 | FileCheck %s --check-prefix=CHECK-STDOUT
+
+# CHECK: error: '[[FILE]]': output verification failed
+# CHECK-STDOUT: warning: verification skipped because writing to stdout
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+DWARF:
+ debug_abbrev:
+ - Table:
+ - Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_producer
+ Form: DW_FORM_string
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_addr
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data8
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_member
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_class_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Tag: DW_TAG_template_type_parameter
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_string
+ debug_info:
+ - Version: 4
+ Entries:
+ - AbbrCode: 1
+ Values:
+ - CStr: by_hand
+ - Value: 0x04
+ - CStr: CU1
+ - Value: 0x1000
+ - Value: 0x100
+ - AbbrCode: 3
+ Values:
+ - CStr: class1
+ - AbbrCode: 4
+ Values:
+ - Value: 0x0000006c
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 3
+ Values:
+ - CStr: class2
+ - AbbrCode: 4
+ Values:
+ - Value: 0x0000006c
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 3
+ Values:
+ - CStr: class3
+ - AbbrCode: 4
+ Values:
+ - Value: 0x0000006c
+ - CStr: member1
+ - AbbrCode: 0
+ - AbbrCode: 8
+ Values:
+ - CStr: int
+ - AbbrCode: 2
+ Values:
+ - CStr: foo1
+ - Value: 0x1000
+ - Value: 0x10
+ - Value: 0x0000002a
+ - AbbrCode: 2
+ Values:
+ - CStr: foo2
+ - Value: 0x0
+ - Value: 0x100
+ - Value: 0x00000040
+ - AbbrCode: 2
+ Values:
+ - CStr: foo3
+ - Value: 0x0
+ - Value: 0x100
+ - Value: 0x00000040
+ - AbbrCode: 0
+...
diff --git a/llvm/test/tools/llvm-dwarfutil/error-invalid-format.test b/llvm/test/tools/llvm-dwarfutil/error-invalid-format.test
new file mode 100644
index 0000000000000..531cde3c4f2b7
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/error-invalid-format.test
@@ -0,0 +1,6 @@
+## This test checks that llvm-dwarfutil displays an error message
+## if an input file is not valid.
+
+# RUN: not llvm-dwarfutil %s - 2>&1 | FileCheck %s -DFILE=%s
+
+# CHECK: error: '[[FILE]]': The file was not recognized as a valid object file
diff --git a/llvm/test/tools/llvm-dwarfutil/error-no-gc-odr.test b/llvm/test/tools/llvm-dwarfutil/error-no-gc-odr.test
new file mode 100644
index 0000000000000..d52cca3c71482
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/error-no-gc-odr.test
@@ -0,0 +1,6 @@
+## This test checks the error message displayed if ODR deduplication
+## is enabled while debug info garbage collection is disabled.
+
+# RUN: not llvm-dwarfutil --no-garbage-collection --odr-deduplication - - 2>&1 | FileCheck --check-prefix CHECK %s
+
+# CHECK: error: cannot use --odr-deduplication without --garbage-collection
diff --git a/llvm/test/tools/llvm-dwarfutil/error-no-input-file.test b/llvm/test/tools/llvm-dwarfutil/error-no-input-file.test
new file mode 100644
index 0000000000000..6d81e7389a5ad
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/error-no-input-file.test
@@ -0,0 +1,6 @@
+## This test checks the error message displayed if
+## an input file does not exist.
+
+# RUN: not llvm-dwarfutil not-existed not-existed 2>&1 | FileCheck %s -DMSG=%errc_ENOENT
+
+# CHECK: error: 'not-existed': [[MSG]]
diff --git a/llvm/test/tools/llvm-dwarfutil/error-positional-args.test b/llvm/test/tools/llvm-dwarfutil/error-positional-args.test
new file mode 100644
index 0000000000000..b46f94e671da9
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/error-positional-args.test
@@ -0,0 +1,8 @@
+## This test checks the error message displayed if an incorrect
+## number of positional arguments is specified.
+
+# RUN: not llvm-dwarfutil - 2>&1 | FileCheck --check-prefix CHECK1 %s
+# RUN: not llvm-dwarfutil - - - 2>&1 | FileCheck --check-prefix CHECK3 %s
+
+# CHECK1: error: exactly two positional arguments expected, 1 provided
+# CHECK3: error: exactly two positional arguments expected, 3 provided
diff --git a/llvm/test/tools/llvm-dwarfutil/error-unknown-option.test b/llvm/test/tools/llvm-dwarfutil/error-unknown-option.test
new file mode 100644
index 0000000000000..d25eb1ce60070
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/error-unknown-option.test
@@ -0,0 +1,6 @@
+## This test checks the error message displayed if an unknown
+## option is specified.
+
+# RUN: not llvm-dwarfutil --unknown-option 2>&1 | FileCheck %s
+
+# CHECK: error: unknown option: --unknown-option
diff --git a/llvm/test/tools/llvm-dwarfutil/error-unknown-tombstone.test b/llvm/test/tools/llvm-dwarfutil/error-unknown-tombstone.test
new file mode 100644
index 0000000000000..afc5a6db6e211
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/error-unknown-tombstone.test
@@ -0,0 +1,6 @@
+## This test checks the error message diplayed if an incorrect
+## tombstone value is specified.
+
+# RUN: not llvm-dwarfutil --tombstone=unknown - - 2>&1 | FileCheck --check-prefix CHECK %s
+
+# CHECK: error: unknown tombstone value: 'unknown'
diff --git a/llvm/test/tools/llvm-dwarfutil/error-unsupported-input-file.test b/llvm/test/tools/llvm-dwarfutil/error-unsupported-input-file.test
new file mode 100644
index 0000000000000..13b19b5cfe719
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/error-unsupported-input-file.test
@@ -0,0 +1,7 @@
+## This test checks the error message displayed if input file
+## has unsupported format.
+
+# RUN: echo "!<thin>" > %t1
+# RUN: not llvm-dwarfutil --garbage-collection %t1 - 2>&1 | FileCheck %s -DFILE=%t1
+
+# CHECK: error: '[[FILE]]': unsupported input file
diff --git a/llvm/test/tools/llvm-dwarfutil/help.test b/llvm/test/tools/llvm-dwarfutil/help.test
new file mode 100644
index 0000000000000..7fc1cc17682e7
--- /dev/null
+++ b/llvm/test/tools/llvm-dwarfutil/help.test
@@ -0,0 +1,25 @@
+## This test checks the help message of llvm-dwarfutil.
+
+# RUN: llvm-dwarfutil | FileCheck %s
+# RUN: llvm-dwarfutil -h | FileCheck %s
+# RUN: llvm-dwarfutil --help | FileCheck %s
+
+# CHECK: OVERVIEW: llvm-dwarfutil is a tool to copy and manipulate debug info
+# CHECK: USAGE: {{.*}}llvm-dwarfutil{{.*}} [options] <input file> <output file>
+# CHECK: OPTIONS:
+# CHECK: --garbage-collection
+# CHECK: --help
+# CHECK: -h
+# CHECK: -j
+# CHECK: --no-garbage-collection
+# CHECK: --no-odr-deduplication
+# CHECK: --no-odr
+# CHECK: --no-separate-debug-file
+# CHECK: --num-threads
+# CHECK: --odr-deduplication
+# CHECK: --separate-debug-file
+# CHECK: --tombstone
+# CHECK: --verbose
+# CHECK: --verify
+# CHECK: --version
+# CHECK: -V
diff --git a/llvm/tools/llvm-dwarfutil/CMakeLists.txt b/llvm/tools/llvm-dwarfutil/CMakeLists.txt
new file mode 100644
index 0000000000000..d932ff1eff2d0
--- /dev/null
+++ b/llvm/tools/llvm-dwarfutil/CMakeLists.txt
@@ -0,0 +1,27 @@
+set(LLVM_TARGET_DEFINITIONS Options.td)
+tablegen(LLVM Options.inc -gen-opt-parser-defs)
+add_public_tablegen_target(DwarfutilTableGen)
+
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ DebugInfoDWARF
+ DWARFLinker
+ MC
+ ObjCopy
+ Object
+ Option
+ Support
+ Target
+ AllTargetsCodeGens
+ AllTargetsDescs
+ AllTargetsInfos
+ )
+
+add_llvm_tool(llvm-dwarfutil
+ llvm-dwarfutil.cpp
+ DebugInfoLinker.cpp
+
+ DEPENDS
+ intrinsics_gen
+ ${tablegen_deps}
+ )
diff --git a/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp
new file mode 100644
index 0000000000000..ee6bf4fa78120
--- /dev/null
+++ b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.cpp
@@ -0,0 +1,281 @@
+//=== DebugInfoLinker.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DebugInfoLinker.h"
+#include "Error.h"
+#include "llvm/DWARFLinker/DWARFLinker.h"
+#include "llvm/DWARFLinker/DWARFStreamer.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
+#include "llvm/Object/ObjectFile.h"
+#include <memory>
+#include <vector>
+
+namespace llvm {
+namespace dwarfutil {
+
+// ObjFileAddressMap allows to check whether specified DIE referencing
+// dead addresses. It uses tombstone values to determine dead addresses.
+// The concrete values of tombstone constants were discussed in
+// https://reviews.llvm.org/D81784 and https://reviews.llvm.org/D84825.
+// So we use following values as indicators of dead addresses:
+//
+// bfd: (LowPC == 0) or (LowPC == 1 and HighPC == 1 and DWARF v4 (or less))
+// or ([LowPC, HighPC] is not inside address ranges of .text sections).
+//
+// maxpc: (LowPC == -1) or (LowPC == -2 and DWARF v4 (or less))
+// That value is assumed to be compatible with
+// http://www.dwarfstd.org/ShowIssue.php?issue=200609.1
+//
+// exec: [LowPC, HighPC] is not inside address ranges of .text sections
+//
+// universal: maxpc and bfd
+class ObjFileAddressMap : public AddressesMap {
+public:
+ ObjFileAddressMap(DWARFContext &Context, const Options &Options,
+ object::ObjectFile &ObjFile)
+ : Opts(Options) {
+ // Remember addresses of existing text sections.
+ for (const object::SectionRef &Sect : ObjFile.sections()) {
+ if (!Sect.isText())
+ continue;
+ const uint64_t Size = Sect.getSize();
+ if (Size == 0)
+ continue;
+ const uint64_t StartAddr = Sect.getAddress();
+ TextAddressRanges[{StartAddr}] = {StartAddr + Size, 0};
+ }
+
+ // Check CU address ranges for tombstone value.
+ for (std::unique_ptr<DWARFUnit> &CU : Context.compile_units()) {
+ Expected<llvm::DWARFAddressRangesVector> ARanges =
+ CU->getUnitDIE().getAddressRanges();
+ if (ARanges) {
+ for (auto &Range : *ARanges) {
+ if (!isDeadAddressRange(Range.LowPC, Range.HighPC, CU->getVersion(),
+ Options.Tombstone, CU->getAddressByteSize()))
+ DWARFAddressRanges[{Range.LowPC}] = {Range.HighPC, 0};
+ }
+ }
+ }
+ }
+
+ // should be renamed into has valid address ranges
+ bool hasValidRelocs() override { return !DWARFAddressRanges.empty(); }
+
+ bool isLiveSubprogram(const DWARFDie &DIE,
+ CompileUnit::DIEInfo &Info) override {
+ assert((DIE.getTag() == dwarf::DW_TAG_subprogram ||
+ DIE.getTag() == dwarf::DW_TAG_label) &&
+ "Wrong type of input die");
+
+ if (Optional<uint64_t> LowPC =
+ dwarf::toAddress(DIE.find(dwarf::DW_AT_low_pc))) {
+ if (!isDeadAddress(*LowPC, DIE.getDwarfUnit()->getVersion(),
+ Opts.Tombstone,
+ DIE.getDwarfUnit()->getAddressByteSize())) {
+ Info.AddrAdjust = 0;
+ Info.InDebugMap = true;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool isLiveVariable(const DWARFDie &DIE,
+ CompileUnit::DIEInfo &Info) override {
+ assert((DIE.getTag() == dwarf::DW_TAG_variable ||
+ DIE.getTag() == dwarf::DW_TAG_constant) &&
+ "Wrong type of input die");
+
+ if (Expected<DWARFLocationExpressionsVector> Loc =
+ DIE.getLocations(dwarf::DW_AT_location)) {
+ DWARFUnit *U = DIE.getDwarfUnit();
+ for (const auto &Entry : *Loc) {
+ DataExtractor Data(toStringRef(Entry.Expr),
+ U->getContext().isLittleEndian(), 0);
+ DWARFExpression Expression(Data, U->getAddressByteSize(),
+ U->getFormParams().Format);
+ bool HasLiveAddresses =
+ any_of(Expression, [&](const DWARFExpression::Operation &Op) {
+ // TODO: add handling of dwarf::DW_OP_addrx
+ return !Op.isError() &&
+ (Op.getCode() == dwarf::DW_OP_addr &&
+ !isDeadAddress(Op.getRawOperand(0), U->getVersion(),
+ Opts.Tombstone,
+ DIE.getDwarfUnit()->getAddressByteSize()));
+ });
+
+ if (HasLiveAddresses) {
+ Info.AddrAdjust = 0;
+ Info.InDebugMap = true;
+ return true;
+ }
+ }
+ } else {
+ // FIXME: missing DW_AT_location is OK here, but other errors should be
+ // reported to the user.
+ consumeError(Loc.takeError());
+ }
+
+ return false;
+ }
+
+ bool applyValidRelocs(MutableArrayRef<char>, uint64_t, bool) override {
+ // no need to apply relocations to the linked binary.
+ return false;
+ }
+
+ RangesTy &getValidAddressRanges() override { return DWARFAddressRanges; };
+
+ void clear() override { DWARFAddressRanges.clear(); }
+
+ llvm::Expected<uint64_t> relocateIndexedAddr(uint64_t, uint64_t) override {
+ // should not be called.
+ return object::createError("no relocations in linked binary");
+ }
+
+protected:
+ // returns true if specified address range is inside address ranges
+ // of executable sections.
+ bool isInsideExecutableSectionsAddressRange(uint64_t LowPC,
+ Optional<uint64_t> HighPC) {
+ auto Range = TextAddressRanges.lower_bound(LowPC);
+ if ((Range == TextAddressRanges.end() || Range->first != LowPC) &&
+ Range != TextAddressRanges.begin())
+ --Range;
+
+ if (Range != TextAddressRanges.end() && Range->first <= LowPC &&
+ (HighPC ? Range->second.HighPC >= HighPC
+ : Range->second.HighPC >= LowPC))
+ return true;
+
+ return false;
+ }
+
+ uint64_t isBFDDeadAddressRange(uint64_t LowPC, Optional<uint64_t> HighPC,
+ uint16_t Version) {
+ if (LowPC == 0)
+ return true;
+
+ if ((Version <= 4) && HighPC && (LowPC == 1 && *HighPC == 1))
+ return true;
+
+ return !isInsideExecutableSectionsAddressRange(LowPC, HighPC);
+ }
+
+ uint64_t isMAXPCDeadAddressRange(uint64_t LowPC, Optional<uint64_t> HighPC,
+ uint16_t Version, uint8_t AddressByteSize) {
+ if (Version <= 4 && HighPC) {
+ if (LowPC == (dwarf::computeTombstoneAddress(AddressByteSize) - 1))
+ return true;
+ } else if (LowPC == dwarf::computeTombstoneAddress(AddressByteSize))
+ return true;
+
+ if (!isInsideExecutableSectionsAddressRange(LowPC, HighPC))
+ warning("Address referencing invalid text section is not marked with "
+ "tombstone value");
+
+ return false;
+ }
+
+ bool isDeadAddressRange(uint64_t LowPC, Optional<uint64_t> HighPC,
+ uint16_t Version, TombstoneKind Tombstone,
+ uint8_t AddressByteSize) {
+ switch (Tombstone) {
+ case TombstoneKind::BFD:
+ return isBFDDeadAddressRange(LowPC, HighPC, Version);
+ case TombstoneKind::MaxPC:
+ return isMAXPCDeadAddressRange(LowPC, HighPC, Version, AddressByteSize);
+ case TombstoneKind::Universal:
+ return isBFDDeadAddressRange(LowPC, HighPC, Version) ||
+ isMAXPCDeadAddressRange(LowPC, HighPC, Version, AddressByteSize);
+ case TombstoneKind::Exec:
+ return !isInsideExecutableSectionsAddressRange(LowPC, HighPC);
+ }
+
+ llvm_unreachable("Unknown tombstone value");
+ }
+
+ bool isDeadAddress(uint64_t LowPC, uint16_t Version, TombstoneKind Tombstone,
+ uint8_t AddressByteSize) {
+ return isDeadAddressRange(LowPC, None, Version, Tombstone, AddressByteSize);
+ }
+
+private:
+ RangesTy DWARFAddressRanges;
+ RangesTy TextAddressRanges;
+ const Options &Opts;
+};
+
+bool linkDebugInfo(object::ObjectFile &File, const Options &Options,
+ raw_pwrite_stream &OutStream) {
+
+ auto ReportWarn = [&](const Twine &Message, StringRef Context,
+ const DWARFDie *Die) {
+ warning(Message, Context);
+
+ if (!Options.Verbose || !Die)
+ return;
+
+ DIDumpOptions DumpOpts;
+ DumpOpts.ChildRecurseDepth = 0;
+ DumpOpts.Verbose = Options.Verbose;
+
+ WithColor::note() << " in DIE:\n";
+ Die->dump(errs(), /*Indent=*/6, DumpOpts);
+ };
+ auto ReportErr = [&](const Twine &Message, StringRef Context,
+ const DWARFDie *) {
+ WithColor::error(errs(), Context) << Message << '\n';
+ };
+
+ // Create output streamer.
+ DwarfStreamer OutStreamer(OutputFileType::Object, OutStream, nullptr,
+ ReportWarn, ReportWarn);
+ if (!OutStreamer.init(File.makeTriple(), ""))
+ return false;
+
+ // Create DWARF linker.
+ DWARFLinker DebugInfoLinker(&OutStreamer, DwarfLinkerClient::LLD);
+
+ DebugInfoLinker.setEstimatedObjfilesAmount(1);
+ DebugInfoLinker.setAccelTableKind(DwarfLinkerAccelTableKind::None);
+ DebugInfoLinker.setErrorHandler(ReportErr);
+ DebugInfoLinker.setWarningHandler(ReportWarn);
+ DebugInfoLinker.setNumThreads(Options.NumThreads);
+ DebugInfoLinker.setNoODR(!Options.DoODRDeduplication);
+ DebugInfoLinker.setVerbosity(Options.Verbose);
+ DebugInfoLinker.setUpdate(!Options.DoGarbageCollection);
+
+ std::vector<std::unique_ptr<DWARFFile>> ObjectsForLinking(1);
+ std::vector<std::unique_ptr<AddressesMap>> AddresssMapForLinking(1);
+ std::vector<std::string> EmptyWarnings;
+
+ std::unique_ptr<DWARFContext> Context = DWARFContext::create(File);
+
+ // Add object files to the DWARFLinker.
+ AddresssMapForLinking[0] =
+ std::make_unique<ObjFileAddressMap>(*Context, Options, File);
+
+ ObjectsForLinking[0] = std::make_unique<DWARFFile>(
+ File.getFileName(), &*Context, AddresssMapForLinking[0].get(),
+ EmptyWarnings);
+
+ for (size_t I = 0; I < ObjectsForLinking.size(); I++)
+ DebugInfoLinker.addObjectFile(*ObjectsForLinking[I]);
+
+ // Link debug info.
+ DebugInfoLinker.link();
+ OutStreamer.finish();
+ return true;
+}
+
+} // end of namespace dwarfutil
+} // end of namespace llvm
diff --git a/llvm/tools/llvm-dwarfutil/DebugInfoLinker.h b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.h
new file mode 100644
index 0000000000000..e95c83cb96090
--- /dev/null
+++ b/llvm/tools/llvm-dwarfutil/DebugInfoLinker.h
@@ -0,0 +1,31 @@
+//===- DebugInfoLinker.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_DWARFUTIL_DEBUGINFOLINKER_H
+#define LLVM_TOOLS_LLVM_DWARFUTIL_DEBUGINFOLINKER_H
+
+#include "Options.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/ObjectFile.h"
+
+namespace llvm {
+namespace dwarfutil {
+
+inline bool isDebugSection(StringRef SecName) {
+ return SecName.startswith(".debug") || SecName.startswith(".zdebug") ||
+ SecName == ".gdb_index";
+}
+
+bool linkDebugInfo(object::ObjectFile &file, const Options &Options,
+ raw_pwrite_stream &OutStream);
+
+} // end of namespace dwarfutil
+} // end of namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_DWARFUTIL_DEBUGINFOLINKER_H
diff --git a/llvm/tools/llvm-dwarfutil/Error.h b/llvm/tools/llvm-dwarfutil/Error.h
new file mode 100644
index 0000000000000..9ef288d4f6573
--- /dev/null
+++ b/llvm/tools/llvm-dwarfutil/Error.h
@@ -0,0 +1,44 @@
+//===- Error.h --------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_DWARFUTIL_ERROR_H
+#define LLVM_TOOLS_LLVM_DWARFUTIL_ERROR_H
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace llvm {
+namespace dwarfutil {
+
+inline void error(Error Err, StringRef Prefix = "") {
+ handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) {
+ WithColor::error(errs(), Prefix) << Info.message() << '\n';
+ });
+ std::exit(EXIT_FAILURE);
+}
+
+inline void warning(const Twine &Message, StringRef Prefix = "") {
+ WithColor::warning(errs(), Prefix) << Message << '\n';
+}
+
+inline void verbose(const Twine &Message, bool Verbose) {
+ if (Verbose)
+ outs() << Message << '\n';
+}
+
+} // end of namespace dwarfutil
+} // end of namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_DWARFUTIL_ERROR_H
diff --git a/llvm/tools/llvm-dwarfutil/Options.h b/llvm/tools/llvm-dwarfutil/Options.h
new file mode 100644
index 0000000000000..c993200ceb4b7
--- /dev/null
+++ b/llvm/tools/llvm-dwarfutil/Options.h
@@ -0,0 +1,46 @@
+//===- Options.h ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_DWARFUTIL_OPTIONS_H
+#define LLVM_TOOLS_LLVM_DWARFUTIL_OPTIONS_H
+
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace llvm {
+namespace dwarfutil {
+
+/// The kind of tombstone value.
+enum class TombstoneKind {
+ BFD, /// 0/[1:1]. Bfd default.
+ MaxPC, /// -1/-2. Assumed to match with
+ /// http://www.dwarfstd.org/ShowIssue.php?issue=200609.1.
+ Universal, /// both: BFD + MaxPC
+ Exec, /// match with address range of executable sections.
+};
+
+struct Options {
+ std::string InputFileName;
+ std::string OutputFileName;
+ bool DoGarbageCollection = false;
+ bool DoODRDeduplication = false;
+ bool BuildSeparateDebugFile = false;
+ TombstoneKind Tombstone = TombstoneKind::Universal;
+ bool Verbose = false;
+ int NumThreads = 0;
+ bool Verify = false;
+
+ std::string getSeparateDebugFileName() const {
+ return OutputFileName + ".debug";
+ }
+};
+
+} // namespace dwarfutil
+} // namespace llvm
+
+#endif // LLVM_TOOLS_LLVM_DWARFUTIL_OPTIONS_H
diff --git a/llvm/tools/llvm-dwarfutil/Options.td b/llvm/tools/llvm-dwarfutil/Options.td
new file mode 100644
index 0000000000000..4ab1b51d808d8
--- /dev/null
+++ b/llvm/tools/llvm-dwarfutil/Options.td
@@ -0,0 +1,65 @@
+include "llvm/Option/OptParser.td"
+
+multiclass BB<string name, string help1, string help2> {
+ def NAME: Flag<["--"], name>, HelpText<help1>;
+ def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>;
+}
+
+def help : Flag<["--"], "help">,
+ HelpText<"Prints this help output">;
+
+def h : Flag<["-"], "h">,
+ Alias<help>,
+ HelpText<"Alias for --help">;
+
+defm odr_deduplication : BB<"odr-deduplication",
+ "Do ODR deduplication for debug types(default)",
+ "Don`t do ODR deduplication for debug types">;
+
+def odr : Flag<["--"], "odr">,
+ Alias<odr_deduplication>,
+ HelpText<"Alias for --odr-deduplication">;
+
+def no_odr : Flag<["--"], "no-odr">,
+ Alias<no_odr_deduplication>,
+ HelpText<"Alias for --no-odr-deduplication">;
+
+defm garbage_collection : BB<"garbage-collection",
+ "Do garbage collection for debug info(default)",
+ "Don`t do garbage collection for debug info">;
+
+defm separate_debug_file : BB<"separate-debug-file",
+ "Create two output files: file w/o debug tables and file with debug tables",
+ "Create single output file, containing debug tables(default)">;
+
+def tombstone: Separate<["--", "-"], "tombstone">,
+ MetaVarName<"[bfd,maxpc,exec,universal]">,
+ HelpText<"Tombstone value used as a marker of invalid address(default: universal)\n"
+ " =bfd - Zero for all addresses and [1,1] for DWARF v4 (or less) address ranges and exec\n"
+ " =maxpc - Minus 1 for all addresses and minus 2 for DWARF v4 (or less) address ranges\n"
+ " =exec - Match with address ranges of executable sections\n"
+ " =universal - Both: bfd and maxpc"
+ >;
+def: Joined<["--", "-"], "tombstone=">, Alias<tombstone>;
+
+def threads: Separate<["--", "-"], "num-threads">,
+ MetaVarName<"<threads>">,
+ HelpText<"Number of available threads for multi-threaded execution. "
+ "Defaults to the number of cores on the current machine">;
+
+def: Separate<["-"], "j">,
+ Alias<threads>,
+ HelpText<"Alias for --num-threads">;
+
+def verbose : Flag<["--"], "verbose">,
+ HelpText<"Enable verbose logging">;
+
+def verify : Flag<["--"], "verify">,
+ HelpText<"Run the DWARF verifier on the resulting debug info">;
+
+def version : Flag<["--"], "version">,
+ HelpText<"Print the version and exit">;
+
+def V : Flag<["-"], "V">,
+ Alias<version>,
+ HelpText<"Alias for --version">;
diff --git a/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp b/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp
new file mode 100644
index 0000000000000..e77c82e0fad9a
--- /dev/null
+++ b/llvm/tools/llvm-dwarfutil/llvm-dwarfutil.cpp
@@ -0,0 +1,527 @@
+//=== llvm-dwarfutil.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DebugInfoLinker.h"
+#include "Error.h"
+#include "Options.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/DebugInfo/DWARF/DWARFVerifier.h"
+#include "llvm/MC/MCTargetOptionsCommandFlags.h"
+#include "llvm/ObjCopy/CommonConfig.h"
+#include "llvm/ObjCopy/ConfigManager.h"
+#include "llvm/ObjCopy/ObjCopy.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CRC.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/TargetSelect.h"
+
+using namespace llvm;
+using namespace object;
+
+namespace {
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OPT_##ID,
+#include "Options.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Options.inc"
+#undef PREFIX
+
+const opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ { \
+ PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
+#include "Options.inc"
+#undef OPTION
+};
+
+class DwarfutilOptTable : public opt::OptTable {
+public:
+ DwarfutilOptTable() : OptTable(InfoTable) {}
+};
+} // namespace
+
+namespace llvm {
+namespace dwarfutil {
+
+std::string ToolName;
+
+static mc::RegisterMCTargetOptionsFlags MOF;
+
+static Error validateAndSetOptions(opt::InputArgList &Args, Options &Options) {
+ auto UnknownArgs = Args.filtered(OPT_UNKNOWN);
+ if (!UnknownArgs.empty())
+ return createStringError(
+ std::errc::invalid_argument,
+ formatv("unknown option: {0}", (*UnknownArgs.begin())->getSpelling())
+ .str()
+ .c_str());
+
+ std::vector<std::string> InputFiles = Args.getAllArgValues(OPT_INPUT);
+ if (InputFiles.size() != 2)
+ return createStringError(
+ std::errc::invalid_argument,
+ formatv("exactly two positional arguments expected, {0} provided",
+ InputFiles.size())
+ .str()
+ .c_str());
+
+ Options.InputFileName = InputFiles[0];
+ Options.OutputFileName = InputFiles[1];
+
+ Options.BuildSeparateDebugFile =
+ Args.hasFlag(OPT_separate_debug_file, OPT_no_separate_debug_file, false);
+ Options.DoODRDeduplication =
+ Args.hasFlag(OPT_odr_deduplication, OPT_no_odr_deduplication, true);
+ Options.DoGarbageCollection =
+ Args.hasFlag(OPT_garbage_collection, OPT_no_garbage_collection, true);
+ Options.Verbose = Args.hasArg(OPT_verbose);
+ Options.Verify = Args.hasArg(OPT_verify);
+
+ if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads))
+ Options.NumThreads = atoi(NumThreads->getValue());
+ else
+ Options.NumThreads = 0; // Use all available hardware threads
+
+ if (opt::Arg *Tombstone = Args.getLastArg(OPT_tombstone)) {
+ StringRef S = Tombstone->getValue();
+ if (S == "bfd")
+ Options.Tombstone = TombstoneKind::BFD;
+ else if (S == "maxpc")
+ Options.Tombstone = TombstoneKind::MaxPC;
+ else if (S == "universal")
+ Options.Tombstone = TombstoneKind::Universal;
+ else if (S == "exec")
+ Options.Tombstone = TombstoneKind::Exec;
+ else
+ return createStringError(
+ std::errc::invalid_argument,
+ formatv("unknown tombstone value: '{0}'", S).str().c_str());
+ }
+
+ if (Options.Verbose) {
+ if (Options.NumThreads != 1 && Args.hasArg(OPT_threads))
+ warning("--num-threads set to 1 because verbose mode is specified");
+
+ Options.NumThreads = 1;
+ }
+
+ if (Options.DoODRDeduplication && Args.hasArg(OPT_odr_deduplication) &&
+ !Options.DoGarbageCollection)
+ return createStringError(
+ std::errc::invalid_argument,
+ "cannot use --odr-deduplication without --garbage-collection");
+
+ if (Options.BuildSeparateDebugFile && Options.OutputFileName == "-")
+ return createStringError(
+ std::errc::invalid_argument,
+ "unable to write to stdout when --separate-debug-file specified");
+
+ return Error::success();
+}
+
+static Error setConfigToAddNewDebugSections(objcopy::ConfigManager &Config,
+ ObjectFile &ObjFile) {
+ // Add new debug sections.
+ for (SectionRef Sec : ObjFile.sections()) {
+ Expected<StringRef> SecName = Sec.getName();
+ if (!SecName)
+ return SecName.takeError();
+
+ if (isDebugSection(*SecName)) {
+ Expected<StringRef> SecData = Sec.getContents();
+ if (!SecData)
+ return SecData.takeError();
+
+ Config.Common.AddSection.emplace_back(objcopy::NewSectionInfo(
+ *SecName, MemoryBuffer::getMemBuffer(*SecData, *SecName, false)));
+ }
+ }
+
+ return Error::success();
+}
+
+static Error verifyOutput(const Options &Opts) {
+ if (Opts.OutputFileName == "-") {
+ warning("verification skipped because writing to stdout");
+ return Error::success();
+ }
+
+ std::string FileName = Opts.BuildSeparateDebugFile
+ ? Opts.getSeparateDebugFileName()
+ : Opts.OutputFileName;
+ Expected<OwningBinary<Binary>> BinOrErr = createBinary(FileName);
+ if (!BinOrErr)
+ return createFileError(FileName, BinOrErr.takeError());
+
+ if (BinOrErr->getBinary()->isObject()) {
+ if (ObjectFile *Obj = static_cast<ObjectFile *>(BinOrErr->getBinary())) {
+ verbose("Verifying DWARF...", Opts.Verbose);
+ std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);
+ DIDumpOptions DumpOpts;
+ if (!DICtx->verify(Opts.Verbose ? outs() : nulls(),
+ DumpOpts.noImplicitRecursion()))
+ return createFileError(FileName,
+ createError("output verification failed"));
+
+ return Error::success();
+ }
+ }
+
+ // The file "FileName" was created by this utility in the previous steps
+ // (i.e. it is already known that it should pass the isObject check).
+ // If the createBinary() function does not return an error, the isObject
+ // check should also be successful.
+ llvm_unreachable(
+ formatv("tool unexpectedly did not emit a supported object file: '{0}'",
+ FileName)
+ .str()
+ .c_str());
+}
+
+class raw_crc_ostream : public raw_ostream {
+public:
+ explicit raw_crc_ostream(raw_ostream &O) : OS(O) { SetUnbuffered(); }
+
+ void reserveExtraSpace(uint64_t ExtraSize) override {
+ OS.reserveExtraSpace(ExtraSize);
+ }
+
+ uint32_t getCRC32() { return CRC32; }
+
+protected:
+ raw_ostream &OS;
+ uint32_t CRC32 = 0;
+
+ /// See raw_ostream::write_impl.
+ void write_impl(const char *Ptr, size_t Size) override {
+ CRC32 = crc32(
+ CRC32, ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(Ptr), Size));
+ OS.write(Ptr, Size);
+ }
+
+ /// Return the current position within the stream, not counting the bytes
+ /// currently in the buffer.
+ uint64_t current_pos() const override { return OS.tell(); }
+};
+
+static Expected<uint32_t> saveSeparateDebugInfo(const Options &Opts,
+ ObjectFile &InputFile) {
+ objcopy::ConfigManager Config;
+ std::string OutputFilename = Opts.getSeparateDebugFileName();
+ Config.Common.InputFilename = Opts.InputFileName;
+ Config.Common.OutputFilename = OutputFilename;
+ Config.Common.OnlyKeepDebug = true;
+ uint32_t WrittenFileCRC32 = 0;
+
+ if (Error Err = writeToOutput(
+ Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error {
+ raw_crc_ostream CRCBuffer(OutFile);
+ if (Error Err = objcopy::executeObjcopyOnBinary(Config, InputFile,
+ CRCBuffer))
+ return Err;
+
+ WrittenFileCRC32 = CRCBuffer.getCRC32();
+ return Error::success();
+ }))
+ return std::move(Err);
+
+ return WrittenFileCRC32;
+}
+
+static Error saveNonDebugInfo(const Options &Opts, ObjectFile &InputFile,
+ uint32_t GnuDebugLinkCRC32) {
+ objcopy::ConfigManager Config;
+ Config.Common.InputFilename = Opts.InputFileName;
+ Config.Common.OutputFilename = Opts.OutputFileName;
+ Config.Common.StripDebug = true;
+ std::string SeparateDebugFileName = Opts.getSeparateDebugFileName();
+ Config.Common.AddGnuDebugLink = sys::path::filename(SeparateDebugFileName);
+ Config.Common.GnuDebugLinkCRC32 = GnuDebugLinkCRC32;
+
+ if (Error Err = writeToOutput(
+ Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error {
+ if (Error Err =
+ objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile))
+ return Err;
+
+ return Error::success();
+ }))
+ return Err;
+
+ return Error::success();
+}
+
+static Error splitDebugIntoSeparateFile(const Options &Opts,
+ ObjectFile &InputFile) {
+ Expected<uint32_t> SeparateDebugFileCRC32OrErr =
+ saveSeparateDebugInfo(Opts, InputFile);
+ if (!SeparateDebugFileCRC32OrErr)
+ return SeparateDebugFileCRC32OrErr.takeError();
+
+ if (Error Err =
+ saveNonDebugInfo(Opts, InputFile, *SeparateDebugFileCRC32OrErr))
+ return Err;
+
+ return Error::success();
+}
+
+using DebugInfoBits = SmallString<10000>;
+
+static Error addSectionsFromLinkedData(objcopy::ConfigManager &Config,
+ ObjectFile &InputFile,
+ DebugInfoBits &LinkedDebugInfoBits) {
+ if (dyn_cast<ELFObjectFile<ELF32LE>>(&InputFile)) {
+ Expected<ELFObjectFile<ELF32LE>> MemFile = ELFObjectFile<ELF32LE>::create(
+ MemoryBufferRef(LinkedDebugInfoBits, ""));
+ if (!MemFile)
+ return MemFile.takeError();
+
+ if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile))
+ return Err;
+ } else if (dyn_cast<ELFObjectFile<ELF64LE>>(&InputFile)) {
+ Expected<ELFObjectFile<ELF64LE>> MemFile = ELFObjectFile<ELF64LE>::create(
+ MemoryBufferRef(LinkedDebugInfoBits, ""));
+ if (!MemFile)
+ return MemFile.takeError();
+
+ if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile))
+ return Err;
+ } else if (dyn_cast<ELFObjectFile<ELF32BE>>(&InputFile)) {
+ Expected<ELFObjectFile<ELF32BE>> MemFile = ELFObjectFile<ELF32BE>::create(
+ MemoryBufferRef(LinkedDebugInfoBits, ""));
+ if (!MemFile)
+ return MemFile.takeError();
+
+ if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile))
+ return Err;
+ } else if (dyn_cast<ELFObjectFile<ELF64BE>>(&InputFile)) {
+ Expected<ELFObjectFile<ELF64BE>> MemFile = ELFObjectFile<ELF64BE>::create(
+ MemoryBufferRef(LinkedDebugInfoBits, ""));
+ if (!MemFile)
+ return MemFile.takeError();
+
+ if (Error Err = setConfigToAddNewDebugSections(Config, *MemFile))
+ return Err;
+ } else
+ return createStringError(std::errc::invalid_argument,
+ "unsupported file format");
+
+ return Error::success();
+}
+
+static Expected<uint32_t>
+saveSeparateLinkedDebugInfo(const Options &Opts, ObjectFile &InputFile,
+ DebugInfoBits LinkedDebugInfoBits) {
+ objcopy::ConfigManager Config;
+ std::string OutputFilename = Opts.getSeparateDebugFileName();
+ Config.Common.InputFilename = Opts.InputFileName;
+ Config.Common.OutputFilename = OutputFilename;
+ Config.Common.StripDebug = true;
+ Config.Common.OnlyKeepDebug = true;
+ uint32_t WrittenFileCRC32 = 0;
+
+ if (Error Err =
+ addSectionsFromLinkedData(Config, InputFile, LinkedDebugInfoBits))
+ return std::move(Err);
+
+ if (Error Err = writeToOutput(
+ Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error {
+ raw_crc_ostream CRCBuffer(OutFile);
+
+ if (Error Err = objcopy::executeObjcopyOnBinary(Config, InputFile,
+ CRCBuffer))
+ return Err;
+
+ WrittenFileCRC32 = CRCBuffer.getCRC32();
+ return Error::success();
+ }))
+ return std::move(Err);
+
+ return WrittenFileCRC32;
+}
+
+static Error saveSingleLinkedDebugInfo(const Options &Opts,
+ ObjectFile &InputFile,
+ DebugInfoBits LinkedDebugInfoBits) {
+ objcopy::ConfigManager Config;
+
+ Config.Common.InputFilename = Opts.InputFileName;
+ Config.Common.OutputFilename = Opts.OutputFileName;
+ Config.Common.StripDebug = true;
+ if (Error Err =
+ addSectionsFromLinkedData(Config, InputFile, LinkedDebugInfoBits))
+ return Err;
+
+ if (Error Err = writeToOutput(
+ Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error {
+ return objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile);
+ }))
+ return Err;
+
+ return Error::success();
+}
+
+static Error saveLinkedDebugInfo(const Options &Opts, ObjectFile &InputFile,
+ DebugInfoBits LinkedDebugInfoBits) {
+ if (Opts.BuildSeparateDebugFile) {
+ Expected<uint32_t> SeparateDebugFileCRC32OrErr =
+ saveSeparateLinkedDebugInfo(Opts, InputFile,
+ std::move(LinkedDebugInfoBits));
+ if (!SeparateDebugFileCRC32OrErr)
+ return SeparateDebugFileCRC32OrErr.takeError();
+
+ if (Error Err =
+ saveNonDebugInfo(Opts, InputFile, *SeparateDebugFileCRC32OrErr))
+ return Err;
+ } else {
+ if (Error Err = saveSingleLinkedDebugInfo(Opts, InputFile,
+ std::move(LinkedDebugInfoBits)))
+ return Err;
+ }
+
+ return Error::success();
+}
+
+static Error saveCopyOfFile(const Options &Opts, ObjectFile &InputFile) {
+ objcopy::ConfigManager Config;
+
+ Config.Common.InputFilename = Opts.InputFileName;
+ Config.Common.OutputFilename = Opts.OutputFileName;
+
+ if (Error Err = writeToOutput(
+ Config.Common.OutputFilename, [&](raw_ostream &OutFile) -> Error {
+ return objcopy::executeObjcopyOnBinary(Config, InputFile, OutFile);
+ }))
+ return Err;
+
+ return Error::success();
+}
+
+static Error applyCLOptions(const struct Options &Opts, ObjectFile &InputFile) {
+ if (Opts.DoGarbageCollection) {
+ verbose("Do garbage collection for debug info ...", Opts.Verbose);
+
+ DebugInfoBits LinkedDebugInfo;
+ raw_svector_ostream OutStream(LinkedDebugInfo);
+
+ if (linkDebugInfo(InputFile, Opts, OutStream)) {
+ if (Error Err =
+ saveLinkedDebugInfo(Opts, InputFile, std::move(LinkedDebugInfo)))
+ return Err;
+
+ return Error::success();
+ }
+
+ return createStringError(std::errc::invalid_argument,
+ "possible broken debug info");
+ } else if (Opts.BuildSeparateDebugFile) {
+ if (Error Err = splitDebugIntoSeparateFile(Opts, InputFile))
+ return Err;
+ } else {
+ if (Error Err = saveCopyOfFile(Opts, InputFile))
+ return Err;
+ }
+
+ return Error::success();
+}
+
+} // end of namespace dwarfutil
+} // end of namespace llvm
+
+int main(int Argc, char const *Argv[]) {
+ using namespace dwarfutil;
+
+ InitLLVM X(Argc, Argv);
+ ToolName = Argv[0];
+
+ // Parse arguments.
+ DwarfutilOptTable T;
+ unsigned MAI;
+ unsigned MAC;
+ ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1);
+ opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC);
+
+ if (Args.hasArg(OPT_help) || Args.size() == 0) {
+ T.printHelp(
+ outs(), (ToolName + " [options] <input file> <output file>").c_str(),
+ "llvm-dwarfutil is a tool to copy and manipulate debug info", false);
+ return EXIT_SUCCESS;
+ }
+
+ if (Args.hasArg(OPT_version)) {
+ cl::PrintVersionMessage();
+ return EXIT_SUCCESS;
+ }
+
+ Options Opts;
+ if (Error Err = validateAndSetOptions(Args, Opts))
+ error(std::move(Err), dwarfutil::ToolName);
+
+ InitializeAllTargets();
+ InitializeAllTargetMCs();
+ InitializeAllTargetInfos();
+ InitializeAllAsmPrinters();
+ InitializeAllAsmParsers();
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
+ MemoryBuffer::getFileOrSTDIN(Opts.InputFileName);
+ if (BuffOrErr.getError())
+ error(createFileError(Opts.InputFileName, BuffOrErr.getError()));
+
+ Expected<std::unique_ptr<Binary>> BinOrErr =
+ object::createBinary(**BuffOrErr);
+ if (!BinOrErr)
+ error(createFileError(Opts.InputFileName, BinOrErr.takeError()));
+
+ Expected<FilePermissionsApplier> PermsApplierOrErr =
+ FilePermissionsApplier::create(Opts.InputFileName);
+ if (!PermsApplierOrErr)
+ error(createFileError(Opts.InputFileName, PermsApplierOrErr.takeError()));
+
+ if (!(*BinOrErr)->isObject())
+ error(createFileError(Opts.InputFileName,
+ createError("unsupported input file")));
+
+ if (Error Err =
+ applyCLOptions(Opts, *static_cast<ObjectFile *>((*BinOrErr).get())))
+ error(createFileError(Opts.InputFileName, std::move(Err)));
+
+ BinOrErr->reset();
+ BuffOrErr->reset();
+
+ if (Error Err = PermsApplierOrErr->apply(Opts.OutputFileName))
+ error(std::move(Err));
+
+ if (Opts.BuildSeparateDebugFile)
+ if (Error Err = PermsApplierOrErr->apply(Opts.getSeparateDebugFileName()))
+ error(std::move(Err));
+
+ if (Opts.Verify) {
+ if (Error Err = verifyOutput(Opts))
+ error(std::move(Err));
+ }
+
+ return EXIT_SUCCESS;
+}
More information about the llvm-commits
mailing list