[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
This commit is contained in:
Alexey Lapshin 2022-07-10 20:11:55 +03:00
parent f7b2d4aac6
commit e2147c26bd
35 changed files with 3124 additions and 2 deletions

View File

@ -0,0 +1,123 @@
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.
- `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/>.

View File

@ -182,6 +182,10 @@ public:
/// 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);

View File

@ -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);

View File

@ -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);

View File

@ -83,6 +83,7 @@ set(LLVM_TEST_DEPENDS
llvm-dlltool
dsymutil
llvm-dwarfdump
llvm-dwarfutil
llvm-dwp
llvm-exegesis
llvm-extract

View File

@ -160,8 +160,8 @@ tools.extend([
'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',

View File

@ -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
...

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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
...

View File

@ -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
...

View File

@ -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
...

View File

@ -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
...

View File

@ -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
...

View File

@ -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
...

View File

@ -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
...

View File

@ -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"

View File

@ -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...

View File

@ -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
...

View File

@ -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

View File

@ -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

View File

@ -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]]

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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}
)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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">;

View File

@ -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;
}