Index: include/llvm/BinaryFormat/Dwarf.h =================================================================== --- include/llvm/BinaryFormat/Dwarf.h +++ include/llvm/BinaryFormat/Dwarf.h @@ -62,6 +62,9 @@ const uint32_t DW_CIE_ID = UINT32_MAX; const uint64_t DW64_CIE_ID = UINT64_MAX; +// Identifier of an invalid DIE offset in the .debug_info section. +const uint32_t DW_INVALID_OFFSET = UINT32_MAX; + enum Tag : uint16_t { #define HANDLE_DW_TAG(ID, NAME, VERSION, VENDOR) DW_TAG_##NAME = ID, #include "llvm/BinaryFormat/Dwarf.def" Index: include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h =================================================================== --- include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h +++ include/llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h @@ -55,6 +55,17 @@ uint32_t getNumHashes(); uint32_t getSizeHdr(); uint32_t getHeaderDataLength(); + ArrayRef> getAtomsDesc(); + + /// Return information related to the DWARF DIE we're looking for when + /// performing a lookup by name. + /// + /// \param HashDataOffset an offset into the hash data table + /// \returns DIEOffset the offset into the .debug_info section for the DIE + /// related to the input hash data offset. Currently this function returns + /// only the DIEOffset but it can be modified to return more data regarding + /// the DIE + uint32_t readAtoms(uint32_t &HashDataOffset); void dump(raw_ostream &OS) const; }; Index: lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp =================================================================== --- lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp +++ lib/DebugInfo/DWARF/DWARFAcceleratorTable.cpp @@ -62,6 +62,41 @@ return Hdr.HeaderDataLength; } +ArrayRef> +DWARFAcceleratorTable::getAtomsDesc() { + return HdrData.Atoms; +} + +bool isSupportedForm(DWARFFormValue FormValue) { + if ((!FormValue.isFormClass(DWARFFormValue::FC_Constant) && + !FormValue.isFormClass(DWARFFormValue::FC_Flag)) || + FormValue.getForm() == dwarf::DW_FORM_sdata) + return false; + return true; +} + +uint32_t DWARFAcceleratorTable::readAtoms(uint32_t &HashDataOffset) { + uint32_t DieOffset = dwarf::DW_INVALID_OFFSET; + + for (auto Atom : getAtomsDesc()) { + DWARFFormValue FormValue(Atom.second); + if (isSupportedForm(FormValue)) { + FormValue.extractValue(AccelSection, &HashDataOffset, NULL); + switch (Atom.first) { + case dwarf::DW_ATOM_die_offset: + DieOffset = *FormValue.getAsUnsignedConstant(); + break; + default: + break; + } + } else { + HashDataOffset = dwarf::DW_INVALID_OFFSET; + } + } + return DieOffset; +} + LLVM_DUMP_METHOD void DWARFAcceleratorTable::dump(raw_ostream &OS) const { // Dump the header. OS << "Magic = " << format("0x%08x", Hdr.Magic) << '\n' Index: lib/DebugInfo/DWARF/DWARFVerifier.cpp =================================================================== --- lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -292,20 +292,81 @@ OS << "Verifying .apple_names...\n"; - // Verify that all buckets have a valid hash index or are empty + // Verify that all buckets have a valid hash index or are empty. uint32_t NumBuckets = AppleNames.getNumBuckets(); uint32_t NumHashes = AppleNames.getNumHashes(); uint32_t BucketsOffset = AppleNames.getSizeHdr() + AppleNames.getHeaderDataLength(); + uint32_t HashesBase = BucketsOffset + NumBuckets * 4; + uint32_t OffsetsBase = HashesBase + NumHashes * 4; for (uint32_t BucketIdx = 0; BucketIdx < NumBuckets; ++BucketIdx) { uint32_t HashIdx = AppleNamesSection.getU32(&BucketsOffset); if (HashIdx >= NumHashes && HashIdx != UINT32_MAX) { - OS << format("error: Bucket[%d] has invalid hash index: [%d]\n", - BucketIdx, HashIdx); + OS << format("error: Bucket[%d] has invalid hash index: %u\n", BucketIdx, + HashIdx); ++NumAppleNamesErrors; } } + + uint32_t NumAtoms = AppleNames.getAtomsDesc().size(); + if (NumAtoms == 0) { + OS << "error: no atoms; failed to read HashData\n"; + ++NumAppleNamesErrors; + return false; + } + + for (uint32_t HashIdx = 0; HashIdx < NumHashes; ++HashIdx) { + uint32_t HashOffset = HashesBase + 4 * HashIdx; + uint32_t DataOffset = OffsetsBase + 4 * HashIdx; + uint32_t Hash = AppleNamesSection.getU32(&HashOffset); + uint32_t HashDataOffset = AppleNamesSection.getU32(&DataOffset); + if (!AppleNamesSection.isValidOffsetForDataOfSize(HashDataOffset, + sizeof(uint64_t))) { + OS << format("error: Hash[%d] has invalid HashData offset: 0x%08x\n", + HashIdx, HashDataOffset); + ++NumAppleNamesErrors; + } + + uint32_t StrpOffset; + uint32_t StringOffset; + uint32_t StringCount = 0; + uint32_t DieOffset = dwarf::DW_INVALID_OFFSET; + + while ((StrpOffset = AppleNamesSection.getU32(&HashDataOffset)) != 0) { + const uint32_t NumHashDataObjects = + AppleNamesSection.getU32(&HashDataOffset); + for (uint32_t HashDataIdx = 0; HashDataIdx < NumHashDataObjects; + ++HashDataIdx) { + const uint32_t HashDataOffsetStart = HashDataOffset; + DieOffset = AppleNames.readAtoms(HashDataOffset); + if (HashDataOffset == dwarf::DW_INVALID_OFFSET) { + OS << format( + "error: unsupported form; failed to read HashData at 0x%08x\n", + HashDataOffsetStart); + ++NumAppleNamesErrors; + return false; + } else if (!DCtx.getDIEForOffset(DieOffset)) { + const uint32_t BucketIdx = + NumBuckets ? (Hash % NumBuckets) : UINT32_MAX; + StringOffset = StrpOffset; + const char *Name = StrData.getCStr(&StringOffset); + if (!Name) + Name = ""; + + OS << format( + "error: .apple_names Bucket[%d] Hash[%d] = 0x%08x " + "Str[%u] = 0x%08x " + "DIE[%d] = 0x%08x is not a valid DIE offset for \"%s\".\n", + BucketIdx, HashIdx, Hash, StringCount, StrpOffset, HashDataIdx, + DieOffset, Name); + + ++NumAppleNamesErrors; + } + } + ++StringCount; + } + } return NumAppleNamesErrors == 0; } Index: test/tools/llvm-dwarfdump/X86/apple_names.test =================================================================== --- /dev/null +++ test/tools/llvm-dwarfdump/X86/apple_names.test @@ -0,0 +1,5 @@ +RUN: llvm-dwarfdump -verify %p/../../../DebugInfo/Inputs/dwarfdump-objc.x86_64.o \ +RUN: | FileCheck %s + +CHECK: Verifying .apple_names +CHECK-NEXT: No errors. Index: test/tools/llvm-dwarfdump/X86/apple_names_verify_atoms.s =================================================================== --- /dev/null +++ test/tools/llvm-dwarfdump/X86/apple_names_verify_atoms.s @@ -0,0 +1,59 @@ +# RUN: llvm-mc %s -filetype obj -triple x86_64-apple-darwin -o - \ +# RUN: | not llvm-dwarfdump -verify - \ +# RUN: | FileCheck %s + +# CHECK: Verifying .apple_names... +# CHECK-NEXT: error: no atoms; failed to read HashData + +# This test is meant to verify that the -verify option +# in llvm-dwarfdump, correctly identifies that there is not Atom. +# As a result, the hashdata cannot be read. + + .section __TEXT,__text,regular,pure_instructions + .file 1 "basic.c" + .comm _i,4,2 ## @i + .comm _j,4,2 ## @j + .section __DWARF,__debug_str,regular,debug +Linfo_string: + .asciz "Apple LLVM version 8.1.0 (clang-802.0.35)" ## string offset=0 + .asciz "basic.c" ## string offset=42 + .asciz "/Users/sgravani/Development/tests" ## string offset=50 + .asciz "i" ## string offset=84 + .asciz "int" ## string offset=86 + .asciz "j" ## string offset=90 + + .section __DWARF,__debug_info,regular,debug +Lsection_info: + .section __DWARF,__apple_names,regular,debug +Lnames_begin: + .long 1212240712 ## Header Magic + .short 1 ## Header Version + .short 0 ## Header Hash Function + .long 2 ## Header Bucket Count + .long 2 ## Header Hash Count + .long 12 ## Header Data Length + .long 0 ## HeaderData Die Offset Base + .long 0 ## HeaderData Atom Count -- error: no atoms; failed to read HashData + .short 1 ## DW_ATOM_die_offset + .short 6 ## DW_FORM_data4 + .long 0 ## Bucket 0 + .long 1 ## Bucket 1 + .long 177678 ## Hash in Bucket 0 + .long 177679 ## Hash in Bucket 1 + .long LNames0-Lnames_begin ## Offset in Bucket 0 + .long LNames1-Lnames_begin ## Offset in Bucket 1 +LNames0: + .long 84 ## i + .long 1 ## Num DIEs + .long 30 + .long 0 +LNames1: + .long 90 ## j + .long 1 ## Num DIEs + .long 58 + .long 0 + +.subsections_via_symbols + .section __DWARF,__debug_line,regular,debug +Lsection_line: +Lline_table_start0: Index: test/tools/llvm-dwarfdump/X86/apple_names_verify_buckets.s =================================================================== --- test/tools/llvm-dwarfdump/X86/apple_names_verify_buckets.s +++ /dev/null @@ -1,192 +0,0 @@ -# RUN: llvm-mc %s -filetype obj -triple x86_64-apple-darwin -o - \ -# RUN: | not llvm-dwarfdump -verify - \ -# RUN: | FileCheck %s - -# CHECK: Verifying .apple_names... -# CHECK-NEXT: error: Bucket[0] has invalid hash index: [-2] - -# This test is meant to verify that the -verify option -# in llvm-dwarfdump, correctly identifies -# an invalid hash index for bucket[0] in the .apple_names section. - - .section __TEXT,__text,regular,pure_instructions - .file 1 "basic.c" - .comm _i,4,2 ## @i - .section __DWARF,__debug_str,regular,debug -Linfo_string: - .asciz "basic.c" ## string offset=42 - .asciz "i" ## string offset=84 - .asciz "int" ## string offset=86 - .section __DWARF,__debug_loc,regular,debug -Lsection_debug_loc: - .section __DWARF,__debug_abbrev,regular,debug -Lsection_abbrev: - .byte 1 ## Abbreviation Code - .byte 17 ## DW_TAG_compile_unit - .byte 1 ## DW_CHILDREN_yes - .byte 37 ## DW_AT_producer - .byte 14 ## DW_FORM_strp - .byte 19 ## DW_AT_language - .byte 5 ## DW_FORM_data2 - .byte 3 ## DW_AT_name - .byte 14 ## DW_FORM_strp - .byte 16 ## DW_AT_stmt_list - .byte 23 ## DW_FORM_sec_offset - .byte 27 ## DW_AT_comp_dir - .byte 14 ## DW_FORM_strp - .byte 0 ## EOM(1) - .byte 0 ## EOM(2) - .byte 2 ## Abbreviation Code - .byte 52 ## DW_TAG_variable - .byte 0 ## DW_CHILDREN_no - .byte 3 ## DW_AT_name - .byte 14 ## DW_FORM_strp - .byte 73 ## DW_AT_type - .byte 19 ## DW_FORM_ref4 - .byte 63 ## DW_AT_external - .byte 25 ## DW_FORM_flag_present - .byte 58 ## DW_AT_decl_file - .byte 11 ## DW_FORM_data1 - .byte 59 ## DW_AT_decl_line - .byte 11 ## DW_FORM_data1 - .byte 2 ## DW_AT_location - .byte 24 ## DW_FORM_exprloc - .byte 0 ## EOM(1) - .byte 0 ## EOM(2) - .byte 3 ## Abbreviation Code - .byte 36 ## DW_TAG_base_type - .byte 0 ## DW_CHILDREN_no - .byte 3 ## DW_AT_name - .byte 14 ## DW_FORM_strp - .byte 62 ## DW_AT_encoding - .byte 11 ## DW_FORM_data1 - .byte 11 ## DW_AT_byte_size - .byte 11 ## DW_FORM_data1 - .byte 0 ## EOM(1) - .byte 0 ## EOM(2) - .byte 0 ## EOM(3) - .section __DWARF,__debug_info,regular,debug -Lsection_info: -Lcu_begin0: - .long 55 ## Length of Unit - .short 4 ## DWARF version number -Lset0 = Lsection_abbrev-Lsection_abbrev ## Offset Into Abbrev. Section - .long Lset0 - .byte 8 ## Address Size (in bytes) - .byte 1 ## Abbrev [1] 0xb:0x30 DW_TAG_compile_unit - .long 0 ## DW_AT_producer - .short 12 ## DW_AT_language - .long 42 ## DW_AT_name -Lset1 = Lline_table_start0-Lsection_line ## DW_AT_stmt_list - .long Lset1 - .long 50 ## DW_AT_comp_dir - .byte 2 ## Abbrev [2] 0x1e:0x15 DW_TAG_variable - .long 84 ## DW_AT_name - .long 51 ## DW_AT_type - ## DW_AT_external - .byte 1 ## DW_AT_decl_file - .byte 1 ## DW_AT_decl_line - .byte 9 ## DW_AT_location - .byte 3 - .quad _i - .byte 3 ## Abbrev [3] 0x33:0x7 DW_TAG_base_type - .long 86 ## DW_AT_name - .byte 5 ## DW_AT_encoding - .byte 4 ## DW_AT_byte_size - .byte 0 ## End Of Children Mark - .section __DWARF,__debug_ranges,regular,debug -Ldebug_range: - .section __DWARF,__debug_macinfo,regular,debug -Ldebug_macinfo: -Lcu_macro_begin0: - .byte 0 ## End Of Macro List Mark - .section __DWARF,__apple_names,regular,debug -Lnames_begin: - .long 1212240712 ## Header Magic - .short 1 ## Header Version - .short 0 ## Header Hash Function - .long 1 ## Header Bucket Count - .long 1 ## Header Hash Count - .long 12 ## Header Data Length - .long 0 ## HeaderData Die Offset Base - .long 1 ## HeaderData Atom Count - .short 1 ## DW_ATOM_die_offset - .short 6 ## DW_FORM_data4 - .long -2 ## Bucket 0 -- error: Bucket[0] has invalid hash index: [-2] - .long 177678 ## Hash in Bucket 0 - .long LNames0-Lnames_begin ## Offset in Bucket 0 -LNames0: - .long 84 ## i - .long 1 ## Num DIEs - .long 30 - .long 0 - .section __DWARF,__apple_objc,regular,debug -Lobjc_begin: - .long 1212240712 ## Header Magic - .short 1 ## Header Version - .short 0 ## Header Hash Function - .long 1 ## Header Bucket Count - .long 0 ## Header Hash Count - .long 12 ## Header Data Length - .long 0 ## HeaderData Die Offset Base - .long 1 ## HeaderData Atom Count - .short 1 ## DW_ATOM_die_offset - .short 6 ## DW_FORM_data4 - .long -1 ## Bucket 0 - .section __DWARF,__apple_namespac,regular,debug -Lnamespac_begin: - .long 1212240712 ## Header Magic - .short 1 ## Header Version - .short 0 ## Header Hash Function - .long 1 ## Header Bucket Count - .long 0 ## Header Hash Count - .long 12 ## Header Data Length - .long 0 ## HeaderData Die Offset Base - .long 1 ## HeaderData Atom Count - .short 1 ## DW_ATOM_die_offset - .short 6 ## DW_FORM_data4 - .long -1 ## Bucket 0 - .section __DWARF,__apple_types,regular,debug -Ltypes_begin: - .long 1212240712 ## Header Magic - .short 1 ## Header Version - .short 0 ## Header Hash Function - .long 1 ## Header Bucket Count - .long 1 ## Header Hash Count - .long 20 ## Header Data Length - .long 0 ## HeaderData Die Offset Base - .long 3 ## HeaderData Atom Count - .short 1 ## DW_ATOM_die_offset - .short 6 ## DW_FORM_data4 - .short 3 ## DW_ATOM_die_tag - .short 5 ## DW_FORM_data2 - .short 4 ## DW_ATOM_type_flags - .short 11 ## DW_FORM_data1 - .long 0 ## Bucket 0 - .long 193495088 ## Hash in Bucket 0 - .long Ltypes0-Ltypes_begin ## Offset in Bucket 0 -Ltypes0: - .long 86 ## int - .long 1 ## Num DIEs - .long 51 - .short 36 - .byte 0 - .long 0 - .section __DWARF,__apple_exttypes,regular,debug -Lexttypes_begin: - .long 1212240712 ## Header Magic - .short 1 ## Header Version - .short 0 ## Header Hash Function - .long 1 ## Header Bucket Count - .long 0 ## Header Hash Count - .long 12 ## Header Data Length - .long 0 ## HeaderData Die Offset Base - .long 1 ## HeaderData Atom Count - .short 7 ## DW_ATOM_ext_types - .short 6 ## DW_FORM_data4 - .long -1 ## Bucket 0 - -.subsections_via_symbols - .section __DWARF,__debug_line,regular,debug -Lsection_line: -Lline_table_start0: Index: test/tools/llvm-dwarfdump/X86/apple_names_verify_data.s =================================================================== --- /dev/null +++ test/tools/llvm-dwarfdump/X86/apple_names_verify_data.s @@ -0,0 +1,64 @@ +# RUN: llvm-mc %s -filetype obj -triple x86_64-apple-darwin -o - \ +# RUN: | not llvm-dwarfdump -verify - \ +# RUN: | FileCheck %s + +# CHECK: Verifying .apple_names... +# CHECK-NEXT: error: Bucket[0] has invalid hash index: 4294967294 +# CHECK-NEXT: error: Hash[0] has invalid HashData offset: 0x000000b4 +# CHECK-NEXT: error: .apple_names Bucket[1] Hash[1] = 0x0002b60f Str[0] = 0x0000005a DIE[0] = 0x00000001 is not a valid DIE offset for "j". + +# This test is meant to verify that the -verify option +# in llvm-dwarfdump, correctly identifies +# an invalid hash index for bucket[0] in the .apple_names section, +# an invalid HashData offset for Hash[0], as well as +# an invalid DIE offset in the .debug_info section. +# We're reading an invalid DIE due to the incorrect interpretation of DW_FORM for the DIE. +# Instead of DW_FORM_data4 the Atom[0].form is: DW_FORM_flag_present. + + .section __TEXT,__text,regular,pure_instructions + .file 1 "basic.c" + .comm _i,4,2 ## @i + .comm _j,4,2 ## @j + .section __DWARF,__debug_str,regular,debug +Linfo_string: + .asciz "Apple LLVM version 8.1.0 (clang-802.0.35)" ## string offset=0 + .asciz "basic.c" ## string offset=42 + .asciz "/Users/sgravani/Development/tests" ## string offset=50 + .asciz "i" ## string offset=84 + .asciz "int" ## string offset=86 + .asciz "j" ## string offset=90 + .section __DWARF,__debug_info,regular,debug +Lsection_info: + .section __DWARF,__apple_names,regular,debug +Lnames_begin: + .long 1212240712 ## Header Magic + .short 1 ## Header Version + .short 0 ## Header Hash Function + .long 2 ## Header Bucket Count + .long 2 ## Header Hash Count + .long 12 ## Header Data Length + .long 0 ## HeaderData Die Offset Base + .long 1 ## HeaderData Atom Count + .short 1 ## DW_ATOM_die_offset + .short 25 ## DW_FORM_data4 -- error: .apple_names Bucket[1] Hash[1] = 0x0002b60f Str[0] = 0x0000005a DIE[0] = 0x00000001 is not a valid DIE offset for "j". + .long -2 ## Bucket 0 -- error: Bucket[0] has invalid hash index: 4294967294 + .long 1 ## Bucket 1 + .long 177678 ## Hash in Bucket 0 + .long 177679 ## Hash in Bucket 1 + .long Lsection_line ## Offset in Bucket 0 -- error: Hash[0] has invalid HashData offset: 0x000000b4 + .long LNames1-Lnames_begin ## Offset in Bucket 1 +LNames0: + .long 84 ## i + .long 1 ## Num DIEs + .long 30 + .long 0 +LNames1: + .long 90 ## j + .long 1 ## Num DIEs + .long 58 + .long 0 + +.subsections_via_symbols + .section __DWARF,__debug_line,regular,debug +Lsection_line: +Lline_table_start0: Index: test/tools/llvm-dwarfdump/X86/apple_names_verify_form.s =================================================================== --- /dev/null +++ test/tools/llvm-dwarfdump/X86/apple_names_verify_form.s @@ -0,0 +1,58 @@ +# RUN: llvm-mc %s -filetype obj -triple x86_64-apple-darwin -o - \ +# RUN: | not llvm-dwarfdump -verify - \ +# RUN: | FileCheck %s + +# CHECK: Verifying .apple_names... +# CHECK-NEXT: error: unsupported form; failed to read HashData at 0x00000040 + +# This test is meant to verify that the -verify option +# in llvm-dwarfdump, correctly identifies that Atom[0].form is unsupported. +# As a result, the hashdata cannot be read. + + .section __TEXT,__text,regular,pure_instructions + .file 1 "basic.c" + .comm _i,4,2 ## @i + .comm _j,4,2 ## @j + .section __DWARF,__debug_str,regular,debug +Linfo_string: + .asciz "Apple LLVM version 8.1.0 (clang-802.0.35)" ## string offset=0 + .asciz "basic.c" ## string offset=42 + .asciz "/Users/sgravani/Development/tests" ## string offset=50 + .asciz "i" ## string offset=84 + .asciz "int" ## string offset=86 + .asciz "j" ## string offset=90 + .section __DWARF,__debug_info,regular,debug +Lsection_info: + .section __DWARF,__apple_names,regular,debug +Lnames_begin: + .long 1212240712 ## Header Magic + .short 1 ## Header Version + .short 0 ## Header Hash Function + .long 2 ## Header Bucket Count + .long 2 ## Header Hash Count + .long 12 ## Header Data Length + .long 0 ## HeaderData Die Offset Base + .long 1 ## HeaderData Atom Count + .short 1 ## DW_ATOM_die_offset + .short 400 ## DW_FORM_data4 -- error: unsupported form; failed to read HashData at 0x00000040 + .long 0 ## Bucket 0 + .long 1 ## Bucket 1 + .long 177678 ## Hash in Bucket 0 + .long 177679 ## Hash in Bucket 1 + .long LNames0-Lnames_begin ## Offset in Bucket 0 + .long LNames1-Lnames_begin ## Offset in Bucket 1 +LNames0: + .long 84 ## i + .long 1 ## Num DIEs + .long 30 + .long 0 +LNames1: + .long 90 ## j + .long 1 ## Num DIEs + .long 58 + .long 0 + +.subsections_via_symbols + .section __DWARF,__debug_line,regular,debug +Lsection_line: +Lline_table_start0: