[llvm-readobj] Allow --hex-dump/--string-dump to dump multiple sections

1) `-x foo` currently dumps one `foo`. This change makes it dump all `foo`.
2) `-x foo -x foo` currently dumps `foo` twice. This change makes it dump `foo` once.
   In addition, if foo has section index 9, `-x foo -x 9` dumps `foo` once.
3) Give a warning instead of an error if `foo` does not exist.

The new behaviors match GNU readelf.

Also, print a new line as a separator between two section dumps.
GNU readelf uses two lines, but one seems good enough.

Reviewed By: grimar, jhenderson

Differential Revision: https://reviews.llvm.org/D63475

llvm-svn: 363683
This commit is contained in:
Fangrui Song 2019-06-18 14:01:03 +00:00
parent d204987ada
commit 677423997d
11 changed files with 223 additions and 166 deletions

View File

@ -32,6 +32,7 @@
# CONTENTS: Hex dump of section '.first':
# CONTENTS-NEXT: 0x00000000 01234567
# CONTENTS-EMPTY:
# CONTENTS-NEXT: Hex dump of section '.second':
# CONTENTS-NEXT: 0x00000000 23456789

View File

@ -37,6 +37,7 @@
# CONTENTS: Hex dump of section '.first':
# CONTENTS-NEXT: 0x00000000 01234567
# CONTENTS-EMPTY:
# CONTENTS-NEXT: Hex dump of section '.second':
# CONTENTS-NEXT: 0x00000000 23456789

View File

@ -0,0 +1,21 @@
# REQUIRES: x86-registered-target
# RUN: llvm-mc -filetype=obj -triple x86_64 %s -o %t.o
# RUN: llvm-readobj -x .a -x .b %t.o | FileCheck %s
# RUN: llvm-readelf -x .a -x .b %t.o | FileCheck %s
# CHECK: Hex dump of section '.a':
# CHECK-NEXT: 0x00000000 00
# CHECK-EMPTY:
# CHECK-NEXT: Hex dump of section '.b':
# CHECK-NEXT: 0x00000000 01
# CHECK-EMPTY:
# CHECK-NEXT: Hex dump of section '.a':
# CHECK-NEXT: 0x00000000 02
.section .a,"a",@progbits,unique,0
.byte 0
.section .b,"a",@progbits
.byte 1
.section .a,"a",@progbits,unique,1
.byte 2

View File

@ -0,0 +1,59 @@
## Test that the -x alias can be used flexibly. Create a baseline and ensure
## all other combinations are identical.
# RUN: llvm-readelf --file-header --hex-dump=.text \
# RUN: %p/Inputs/trivial.obj.elf-x86-64 > %t.hexdump.out
# RUN: llvm-readelf -h --hex-dump .text %p/Inputs/trivial.obj.elf-x86-64 > %t.hexdump.1
# RUN: llvm-readelf -h -x .text %p/Inputs/trivial.obj.elf-x86-64 > %t.hexdump.2
# RUN: llvm-readelf -h -x=.text %p/Inputs/trivial.obj.elf-x86-64 > %t.hexdump.3
# RUN: llvm-readelf -h -x.text %p/Inputs/trivial.obj.elf-x86-64 > %t.hexdump.4
# RUN: llvm-readelf -hx .text %p/Inputs/trivial.obj.elf-x86-64 > %t.hexdump.5
# RUN: llvm-readelf -hx=.text %p/Inputs/trivial.obj.elf-x86-64 > %t.hexdump.6
# RUN: llvm-readelf -hx.text %p/Inputs/trivial.obj.elf-x86-64 > %t.hexdump.7
# RUN: cmp %t.hexdump.out %t.hexdump.1
# RUN: cmp %t.hexdump.out %t.hexdump.2
# RUN: cmp %t.hexdump.out %t.hexdump.3
# RUN: cmp %t.hexdump.out %t.hexdump.4
# RUN: cmp %t.hexdump.out %t.hexdump.5
# RUN: cmp %t.hexdump.out %t.hexdump.6
# RUN: cmp %t.hexdump.out %t.hexdump.7
# RUN: llvm-readelf -S %p/Inputs/trivial.obj.elf-x86-64 | FileCheck %s --check-prefix=ELF-SEC
## Both 9 and .strtab refer to .strtab. Test we dump the section only once.
# RUN: llvm-readobj -x 9 -x 9 -x .strtab -x .strtab %p/Inputs/trivial.obj.elf-x86-64 2>&1 | \
# RUN: FileCheck %s --check-prefix=ELF
# RUN: llvm-readobj -x 9 -x .strtab -x 10 -x not_exist \
# RUN: %p/Inputs/trivial.obj.elf-x86-64 2>&1 | FileCheck %s --check-prefixes=ELF-WARN,ELF
# ELF-SEC: [ 9] .strtab
# ELF-WARN: warning: could not find section 'not_exist'
# ELF-WARN: warning: could not find section 10
# ELF: Hex dump of section '.strtab':
# ELF-NEXT: 0x00000000 00747269 7669616c 2e6c6c00 6d61696e .trivial.ll.main
# ELF-NEXT: 0x00000010 002e4c2e 73747200 70757473 00536f6d ..L.str.puts.Som
# ELF-NEXT: 0x00000020 654f7468 65724675 6e637469 6f6e005f eOtherFunction._
# ELF-NEXT: 0x00000030 474c4f42 414c5f4f 46465345 545f5441 GLOBAL_OFFSET_TA
# ELF-NEXT: 0x00000040 424c455f 00 BLE_.
# ELF-NOT: {{.}}
## Below we test -x can be used for other binary formats.
# RUN: llvm-readobj -x 1 %p/Inputs/trivial.obj.coff-x86-64 \
# RUN: | FileCheck %s --check-prefix COFF
# COFF: 0x00000000 4883ec28 488d0d00 000000e8 00000000 H..(H...........
# COFF: 0x00000010 e8000000 0031c048 83c428c3 .....1.H..(.
# RUN: llvm-readobj -x 1 %p/Inputs/trivial.obj.macho-x86-64 \
# RUN: | FileCheck %s --check-prefix MACHO
# MACHO: 0x00000000 50488d3d 00000000 e8000000 00e80000 PH.=............
# MACHO: 0x00000010 000031c0 5ac3 ..1.Z.
# RUN: llvm-readobj -x 1 %p/Inputs/trivial.obj.wasm \
# RUN: | FileCheck %s --check-prefix WASM
# WASM: 0x00000000 04600001 7f60017f 017f6000 0060017f .`...`....`..`..
# WASM: 0x00000010 00 .

View File

@ -1,32 +0,0 @@
// Check dumping of the hexadecimal bytes of a section.
RUN: llvm-readobj -x .text %p/../../Object/Inputs/hello-world.elf-x86-64 | FileCheck %s
CHECK: Hex dump of section '.text':
CHECK-NEXT: {{^}}0x00400460
// Test that the -x alias can be used flexibly. Create a baseline and ensure
// all other combinations are identical.
RUN: llvm-readelf --file-header --hex-dump=.text \
RUN: %p/../../Object/Inputs/hello-world.elf-x86-64 > %t.hexdump.out
RUN: llvm-readelf -h --hex-dump .text \
RUN: %p/../../Object/Inputs/hello-world.elf-x86-64 > %t.hexdump.1
RUN: llvm-readelf -h -x .text \
RUN: %p/../../Object/Inputs/hello-world.elf-x86-64 > %t.hexdump.2
RUN: llvm-readelf -h -x=.text \
RUN: %p/../../Object/Inputs/hello-world.elf-x86-64 > %t.hexdump.3
RUN: llvm-readelf -h -x.text \
RUN: %p/../../Object/Inputs/hello-world.elf-x86-64 > %t.hexdump.4
RUN: llvm-readelf -hx .text \
RUN: %p/../../Object/Inputs/hello-world.elf-x86-64 > %t.hexdump.5
RUN: llvm-readelf -hx=.text \
RUN: %p/../../Object/Inputs/hello-world.elf-x86-64 > %t.hexdump.6
RUN: llvm-readelf -hx.text \
RUN: %p/../../Object/Inputs/hello-world.elf-x86-64 > %t.hexdump.7
RUN: cmp %t.hexdump.out %t.hexdump.1
RUN: cmp %t.hexdump.out %t.hexdump.2
RUN: cmp %t.hexdump.out %t.hexdump.3
RUN: cmp %t.hexdump.out %t.hexdump.4
RUN: cmp %t.hexdump.out %t.hexdump.5
RUN: cmp %t.hexdump.out %t.hexdump.6
RUN: cmp %t.hexdump.out %t.hexdump.7

View File

@ -1,26 +0,0 @@
RUN: llvm-readobj -x .strtab %p/Inputs/trivial.obj.elf-x86-64 \
RUN: | FileCheck %s --check-prefix ELF
ELF: 0x00000000 00747269 7669616c 2e6c6c00 6d61696e .trivial.ll.main
ELF: 0x00000010 002e4c2e 73747200 70757473 00536f6d ..L.str.puts.Som
ELF: 0x00000020 654f7468 65724675 6e637469 6f6e005f eOtherFunction._
ELF: 0x00000030 474c4f42 414c5f4f 46465345 545f5441 GLOBAL_OFFSET_TA
ELF: 0x00000040 424c455f 00 BLE_.
RUN: llvm-readobj -x 1 %p/Inputs/trivial.obj.coff-x86-64 \
RUN: | FileCheck %s --check-prefix COFF
COFF: 0x00000000 4883ec28 488d0d00 000000e8 00000000 H..(H...........
COFF: 0x00000010 e8000000 0031c048 83c428c3 .....1.H..(.
RUN: llvm-readobj -x 1 %p/Inputs/trivial.obj.macho-x86-64 \
RUN: | FileCheck %s --check-prefix MACHO
MACHO: 0x00000000 50488d3d 00000000 e8000000 00e80000 PH.=............
MACHO: 0x00000010 000031c0 5ac3 ..1.Z.
RUN: llvm-readobj -x 1 %p/Inputs/trivial.obj.wasm \
RUN: | FileCheck %s --check-prefix WASM
WASM: 0x00000000 04600001 7f60017f 017f6000 0060017f .`...`....`..`..
WASM: 0x00000010 00 .

View File

@ -0,0 +1,21 @@
# REQUIRES: x86-registered-target
# RUN: llvm-mc -filetype=obj -triple x86_64 %s -o %t.o
# RUN: llvm-readobj -p .a -p .b %t.o | FileCheck %s
# RUN: llvm-readelf -p .a -p .b %t.o | FileCheck %s
# CHECK: String dump of section '.a':
# CHECK-NEXT: [ 0] 0
# CHECK-EMPTY:
# CHECK-NEXT: String dump of section '.b':
# CHECK-NEXT: [ 0] 1
# CHECK-EMPTY:
# CHECK-NEXT: String dump of section '.a':
# CHECK-NEXT: [ 0] 2
.section .a,"a",@progbits,unique,0
.asciz "0"
.section .b,"a",@progbits
.asciz "1"
.section .a,"a",@progbits,unique,1
.asciz "2"

View File

@ -9,9 +9,9 @@
# flag (-p), with different prefix modes (-p .foo, -p=.foo, -p.foo), and with
# the value being a index section number instead of a section name.
# RUN: llvm-readobj -p=.strings -p=.not_null_terminated %t > %t.readobj.1
# RUN: llvm-readobj -p.strings -p.not_null_terminated %t > %t.readobj.2
# RUN: llvm-readobj -p.strings -p.strings -p.not_null_terminated %t > %t.readobj.2
# RUN: llvm-readobj --string-dump=1 --string-dump=2 %t > %t.readobj.3
# RUN: llvm-readobj -p1 -p2 %t > %t.readobj.4
# RUN: llvm-readobj -p1 -p1 -p2 %t > %t.readobj.4
# RUN: llvm-readobj -p=1 -p=2 %t > %t.readobj.5
# RUN: cmp %t.readobj.out %t.readobj.1
@ -45,19 +45,20 @@
# CHECK-NEXT: [ 5] are
# CHECK-NEXT: [ 9] some
# CHECK-NEXT: [ e] strings
# CHECK-EMPTY:
# CHECK-NEXT: String dump of section '.not_null_terminated':
# CHECK-NEXT: [ 0] no
# CHECK-NEXT: [ 3] null{{$}}
# CHECK-NOT: {{.}}
# RUN: not llvm-readobj --string-dump=does_not_exist %t 2>&1 | FileCheck %s --check-prefix=ERR1
# RUN: not llvm-readobj --string-dump=42 %t 2>&1 | FileCheck %s --check-prefix=ERR2
# RUN: llvm-readobj --string-dump=does_not_exist %t 2>&1 | FileCheck %s --check-prefix=WARN1
# RUN: llvm-readobj --string-dump=42 %t 2>&1 | FileCheck %s --check-prefix=WARN2
# RUN: not llvm-readelf --string-dump=does_not_exist %t 2>&1 | FileCheck %s --check-prefix=ERR1
# RUN: not llvm-readelf --string-dump=42 %t 2>&1 | FileCheck %s --check-prefix=ERR2
# RUN: llvm-readelf --string-dump=does_not_exist %t 2>&1 | FileCheck %s --check-prefix=WARN1
# RUN: llvm-readelf --string-dump=42 %t 2>&1 | FileCheck %s --check-prefix=WARN2
# ERR1: error: could not find section 'does_not_exist'
# ERR2: error: could not find section '42'
# WARN1: warning: could not find section 'does_not_exist'
# WARN2: warning: could not find section 42
--- !ELF
FileHeader:

View File

@ -19,6 +19,7 @@
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
#include <map>
namespace llvm {
@ -32,45 +33,55 @@ static void printAsPrintable(raw_ostream &W, const uint8_t *Start, size_t Len) {
W << (isPrint(Start[i]) ? static_cast<char>(Start[i]) : '.');
}
static Expected<object::SectionRef>
getSecNameOrIndexAsSecRef(const object::ObjectFile *Obj, StringRef SecName) {
char *StrPtr;
long SectionIndex = strtol(SecName.data(), &StrPtr, 10);
long SecIndex;
if (Obj->isELF())
SecIndex = 0;
static std::vector<object::SectionRef>
getSectionRefsByNameOrIndex(const object::ObjectFile *Obj,
ArrayRef<std::string> Sections) {
std::vector<object::SectionRef> Ret;
std::map<std::string, bool> SecNames;
std::map<unsigned, bool> SecIndices;
unsigned SecIndex;
for (StringRef Section : Sections) {
if (!Section.getAsInteger(0, SecIndex))
SecIndices.emplace(SecIndex, false);
else
SecIndex = 1;
SecNames.emplace(Section, false);
}
SecIndex = Obj->isELF() ? 0 : 1;
for (object::SectionRef SecRef : Obj->sections()) {
if (*StrPtr) {
StringRef SectionName;
if (std::error_code E = SecRef.getName(SectionName))
return errorCodeToError(E);
if (SectionName == SecName)
return SecRef;
} else if (SecIndex == SectionIndex)
return SecRef;
StringRef SecName;
error(SecRef.getName(SecName));
auto NameIt = SecNames.find(SecName);
if (NameIt != SecNames.end())
NameIt->second = true;
auto IndexIt = SecIndices.find(SecIndex);
if (IndexIt != SecIndices.end())
IndexIt->second = true;
if (NameIt != SecNames.end() || IndexIt != SecIndices.end())
Ret.push_back(SecRef);
SecIndex++;
}
return make_error<StringError>(
formatv("could not find section '{0}'", SecName),
object::object_error::parse_failed);
for (const std::pair<std::string, bool> &S : SecNames)
if (!S.second)
reportWarning(formatv("could not find section '{0}'", S.first).str());
for (std::pair<unsigned, bool> S : SecIndices)
if (!S.second)
reportWarning(formatv("could not find section {0}", S.first).str());
return Ret;
}
void ObjDumper::printSectionAsString(const object::ObjectFile *Obj,
StringRef SecName) {
Expected<object::SectionRef> SectionRefOrError =
getSecNameOrIndexAsSecRef(Obj, SecName);
if (!SectionRefOrError)
error(std::move(SectionRefOrError));
object::SectionRef Section = *SectionRefOrError;
void ObjDumper::printSectionsAsString(const object::ObjectFile *Obj,
ArrayRef<std::string> Sections) {
bool First = true;
for (object::SectionRef Section :
getSectionRefsByNameOrIndex(Obj, Sections)) {
StringRef SectionName;
if (std::error_code E = Section.getName(SectionName))
error(E);
error(Section.getName(SectionName));
if (!First)
W.startLine() << '\n';
First = false;
W.startLine() << "String dump of section '" << SectionName << "':\n";
StringRef SectionContent = unwrapOrError(Section.getContents());
@ -92,18 +103,18 @@ void ObjDumper::printSectionAsString(const object::ObjectFile *Obj,
CurrentWord += WordSize + 1;
}
}
}
void ObjDumper::printSectionAsHex(const object::ObjectFile *Obj,
StringRef SecName) {
Expected<object::SectionRef> SectionRefOrError =
getSecNameOrIndexAsSecRef(Obj, SecName);
if (!SectionRefOrError)
error(std::move(SectionRefOrError));
object::SectionRef Section = *SectionRefOrError;
void ObjDumper::printSectionsAsHex(const object::ObjectFile *Obj,
ArrayRef<std::string> Sections) {
bool First = true;
for (object::SectionRef Section :
getSectionRefsByNameOrIndex(Obj, Sections)) {
StringRef SectionName;
if (std::error_code E = Section.getName(SectionName))
error(E);
error(Section.getName(SectionName));
if (!First)
W.startLine() << '\n';
First = false;
W.startLine() << "Hex dump of section '" << SectionName << "':\n";
StringRef SectionContent = unwrapOrError(Section.getContents());
@ -137,11 +148,13 @@ void ObjDumper::printSectionAsHex(const object::ObjectFile *Obj,
TmpSecPtr = SecPtr;
for (i = 0; TmpSecPtr + i < SecEnd && i < 16; ++i)
W.startLine() << (isPrint(TmpSecPtr[i]) ? static_cast<char>(TmpSecPtr[i])
W.startLine() << (isPrint(TmpSecPtr[i])
? static_cast<char>(TmpSecPtr[i])
: '.');
W.startLine() << '\n';
}
}
}
} // namespace llvm

View File

@ -104,8 +104,10 @@ public:
virtual void printStackMap() const = 0;
void printSectionAsString(const object::ObjectFile *Obj, StringRef SecName);
void printSectionAsHex(const object::ObjectFile *Obj, StringRef SecName);
void printSectionsAsString(const object::ObjectFile *Obj,
ArrayRef<std::string> Sections);
void printSectionsAsHex(const object::ObjectFile *Obj,
ArrayRef<std::string> Sections);
protected:
ScopedPrinter &W;

View File

@ -495,13 +495,9 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer) {
if (opts::ProgramHeaders || opts::SectionMapping == cl::BOU_TRUE)
Dumper->printProgramHeaders(opts::ProgramHeaders, opts::SectionMapping);
if (!opts::StringDump.empty())
llvm::for_each(opts::StringDump, [&Dumper, Obj](StringRef SectionName) {
Dumper->printSectionAsString(Obj, SectionName);
});
Dumper->printSectionsAsString(Obj, opts::StringDump);
if (!opts::HexDump.empty())
llvm::for_each(opts::HexDump, [&Dumper, Obj](StringRef SectionName) {
Dumper->printSectionAsHex(Obj, SectionName);
});
Dumper->printSectionsAsHex(Obj, opts::HexDump);
if (opts::HashTable)
Dumper->printHashTable();
if (opts::GnuHashTable)