Index: llvm/test/tools/llvm-readobj/ELF/dynamic-malformed.test =================================================================== --- llvm/test/tools/llvm-readobj/ELF/dynamic-malformed.test +++ llvm/test/tools/llvm-readobj/ELF/dynamic-malformed.test @@ -245,3 +245,114 @@ VAddr: 0x1000 Sections: - Section: .dynamic + +## Check how we handle cases when the dynamic string table is not null-terminated. +## Check that we are still trying to dump strings and that we do not dump bytes past the +## string table bounds specified by the DT_STRSZ tag. + +## Case A: the value of the DT_STRSZ tag is equal to the size of +## the not null-terminated dynamic string table. +# RUN: yaml2obj %s -DSTRSZ=7 --docnum=6 -o %t6 +# RUN: llvm-readobj --dynamic-table %t6 | FileCheck %s --check-prefix=NOT-TERMINATED -DSTR=test.so +# RUN: llvm-readelf --dynamic-table %t6 | FileCheck %s --check-prefix=NOT-TERMINATED -DSTR=test.so + +# NOT-TERMINATED: {{[(]?}}NEEDED{{[)]?}} Shared library: [] +# NOT-TERMINATED-NEXT: {{[(]?}}FILTER{{[)]?}} Filter library: [] +# NOT-TERMINATED-NEXT: {{[(]?}}AUXILIARY{{[)]?}} Auxiliary library: [] +# NOT-TERMINATED-NEXT: {{[(]?}}USED{{[)]?}} Not needed object: [] +# NOT-TERMINATED-NEXT: {{[(]?}}SONAME{{[)]?}} Library soname: [] +# NOT-TERMINATED-NEXT: {{[(]?}}RPATH{{[)]?}} Library rpath: [] +# NOT-TERMINATED-NEXT: {{[(]?}}RUNPATH{{[)]?}} Library runpath: [] +# NOT-TERMINATED-NEXT: {{[(]?}}NULL{{[)]?}} 0x0 + +## Case B: the value of the DT_STRSZ tag is less than the size of +## the not null-terminated dynamic string table. +# RUN: yaml2obj %s -DSTRSZ=3 --docnum=6 -o %t7 +# RUN: llvm-readobj --dynamic-table %t7 | FileCheck %s --check-prefix=NOT-TERMINATED -DSTR=tes +# RUN: llvm-readelf --dynamic-table %t7 | FileCheck %s --check-prefix=NOT-TERMINATED -DSTR=tes + +## Case C: the value of the DT_STRSZ tag is one byte larger than the size of +## the not null-terminated dynamic string table. +# RUN: yaml2obj %s -DSTRSZ=8 --docnum=6 -o %t8 +# RUN: llvm-readobj --dynamic-table %t8 | FileCheck %s --check-prefix=NOT-TERMINATED -DSTR=test.soa +# RUN: llvm-readelf --dynamic-table %t8 | FileCheck %s --check-prefix=NOT-TERMINATED -DSTR=test.soa + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .dynstr + Type: SHT_STRTAB + Address: 0x1000 + Content: '746573742e736f' ## "test.so", there is no expected null symbol at the end. + - Type: Fill + Pattern: "61626300" ## 'a', 'b', 'c', '\0'. + Size: "4" + - Name: .dynamic + Type: SHT_DYNAMIC + Address: 0x1100 + Entries: + - Tag: DT_STRTAB + Value: 0x1000 + - Tag: DT_STRSZ + Value: [[STRSZ]] + - Tag: DT_NEEDED + Value: 0 + - Tag: DT_FILTER + Value: 0 + - Tag: DT_AUXILIARY + Value: 0 + - Tag: DT_USED + Value: 0 + - Tag: DT_SONAME + Value: 0 + - Tag: DT_RPATH + Value: 0 + - Tag: DT_RUNPATH + Value: 0 + - Tag: DT_NULL + Value: 0 +Symbols: [] +ProgramHeaders: + - Type: PT_LOAD + VAddr: 0x1000 + Sections: + - Section: .dynstr + - Section: .dynamic + - Type: PT_DYNAMIC + VAddr: 0x1100 + Sections: + - Section: .dynamic + +## Check what we print when the dynamic string table ends past the end of the file. + +## Case A: the value of DT_STRSZ tag is set so that the string table ends right before the EOF. +# RUN: yaml2obj %s -DSTRSZ=0x278 --docnum=6 -o %t9.1 +# RUN: llvm-readobj --dynamic-table %t9.1 | FileCheck %s --check-prefix=BEFORE-THE-EOF +# RUN: llvm-readelf --dynamic-table %t9.1 | FileCheck %s --check-prefix=BEFORE-THE-EOF + +# BEFORE-THE-EOF: {{[(]?}}NEEDED{{[)]?}} Shared library: [test.soabc] +# BEFORE-THE-EOF-NEXT: {{[(]?}}FILTER{{[)]?}} Filter library: [test.soabc] +# BEFORE-THE-EOF-NEXT: {{[(]?}}AUXILIARY{{[)]?}} Auxiliary library: [test.soabc] +# BEFORE-THE-EOF-NEXT: {{[(]?}}USED{{[)]?}} Not needed object: [test.soabc] +# BEFORE-THE-EOF-NEXT: {{[(]?}}SONAME{{[)]?}} Library soname: [test.soabc] +# BEFORE-THE-EOF-NEXT: {{[(]?}}RPATH{{[)]?}} Library rpath: [test.soabc] +# BEFORE-THE-EOF-NEXT: {{[(]?}}RUNPATH{{[)]?}} Library runpath: [test.soabc] +# BEFORE-THE-EOF-NEXT: {{[(]?}}NULL{{[)]?}} 0x0 + +# RUN: yaml2obj %s -DSTRSZ=0x279 --docnum=6 -o %t9.2 +# RUN: llvm-readobj --dynamic-table %t9.2 | FileCheck %s --check-prefix=PAST-THE-EOF +# RUN: llvm-readelf --dynamic-table %t9.2 | FileCheck %s --check-prefix=PAST-THE-EOF + +## Case B: the value of DT_STRSZ tag is set so that the string table goes 1 byte past the EOF. +# PAST-THE-EOF: {{[(]?}}NEEDED{{[)]?}} Shared library: [] +# PAST-THE-EOF-NEXT: {{[(]?}}FILTER{{[)]?}} Filter library: [] +# PAST-THE-EOF-NEXT: {{[(]?}}AUXILIARY{{[)]?}} Auxiliary library: [] +# PAST-THE-EOF-NEXT: {{[(]?}}USED{{[)]?}} Not needed object: [] +# PAST-THE-EOF-NEXT: {{[(]?}}SONAME{{[)]?}} Library soname: [] +# PAST-THE-EOF-NEXT: {{[(]?}}RPATH{{[)]?}} Library rpath: [] +# PAST-THE-EOF-NEXT: {{[(]?}}RUNPATH{{[)]?}} Library runpath: [] +# PAST-THE-EOF-NEXT: {{[(]?}}NULL{{[)]?}} 0x0 Index: llvm/test/tools/llvm-readobj/ELF/loadname.test =================================================================== --- llvm/test/tools/llvm-readobj/ELF/loadname.test +++ llvm/test/tools/llvm-readobj/ELF/loadname.test @@ -14,7 +14,7 @@ # GNU: Dynamic section at offset 0x80 contains 4 entries: # GNU-NEXT: Tag Type Name/Value # GNU-NEXT: 0x0000000000000005 (STRTAB) 0x0 -# GNU-NEXT: 0x000000000000000a (STRSZ) 7 (bytes) +# GNU-NEXT: 0x000000000000000a (STRSZ) 8 (bytes) # GNU-NEXT: 0x000000000000000e (SONAME) Library soname: [test.so] # GNU-NEXT: 0x0000000000000000 (NULL) 0x0 @@ -37,7 +37,7 @@ - Tag: DT_STRTAB Value: [[DTSTRTABVAL]] - Tag: DT_STRSZ - Value: 0x0000000000000007 + Value: 0x0000000000000008 - Tag: DT_SONAME Value: 0x0000000000000000 - Tag: DT_NULL Index: llvm/tools/llvm-readobj/ELFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/ELFDumper.cpp +++ llvm/tools/llvm-readobj/ELFDumper.cpp @@ -2561,9 +2561,28 @@ std::string ELFDumper::getDynamicString(uint64_t Value) const { if (DynamicStringTable.empty()) return ""; - if (Value < DynamicStringTable.size()) - return DynamicStringTable.data() + Value; - return Twine("").str(); + if (Value >= DynamicStringTable.size()) + return Twine("").str(); + + const uint64_t FileSize = ObjF->getELFFile()->getBufSize(); + const uint64_t TableOffset = + (const uint8_t *)DynamicStringTable.data() - ObjF->getELFFile()->base(); + if (DynamicStringTable.size() > FileSize - TableOffset) + return ("") + .str(); + + if (DynamicStringTable.back() != '\0') + return ("") + .str(); + + return DynamicStringTable.data() + Value; } template void ELFDumper::printUnwindInfo() {