forked from OSchip/llvm-project
[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:
parent
f7b2d4aac6
commit
e2147c26bd
|
@ -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/>.
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -83,6 +83,7 @@ set(LLVM_TEST_DEPENDS
|
|||
llvm-dlltool
|
||||
dsymutil
|
||||
llvm-dwarfdump
|
||||
llvm-dwarfutil
|
||||
llvm-dwp
|
||||
llvm-exegesis
|
||||
llvm-extract
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
...
|
|
@ -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"
|
|
@ -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"
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
...
|
|
@ -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
|
||||
...
|
|
@ -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
|
||||
...
|
|
@ -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
|
||||
...
|
|
@ -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
|
||||
...
|
|
@ -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
|
||||
...
|
|
@ -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
|
||||
...
|
|
@ -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"
|
|
@ -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...
|
|
@ -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
|
||||
...
|
|
@ -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
|
|
@ -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
|
|
@ -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]]
|
|
@ -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
|
|
@ -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
|
|
@ -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'
|
|
@ -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
|
|
@ -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}
|
||||
)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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">;
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue