aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2018-01-24 20:26:03 +0000
committerDimitry Andric <dim@FreeBSD.org>2018-01-24 20:26:03 +0000
commita506d0d6a9a6c2745e058e35ad4c62d1ddc5f20e (patch)
tree1eabba27e7647f059f5df7d9ff1fe39e08acda74
parent97dd191f563d0b295e4abc86dc95a4f6276c6d8f (diff)
downloadsrc-a506d0d6a9a6c2745e058e35ad4c62d1ddc5f20e.tar.gz
src-a506d0d6a9a6c2745e058e35ad4c62d1ddc5f20e.zip
Vendor import of lld release_60 branch r323338:vendor/lld/lld-release_60-r323338
Notes
Notes: svn path=/vendor/lld/dist-release_60/; revision=328370 svn path=/vendor/lld/lld-release_60-r323338/; revision=328371; tag=vendor/lld/lld-release_60-r323338
-rw-r--r--COFF/Driver.cpp1
-rw-r--r--ELF/LinkerScript.cpp28
-rw-r--r--ELF/OutputSections.cpp9
-rw-r--r--ELF/OutputSections.h1
-rw-r--r--ELF/ScriptParser.cpp11
-rw-r--r--ELF/SymbolTable.cpp3
-rw-r--r--ELF/SyntheticSections.cpp7
-rw-r--r--ELF/Writer.cpp7
-rw-r--r--test/ELF/Inputs/as-needed-lazy.s3
-rw-r--r--test/ELF/Inputs/compress-debug.s5
-rw-r--r--test/ELF/as-needed-lazy.s14
-rw-r--r--test/ELF/compress-debug-sections-reloc.s26
-rw-r--r--test/ELF/linkerscript/at-self-reference.s63
-rw-r--r--test/ELF/linkerscript/at2.s81
-rw-r--r--test/ELF/linkerscript/compress-debug-sections-custom.s35
-rw-r--r--test/ELF/linkerscript/parse-section-in-addr.s10
-rw-r--r--test/ELF/sysv-hash-no-rosegment.s13
17 files changed, 289 insertions, 28 deletions
diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp
index 1aaec355c7a5..0f3d8fb0b4ef 100644
--- a/COFF/Driver.cpp
+++ b/COFF/Driver.cpp
@@ -57,6 +57,7 @@ bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) {
errorHandler().ErrorLimitExceededMsg =
"too many errors emitted, stopping now"
" (use /ERRORLIMIT:0 to see all errors)";
+ errorHandler().ExitEarly = CanExitEarly;
Config = make<Configuration>();
Config->Argv = {Args.begin(), Args.end()};
Config->CanExitEarly = CanExitEarly;
diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp
index 8f50a977fd75..33a618952456 100644
--- a/ELF/LinkerScript.cpp
+++ b/ELF/LinkerScript.cpp
@@ -608,13 +608,6 @@ void LinkerScript::switchTo(OutputSection *Sec) {
Ctx->OutSec = Sec;
Ctx->OutSec->Addr = advance(0, Ctx->OutSec->Alignment);
-
- // If neither AT nor AT> is specified for an allocatable section, the linker
- // will set the LMA such that the difference between VMA and LMA for the
- // section is the same as the preceding output section in the same region
- // https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html
- if (Ctx->LMAOffset)
- Ctx->OutSec->LMAOffset = Ctx->LMAOffset();
}
// This function searches for a memory region to place the given output
@@ -662,17 +655,28 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
if (Ctx->MemRegion)
Dot = Ctx->MemRegionOffset[Ctx->MemRegion];
+ switchTo(Sec);
+
if (Sec->LMAExpr) {
uint64_t D = Dot;
Ctx->LMAOffset = [=] { return Sec->LMAExpr().getValue() - D; };
}
- switchTo(Sec);
+ if (!Sec->LMARegionName.empty()) {
+ if (MemoryRegion *MR = MemoryRegions.lookup(Sec->LMARegionName)) {
+ uint64_t Offset = MR->Origin - Dot;
+ Ctx->LMAOffset = [=] { return Offset; };
+ } else {
+ error("memory region '" + Sec->LMARegionName + "' not declared");
+ }
+ }
- // We do not support custom layout for compressed debug sectons.
- // At this point we already know their size and have compressed content.
- if (Ctx->OutSec->Flags & SHF_COMPRESSED)
- return;
+ // If neither AT nor AT> is specified for an allocatable section, the linker
+ // will set the LMA such that the difference between VMA and LMA for the
+ // section is the same as the preceding output section in the same region
+ // https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html
+ if (Ctx->LMAOffset)
+ Ctx->OutSec->LMAOffset = Ctx->LMAOffset();
// The Size previously denoted how many InputSections had been added to this
// section, and was used for sorting SHF_LINK_ORDER sections. Reset it to
diff --git a/ELF/OutputSections.cpp b/ELF/OutputSections.cpp
index f0677f7e1ca5..94c98284196f 100644
--- a/ELF/OutputSections.cpp
+++ b/ELF/OutputSections.cpp
@@ -183,15 +183,6 @@ template <class ELFT> void OutputSection::maybeCompress() {
!Name.startswith(".debug_"))
return;
- // Calculate the section offsets and size pre-compression.
- Size = 0;
- for (BaseCommand *Cmd : SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(Cmd))
- for (InputSection *IS : ISD->Sections) {
- IS->OutSecOff = alignTo(Size, IS->Alignment);
- this->Size = IS->OutSecOff + IS->getSize();
- }
-
// Create a section header.
ZDebugHeader.resize(sizeof(Elf_Chdr));
auto *Hdr = reinterpret_cast<Elf_Chdr *>(ZDebugHeader.data());
diff --git a/ELF/OutputSections.h b/ELF/OutputSections.h
index b2845773e9af..009f45c03333 100644
--- a/ELF/OutputSections.h
+++ b/ELF/OutputSections.h
@@ -99,6 +99,7 @@ public:
ConstraintKind Constraint = ConstraintKind::NoConstraint;
std::string Location;
std::string MemoryRegionName;
+ std::string LMARegionName;
bool Noload = false;
template <class ELFT> void finalize();
diff --git a/ELF/ScriptParser.cpp b/ELF/ScriptParser.cpp
index 4263944981f2..e068beeee262 100644
--- a/ELF/ScriptParser.cpp
+++ b/ELF/ScriptParser.cpp
@@ -709,6 +709,14 @@ OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
if (consume(">"))
Cmd->MemoryRegionName = next();
+ if (consume("AT")) {
+ expect(">");
+ Cmd->LMARegionName = next();
+ }
+
+ if (Cmd->LMAExpr && !Cmd->LMARegionName.empty())
+ error("section can't have both LMA and a load region");
+
Cmd->Phdrs = readOutputSectionPhdrs();
if (consume("="))
@@ -922,7 +930,10 @@ ByteCommand *ScriptParser::readByteCommand(StringRef Tok) {
StringRef ScriptParser::readParenLiteral() {
expect("(");
+ bool Orig = InExpr;
+ InExpr = false;
StringRef Tok = next();
+ InExpr = Orig;
expect(")");
return Tok;
}
diff --git a/ELF/SymbolTable.cpp b/ELF/SymbolTable.cpp
index b6bf21998863..c3a00bea4aaa 100644
--- a/ELF/SymbolTable.cpp
+++ b/ELF/SymbolTable.cpp
@@ -491,12 +491,13 @@ void SymbolTable::addShared(StringRef Name, SharedFile<ELFT> &File,
if (WasInserted || ((S->isUndefined() || S->isLazy()) &&
S->getVisibility() == STV_DEFAULT)) {
uint8_t Binding = S->Binding;
+ bool WasUndefined = S->isUndefined();
replaceSymbol<SharedSymbol>(S, File, Name, Sym.getBinding(), Sym.st_other,
Sym.getType(), Sym.st_value, Sym.st_size,
Alignment, VerdefIndex);
if (!WasInserted) {
S->Binding = Binding;
- if (!S->isWeak() && !Config->GcSections)
+ if (!S->isWeak() && !Config->GcSections && WasUndefined)
File.IsNeeded = true;
}
}
diff --git a/ELF/SyntheticSections.cpp b/ELF/SyntheticSections.cpp
index f878acf8cf3f..a5a851f95400 100644
--- a/ELF/SyntheticSections.cpp
+++ b/ELF/SyntheticSections.cpp
@@ -1823,6 +1823,9 @@ void HashTableSection::finalizeContents() {
}
void HashTableSection::writeTo(uint8_t *Buf) {
+ // See comment in GnuHashTableSection::writeTo.
+ memset(Buf, 0, Size);
+
unsigned NumSymbols = InX::DynSymTab->getNumSymbols();
uint32_t *P = reinterpret_cast<uint32_t *>(Buf);
@@ -2435,10 +2438,8 @@ void MergeNoTailSection::finalizeContents() {
parallelForEachN(0, Concurrency, [&](size_t ThreadId) {
for (MergeInputSection *Sec : Sections) {
for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) {
- if (!Sec->Pieces[I].Live)
- continue;
size_t ShardId = getShardId(Sec->Pieces[I].Hash);
- if ((ShardId & (Concurrency - 1)) == ThreadId)
+ if ((ShardId & (Concurrency - 1)) == ThreadId && Sec->Pieces[I].Live)
Sec->Pieces[I].OutputOff = Shards[ShardId].add(Sec->getData(I));
}
}
diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp
index 24c3e1ee207c..5feff456ffa9 100644
--- a/ELF/Writer.cpp
+++ b/ELF/Writer.cpp
@@ -427,13 +427,14 @@ template <class ELFT> void Writer<ELFT>::run() {
if (errorCount())
return;
+ Script->assignAddresses();
+
// If -compressed-debug-sections is specified, we need to compress
// .debug_* sections. Do it right now because it changes the size of
// output sections.
- parallelForEach(OutputSections,
- [](OutputSection *Sec) { Sec->maybeCompress<ELFT>(); });
+ for (OutputSection *Sec : OutputSections)
+ Sec->maybeCompress<ELFT>();
- Script->assignAddresses();
Script->allocateHeaders(Phdrs);
// Remove empty PT_LOAD to avoid causing the dynamic linker to try to mmap a
diff --git a/test/ELF/Inputs/as-needed-lazy.s b/test/ELF/Inputs/as-needed-lazy.s
new file mode 100644
index 000000000000..7f9c360dda20
--- /dev/null
+++ b/test/ELF/Inputs/as-needed-lazy.s
@@ -0,0 +1,3 @@
+.global foo
+foo:
+ nop
diff --git a/test/ELF/Inputs/compress-debug.s b/test/ELF/Inputs/compress-debug.s
new file mode 100644
index 000000000000..5fd9d39a98a0
--- /dev/null
+++ b/test/ELF/Inputs/compress-debug.s
@@ -0,0 +1,5 @@
+.text
+.fill 0x44
+
+.section .debug_info,"",@progbits
+.fill 0x43
diff --git a/test/ELF/as-needed-lazy.s b/test/ELF/as-needed-lazy.s
new file mode 100644
index 000000000000..e892b9980aad
--- /dev/null
+++ b/test/ELF/as-needed-lazy.s
@@ -0,0 +1,14 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/as-needed-lazy.s -o %t2.o
+# RUN: ld.lld %t2.o -o %t2.so -shared
+# RUN: rm -f %t2.a
+# RUN: llvm-ar rc %t2.a %t2.o
+# RUN: ld.lld %t1.o %t2.a --as-needed %t2.so -o %t
+# RUN: llvm-readobj -d %t | FileCheck %s
+
+# CHECK-NOT: NEEDED
+
+.global _start
+_start:
+ nop
diff --git a/test/ELF/compress-debug-sections-reloc.s b/test/ELF/compress-debug-sections-reloc.s
new file mode 100644
index 000000000000..b4ee4ea6dd97
--- /dev/null
+++ b/test/ELF/compress-debug-sections-reloc.s
@@ -0,0 +1,26 @@
+# REQUIRES: x86, zlib
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/compress-debug.s -o %t2.o
+# RUN: ld.lld %t2.o %t.o -o %t1 --compress-debug-sections=zlib -Ttext=0
+# RUN: llvm-dwarfdump %t1 -debug-str | FileCheck %s
+# These two checks correspond to the patched values of a_sym and a_debug_sym.
+# D = 0x44 - address of .text input section for this file (the start address of
+# .text is 0 as requested on the command line, and the size of the
+# preceding .text in the other input file is 0x44).
+# C = 0x43 - offset of .debug_info section for this file (the size of
+# the preceding .debug_info from the other input file is 0x43).
+# CHECK: 0x00000000: "D"
+# CHECK: 0x00000004: "C"
+
+.text
+a_sym:
+nop
+
+.section .debug_str,"",@progbits
+.long a_sym
+.long a_debug_sym
+
+.section .debug_info,"",@progbits
+a_debug_sym:
+.long 0x88776655
diff --git a/test/ELF/linkerscript/at-self-reference.s b/test/ELF/linkerscript/at-self-reference.s
new file mode 100644
index 000000000000..7208a4b9fcd4
--- /dev/null
+++ b/test/ELF/linkerscript/at-self-reference.s
@@ -0,0 +1,63 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x1000; \
+# RUN: .aaa : AT(ADDR(.aaa)) { *(.aaa) } \
+# RUN: .bbb : AT(ADDR(.bbb)) { *(.bbb) } \
+# RUN: }" > %t.script
+# RUN: ld.lld %t --script %t.script -o %t2
+# RUN: llvm-readobj -program-headers %t2 | FileCheck %s
+
+# CHECK: ProgramHeaders [
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD (0x1)
+# CHECK-NEXT: Offset: 0x1000
+# CHECK-NEXT: VirtualAddress: 0x1000
+# CHECK-NEXT: PhysicalAddress: 0x1000
+# CHECK-NEXT: FileSize: 3
+# CHECK-NEXT: MemSize: 3
+# CHECK-NEXT: Flags [ (0x5)
+# CHECK-NEXT: PF_R (0x4)
+# CHECK-NEXT: PF_X (0x1)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 4096
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD (0x1)
+# CHECK-NEXT: Offset: 0x1008
+# CHECK-NEXT: VirtualAddress: 0x1008
+# CHECK-NEXT: PhysicalAddress: 0x1008
+# CHECK-NEXT: FileSize: 9
+# CHECK-NEXT: MemSize: 9
+# CHECK-NEXT: Flags [ (0x5)
+# CHECK-NEXT: PF_R (0x4)
+# CHECK-NEXT: PF_X (0x1)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 4096
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_GNU_STACK (0x6474E551)
+# CHECK-NEXT: Offset: 0x0
+# CHECK-NEXT: VirtualAddress: 0x0
+# CHECK-NEXT: PhysicalAddress: 0x0
+# CHECK-NEXT: FileSize: 0
+# CHECK-NEXT: MemSize: 0
+# CHECK-NEXT: Flags [ (0x6)
+# CHECK-NEXT: PF_R (0x4)
+# CHECK-NEXT: PF_W (0x2)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 0
+# CHECK-NEXT: }
+# CHECK-NEXT:]
+
+.global _start
+_start:
+ nop
+
+
+.section .aaa, "a"
+.asciz "aa"
+
+.section .bbb, "a"
+.align 8
+.quad 0
diff --git a/test/ELF/linkerscript/at2.s b/test/ELF/linkerscript/at2.s
new file mode 100644
index 000000000000..1545b1d826a3
--- /dev/null
+++ b/test/ELF/linkerscript/at2.s
@@ -0,0 +1,81 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "MEMORY { \
+# RUN: AX (ax) : ORIGIN = 0x2000, LENGTH = 0x100 \
+# RUN: AW (aw) : ORIGIN = 0x3000, LENGTH = 0x100 \
+# RUN: FLASH (ax) : ORIGIN = 0x6000, LENGTH = 0x100 \
+# RUN: RAM (aw) : ORIGIN = 0x7000, LENGTH = 0x100 } \
+# RUN: SECTIONS { \
+# RUN: .foo1 : { *(.foo1) } > AX AT>FLASH \
+# RUN: .foo2 : { *(.foo2) } > AX \
+# RUN: .bar1 : { *(.bar1) } > AW AT> RAM \
+# RUN: .bar2 : { *(.bar2) } > AW AT > RAM \
+# RUN: .bar3 : { *(.bar3) } > AW AT >RAM \
+# RUN: }" > %t.script
+# RUN: ld.lld %t --script %t.script -o %t2
+# RUN: llvm-readobj -program-headers %t2 | FileCheck %s
+# RUN: llvm-objdump -section-headers %t2 | FileCheck %s --check-prefix=SECTIONS
+
+# CHECK: ProgramHeaders [
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x1000
+# CHECK-NEXT: VirtualAddress: 0x2000
+# CHECK-NEXT: PhysicalAddress: 0x6000
+# CHECK-NEXT: FileSize: 16
+# CHECK-NEXT: MemSize: 16
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: PF_X
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment:
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x2000
+# CHECK-NEXT: VirtualAddress: 0x3000
+# CHECK-NEXT: PhysicalAddress: 0x7000
+# CHECK-NEXT: FileSize: 24
+# CHECK-NEXT: MemSize: 24
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: PF_W
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 4096
+# CHECK-NEXT: }
+
+# SECTIONS: Sections:
+# SECTIONS-NEXT: Idx Name Size Address
+# SECTIONS-NEXT: 0 00000000 0000000000000000
+# SECTIONS-NEXT: 1 .foo1 00000008 0000000000002000
+# SECTIONS-NEXT: 2 .foo2 00000008 0000000000002008
+# SECTIONS-NEXT: 3 .text 00000000 0000000000002010
+# SECTIONS-NEXT: 4 .bar1 00000008 0000000000003000
+# SECTIONS-NEXT: 5 .bar2 00000008 0000000000003008
+# SECTIONS-NEXT: 6 .bar3 00000008 0000000000003010
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "MEMORY { \
+# RUN: FLASH (ax) : ORIGIN = 0x2000, LENGTH = 0x100 \
+# RUN: RAM (aw) : ORIGIN = 0x5000, LENGTH = 0x100 } \
+# RUN: SECTIONS { \
+# RUN: .foo1 : AT(0x500) { *(.foo1) } > FLASH AT>FLASH \
+# RUN: }" > %t2.script
+# RUN: not ld.lld %t --script %t2.script -o %t2 2>&1 | \
+# RUN: FileCheck %s --check-prefix=ERR
+# ERR: error: section can't have both LMA and a load region
+
+.section .foo1, "ax"
+.quad 0
+
+.section .foo2, "ax"
+.quad 0
+
+.section .bar1, "aw"
+.quad 0
+
+.section .bar2, "aw"
+.quad 0
+
+.section .bar3, "aw"
+.quad 0
diff --git a/test/ELF/linkerscript/compress-debug-sections-custom.s b/test/ELF/linkerscript/compress-debug-sections-custom.s
new file mode 100644
index 000000000000..31fdd56381b0
--- /dev/null
+++ b/test/ELF/linkerscript/compress-debug-sections-custom.s
@@ -0,0 +1,35 @@
+# REQUIRES: x86, zlib
+
+# RUN: echo "SECTIONS { \
+# RUN: .text : { . += 0x10; *(.text) } \
+# RUN: .debug_str : { . += 0x10; *(.debug_str) } \
+# RUN: .debug_info : { . += 0x10; *(.debug_info) } \
+# RUN: }" > %t.script
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/../Inputs/compress-debug.s -o %t2.o
+# RUN: ld.lld %t2.o %t.o -o %t1 --compress-debug-sections=zlib -T %t.script
+# RUN: llvm-dwarfdump %t1 -debug-str | FileCheck %s
+# These two checks correspond to the patched values of a_sym and a_debug_sym.
+# T = 0x54 - address of .text input section for this file (the start address of
+# .text is 0 by default, the size of the preceding .text in the other input
+# file is 0x44, and the linker script adds an additional 0x10).
+# S = 0x53 - offset of .debug_info section for this file (the size of
+# the preceding .debug_info from the other input file is 0x43, and the
+# linker script adds an additional 0x10).
+# Also note that the .debug_str offsets are also offset by 0x10, as directed by
+# the linker script.
+# CHECK: 0x00000010: "T"
+# CHECK: 0x00000014: "S"
+
+.text
+a_sym:
+nop
+
+.section .debug_str,"",@progbits
+.long a_sym
+.long a_debug_sym
+
+.section .debug_info,"",@progbits
+a_debug_sym:
+.long 0x88776655
diff --git a/test/ELF/linkerscript/parse-section-in-addr.s b/test/ELF/linkerscript/parse-section-in-addr.s
new file mode 100644
index 000000000000..7a79f646310d
--- /dev/null
+++ b/test/ELF/linkerscript/parse-section-in-addr.s
@@ -0,0 +1,10 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+
+# RUN: echo "SECTIONS { \
+# RUN: .foo-bar : AT(ADDR(.foo-bar)) { *(.text) } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t.so --script %t.script %t.o -shared
+# RUN: llvm-readelf -S %t.so | FileCheck %s
+
+# CHECK: .foo-bar
diff --git a/test/ELF/sysv-hash-no-rosegment.s b/test/ELF/sysv-hash-no-rosegment.s
new file mode 100644
index 000000000000..31b9d2fbec05
--- /dev/null
+++ b/test/ELF/sysv-hash-no-rosegment.s
@@ -0,0 +1,13 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld -shared --no-rosegment -o %t %t.o
+# RUN: llvm-readobj -hash-table %t | FileCheck %s
+
+# CHECK: HashTable {
+# CHECK-NEXT: Num Buckets: 2
+# CHECK-NEXT: Num Chains: 2
+# CHECK-NEXT: Buckets: [1, 0]
+# CHECK-NEXT: Chains: [0, 0]
+# CHECK-NEXT: }
+
+callq undef@PLT