aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2017-01-02 19:19:15 +0000
committerDimitry Andric <dim@FreeBSD.org>2017-01-02 19:19:15 +0000
commitd93e1dfac8711cfed1a9d9cd1876a788b83945cd (patch)
tree5896fa6c02a262a6148b215487e545d937de58b7
parent8d43286d630f9224de07809ea253e83ebb9cdee6 (diff)
downloadsrc-d93e1dfac8711cfed1a9d9cd1876a788b83945cd.tar.gz
src-d93e1dfac8711cfed1a9d9cd1876a788b83945cd.zip
Vendor import of lld trunk r290819:vendor/lld/lld-trunk-r290819
Notes
Notes: svn path=/vendor/lld/dist/; revision=311125 svn path=/vendor/lld/lld-trunk-r290819/; revision=311126; tag=vendor/lld/lld-trunk-r290819
-rw-r--r--CMakeLists.txt61
-rw-r--r--COFF/CMakeLists.txt18
-rw-r--r--COFF/Chunks.cpp32
-rw-r--r--COFF/Chunks.h7
-rw-r--r--COFF/Config.h28
-rw-r--r--COFF/DLL.cpp2
-rw-r--r--COFF/Driver.cpp428
-rw-r--r--COFF/Driver.h38
-rw-r--r--COFF/DriverUtils.cpp194
-rw-r--r--COFF/Error.cpp21
-rw-r--r--COFF/Error.h17
-rw-r--r--COFF/ICF.cpp278
-rw-r--r--COFF/InputFiles.cpp179
-rw-r--r--COFF/InputFiles.h68
-rw-r--r--COFF/Librarian.cpp39
-rw-r--r--COFF/MarkLive.cpp6
-rw-r--r--COFF/Memory.h52
-rw-r--r--COFF/ModuleDef.cpp12
-rw-r--r--COFF/Options.td8
-rw-r--r--COFF/PDB.cpp214
-rw-r--r--COFF/PDB.h25
-rw-r--r--COFF/Strings.cpp30
-rw-r--r--COFF/Strings.h23
-rw-r--r--COFF/SymbolTable.cpp494
-rw-r--r--COFF/SymbolTable.h66
-rw-r--r--COFF/Symbols.cpp178
-rw-r--r--COFF/Symbols.h142
-rw-r--r--COFF/Writer.cpp198
-rw-r--r--COFF/Writer.h4
-rw-r--r--ELF/CMakeLists.txt17
-rw-r--r--ELF/Config.h67
-rw-r--r--ELF/Driver.cpp553
-rw-r--r--ELF/Driver.h51
-rw-r--r--ELF/DriverUtils.cpp222
-rw-r--r--ELF/EhFrame.cpp147
-rw-r--r--ELF/EhFrame.h8
-rw-r--r--ELF/Error.cpp91
-rw-r--r--ELF/Error.h44
-rw-r--r--ELF/GdbIndex.cpp205
-rw-r--r--ELF/GdbIndex.h99
-rw-r--r--ELF/ICF.cpp508
-rw-r--r--ELF/InputFiles.cpp727
-rw-r--r--ELF/InputFiles.h123
-rw-r--r--ELF/InputSection.cpp677
-rw-r--r--ELF/InputSection.h295
-rw-r--r--ELF/LTO.cpp376
-rw-r--r--ELF/LTO.h24
-rw-r--r--ELF/LinkerScript.cpp2060
-rw-r--r--ELF/LinkerScript.h275
-rw-r--r--ELF/MarkLive.cpp150
-rw-r--r--ELF/Memory.h67
-rw-r--r--ELF/Mips.cpp369
-rw-r--r--ELF/Options.td111
-rw-r--r--ELF/OutputSections.cpp1768
-rw-r--r--ELF/OutputSections.h659
-rw-r--r--ELF/Relocations.cpp501
-rw-r--r--ELF/Relocations.h49
-rw-r--r--ELF/ScriptParser.cpp133
-rw-r--r--ELF/ScriptParser.h24
-rw-r--r--ELF/Strings.cpp80
-rw-r--r--ELF/Strings.h65
-rw-r--r--ELF/SymbolListFile.cpp168
-rw-r--r--ELF/SymbolListFile.h27
-rw-r--r--ELF/SymbolTable.cpp581
-rw-r--r--ELF/SymbolTable.h91
-rw-r--r--ELF/Symbols.cpp176
-rw-r--r--ELF/Symbols.h153
-rw-r--r--ELF/SyntheticSections.cpp1990
-rw-r--r--ELF/SyntheticSections.h747
-rw-r--r--ELF/Target.cpp608
-rw-r--r--ELF/Target.h20
-rw-r--r--ELF/Threads.h90
-rw-r--r--ELF/Thunks.cpp37
-rw-r--r--ELF/Writer.cpp1742
-rw-r--r--ELF/Writer.h48
-rw-r--r--cmake/modules/AddLLD.cmake45
-rw-r--r--docs/Driver.rst2
-rw-r--r--docs/NewLLD.rst6
-rw-r--r--docs/ReleaseNotes.rst101
-rw-r--r--docs/conf.py4
-rw-r--r--docs/windows_support.rst2
-rw-r--r--include/lld/Config/Version.h34
-rw-r--r--include/lld/Config/Version.inc.in1
-rw-r--r--include/lld/Core/Atom.h16
-rw-r--r--include/lld/Core/DefinedAtom.h4
-rw-r--r--include/lld/Core/LinkingContext.h21
-rw-r--r--include/lld/Core/Node.h11
-rw-r--r--include/lld/Core/Parallel.h69
-rw-r--r--include/lld/Core/Pass.h13
-rw-r--r--include/lld/Core/PassManager.h2
-rw-r--r--include/lld/Core/Reader.h17
-rw-r--r--include/lld/Core/Reference.h10
-rw-r--r--include/lld/Core/Reproduce.h73
-rw-r--r--include/lld/Core/Simple.h83
-rw-r--r--include/lld/Core/SymbolTable.h10
-rw-r--r--include/lld/Driver/Driver.h2
-rw-r--r--include/lld/ReaderWriter/MachOLinkingContext.h6
-rw-r--r--include/lld/Support/Memory.h63
-rw-r--r--lib/Config/Version.cpp52
-rw-r--r--lib/Core/CMakeLists.txt1
-rw-r--r--lib/Core/DefinedAtom.cpp12
-rw-r--r--lib/Core/Error.cpp6
-rw-r--r--lib/Core/File.cpp5
-rw-r--r--lib/Core/LinkingContext.cpp11
-rw-r--r--lib/Core/Reader.cpp10
-rw-r--r--lib/Core/Reproduce.cpp128
-rw-r--r--lib/Core/Resolver.cpp2
-rw-r--r--lib/Core/SymbolTable.cpp32
-rw-r--r--lib/Core/Writer.cpp9
-rw-r--r--lib/Driver/DarwinLdDriver.cpp68
-rw-r--r--lib/ReaderWriter/FileArchive.cpp23
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_arm.cpp20
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_arm64.cpp34
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_x86.cpp4
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp21
-rw-r--r--lib/ReaderWriter/MachO/CMakeLists.txt2
-rw-r--r--lib/ReaderWriter/MachO/CompactUnwindPass.cpp4
-rw-r--r--lib/ReaderWriter/MachO/DebugInfo.h106
-rw-r--r--lib/ReaderWriter/MachO/File.h18
-rw-r--r--lib/ReaderWriter/MachO/GOTPass.cpp2
-rw-r--r--lib/ReaderWriter/MachO/LayoutPass.cpp2
-rw-r--r--lib/ReaderWriter/MachO/MachOLinkingContext.cpp63
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFile.h3
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp14
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h9
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp163
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp220
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp372
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp2
-rw-r--r--lib/ReaderWriter/MachO/ObjCPass.cpp2
-rw-r--r--lib/ReaderWriter/MachO/ShimPass.cpp4
-rw-r--r--lib/ReaderWriter/MachO/StubsPass.cpp6
-rw-r--r--lib/ReaderWriter/MachO/TLVPass.cpp2
-rw-r--r--lib/ReaderWriter/YAML/ReaderWriterYAML.cpp31
-rw-r--r--test/CMakeLists.txt6
-rwxr-xr-xtest/COFF/Inputs/cl-gl.objbin0 -> 3734 bytes
-rw-r--r--test/COFF/Inputs/far-arm-thumb-abs.s2
-rw-r--r--test/COFF/Inputs/lto-lazy-reference-dummy.ll6
-rw-r--r--test/COFF/Inputs/lto-lazy-reference-quadruple.ll16
-rw-r--r--test/COFF/arm-thumb-branch-error.s10
-rw-r--r--test/COFF/cl-gl.test4
-rw-r--r--test/COFF/common.test2
-rw-r--r--test/COFF/conflict-mangled.test37
-rw-r--r--test/COFF/conflict.test2
-rw-r--r--test/COFF/delayimports32.test6
-rw-r--r--test/COFF/dll.test1
-rw-r--r--test/COFF/dumppdb.test181
-rw-r--r--test/COFF/export-exe.test1
-rw-r--r--test/COFF/export.test1
-rw-r--r--test/COFF/export32.test1
-rw-r--r--test/COFF/icf-associative.test104
-rw-r--r--test/COFF/include2.test2
-rw-r--r--test/COFF/linkrepro.test38
-rw-r--r--test/COFF/lldmap.test6
-rw-r--r--test/COFF/lto-lazy-reference.ll21
-rw-r--r--test/COFF/noentry.test1
-rw-r--r--test/COFF/order.test2
-rw-r--r--test/COFF/pdb.test421
-rw-r--r--test/COFF/reloc-arm.test25
-rw-r--r--test/COFF/rsds.test113
-rw-r--r--test/COFF/symtab.test52
-rw-r--r--test/ELF/Inputs/arm-attributes1.s (renamed from test/ELF/arm-attributes-remove.s)24
-rw-r--r--test/ELF/Inputs/arm-exidx-cantunwind.s40
-rw-r--r--test/ELF/Inputs/arm-shared.s8
-rw-r--r--test/ELF/Inputs/arm-tls-get-addr.s13
-rw-r--r--test/ELF/Inputs/bad-archive.a2
-rw-r--r--test/ELF/Inputs/comment-gc.s1
-rw-r--r--test/ELF/Inputs/conflict-debug.s5
-rw-r--r--test/ELF/Inputs/gdb-index-a.elfbin0 -> 3040 bytes
-rw-r--r--test/ELF/Inputs/gdb-index-b.elfbin0 -> 3048 bytes
-rw-r--r--test/ELF/Inputs/i386-tls-got.s5
-rw-r--r--test/ELF/Inputs/icf-non-mergeable.s8
-rwxr-xr-xtest/ELF/Inputs/invalid-shstrndx.sobin13032 -> 0 bytes
-rw-r--r--test/ELF/Inputs/mips-concatenated-abiflags.obin0 -> 1084 bytes
-rw-r--r--test/ELF/Inputs/mips-fnpic.s6
-rw-r--r--test/ELF/Inputs/mips-fpic.s6
-rwxr-xr-xtest/ELF/Inputs/mips-gp0-non-zero.obin0 -> 848 bytes
-rw-r--r--test/ELF/Inputs/mips-n32-rels.obin0 -> 1092 bytes
-rw-r--r--test/ELF/Inputs/relocatable-tls.s1
-rw-r--r--test/ELF/Inputs/relocation-relative-absolute.s2
-rw-r--r--test/ELF/Inputs/shared2-x86-64.s9
-rw-r--r--test/ELF/Inputs/shf-info-link.test21
-rw-r--r--test/ELF/Inputs/startstop-shared2.s2
-rw-r--r--test/ELF/Inputs/symbol-override.s2
-rw-r--r--test/ELF/Inputs/uabs_label.s4
-rw-r--r--test/ELF/Inputs/undef-debug.s11
-rw-r--r--test/ELF/Inputs/use-bar.s2
-rw-r--r--test/ELF/Inputs/verdef-defaultver.s4
-rw-r--r--test/ELF/aarch64-abs16.s2
-rw-r--r--test/ELF/aarch64-abs32.s6
-rw-r--r--test/ELF/aarch64-condb-reloc.s98
-rw-r--r--test/ELF/aarch64-copy.s34
-rw-r--r--test/ELF/aarch64-copy2.s2
-rw-r--r--test/ELF/aarch64-data-relocs.s8
-rw-r--r--test/ELF/aarch64-fpic-abs16.s2
-rw-r--r--test/ELF/aarch64-fpic-add_abs_lo12_nc.s2
-rw-r--r--test/ELF/aarch64-fpic-adr_prel_lo21.s2
-rw-r--r--test/ELF/aarch64-fpic-adr_prel_pg_hi21.s2
-rw-r--r--test/ELF/aarch64-fpic-ldst32_abs_lo12_nc.s2
-rw-r--r--test/ELF/aarch64-fpic-ldst64_abs_lo12_nc.s2
-rw-r--r--test/ELF/aarch64-fpic-ldst8_abs_lo12_nc.s2
-rw-r--r--test/ELF/aarch64-fpic-prel16.s2
-rw-r--r--test/ELF/aarch64-fpic-prel32.s2
-rw-r--r--test/ELF/aarch64-fpic-prel64.s2
-rw-r--r--test/ELF/aarch64-gnu-ifunc-plt.s85
-rw-r--r--test/ELF/aarch64-gnu-ifunc.s55
-rw-r--r--test/ELF/aarch64-got-reloc.s30
-rw-r--r--test/ELF/aarch64-prel16.s2
-rw-r--r--test/ELF/aarch64-prel32.s2
-rw-r--r--test/ELF/aarch64-relocs.s68
-rw-r--r--test/ELF/aarch64-tls-gdie.s14
-rw-r--r--test/ELF/aarch64-tls-gdle.s8
-rw-r--r--test/ELF/aarch64-tls-ie.s24
-rw-r--r--test/ELF/aarch64-tls-iele.s8
-rw-r--r--test/ELF/aarch64-tls-le.s6
-rw-r--r--test/ELF/aarch64-tls-static.s14
-rw-r--r--test/ELF/aarch64-tlsdesc.s58
-rw-r--r--test/ELF/aarch64-tstbr14-reloc.s100
-rw-r--r--test/ELF/aarch64-undefined-weak.s45
-rw-r--r--test/ELF/abs-conflict.s16
-rw-r--r--test/ELF/abs-hidden.s2
-rw-r--r--test/ELF/allow-multiple-definition.s4
-rw-r--r--test/ELF/amdgpu-entry.s16
-rw-r--r--test/ELF/amdgpu-globals.s2
-rw-r--r--test/ELF/amdgpu-relocs.s96
-rw-r--r--test/ELF/arm-attributes.s183
-rw-r--r--test/ELF/arm-blx.s1
-rw-r--r--test/ELF/arm-branch.s1
-rw-r--r--test/ELF/arm-data-prel.s8
-rw-r--r--test/ELF/arm-eabi-version.s14
-rw-r--r--test/ELF/arm-exidx-canunwind.s99
-rw-r--r--test/ELF/arm-exidx-gc.s125
-rw-r--r--test/ELF/arm-exidx-link.s25
-rw-r--r--test/ELF/arm-exidx-order.s169
-rw-r--r--test/ELF/arm-exidx-output.s44
-rw-r--r--test/ELF/arm-exidx-relocatable.s132
-rw-r--r--test/ELF/arm-exidx-sentinel-norelocatable.s17
-rw-r--r--test/ELF/arm-exidx-sentinel-orphan.s23
-rw-r--r--test/ELF/arm-exidx-shared.s45
-rw-r--r--test/ELF/arm-gnu-ifunc-plt.s93
-rw-r--r--test/ELF/arm-gnu-ifunc.s156
-rw-r--r--test/ELF/arm-got-relative.s2
-rw-r--r--test/ELF/arm-pie-relative.s25
-rw-r--r--test/ELF/arm-static-defines.s44
-rw-r--r--test/ELF/arm-target1.s32
-rw-r--r--test/ELF/arm-target2.s60
-rw-r--r--test/ELF/arm-thumb-blx.s2
-rw-r--r--test/ELF/arm-thumb-branch.s1
-rw-r--r--test/ELF/arm-thumb-interwork-thunk-range.s15
-rw-r--r--test/ELF/arm-thumb-interwork-thunk.s4
-rw-r--r--test/ELF/arm-thumb-narrow-branch-check.s1
-rw-r--r--test/ELF/arm-thumb-no-undefined-thunk.s24
-rw-r--r--test/ELF/arm-thumb-undefined-weak.s38
-rw-r--r--test/ELF/arm-tls-gd32.s106
-rw-r--r--test/ELF/arm-tls-ie32.s96
-rw-r--r--test/ELF/arm-tls-ldm32.s73
-rw-r--r--test/ELF/arm-tls-le32.s77
-rw-r--r--test/ELF/arm-tls-norelax-gd-ie.s30
-rw-r--r--test/ELF/arm-tls-norelax-gd-le.s37
-rw-r--r--test/ELF/arm-tls-norelax-ie-le.s41
-rw-r--r--test/ELF/arm-tls-norelax-ld-le.s35
-rw-r--r--test/ELF/arm-undefined-weak.s39
-rw-r--r--test/ELF/arm-use-r-output.s13
-rw-r--r--test/ELF/auxiliary.s12
-rw-r--r--test/ELF/avoid-empty-program-headers.s16
-rw-r--r--test/ELF/bad-archive.s11
-rw-r--r--test/ELF/basic-aarch64.s52
-rw-r--r--test/ELF/basic-mips.s62
-rw-r--r--test/ELF/basic-ppc.s62
-rw-r--r--test/ELF/basic.s70
-rw-r--r--test/ELF/basic32.s38
-rw-r--r--test/ELF/basic64be.s41
-rw-r--r--test/ELF/bss.s14
-rw-r--r--test/ELF/bsymbolic-undef.s4
-rw-r--r--test/ELF/bsymbolic.s2
-rw-r--r--test/ELF/build-id.s34
-rw-r--r--test/ELF/color-diagnostics.test18
-rw-r--r--test/ELF/comdat.s9
-rw-r--r--test/ELF/comment-gc.s15
-rw-r--r--test/ELF/common.s10
-rw-r--r--test/ELF/compressed-debug-input.s105
-rw-r--r--test/ELF/conflict.s28
-rw-r--r--test/ELF/copy-errors.s4
-rw-r--r--test/ELF/copy-in-shared.s2
-rw-r--r--test/ELF/copy-rel-corrupted.s2
-rw-r--r--test/ELF/copy-rel-pie-error.s4
-rw-r--r--test/ELF/debug-gc.s30
-rw-r--r--test/ELF/defined-tls_get_addr.s10
-rw-r--r--test/ELF/discard-locals.s2
-rw-r--r--test/ELF/discard-none.s4
-rw-r--r--test/ELF/dont-export-hidden.s2
-rw-r--r--test/ELF/driver.test10
-rw-r--r--test/ELF/duplicate-internal.s11
-rw-r--r--test/ELF/dynamic-got-rela.s34
-rw-r--r--test/ELF/dynamic-got.s39
-rw-r--r--test/ELF/dynamic-list-extern.s15
-rw-r--r--test/ELF/dynamic-list.s26
-rw-r--r--test/ELF/dynamic-reloc-in-ro.s2
-rw-r--r--test/ELF/dynamic-reloc-index.s2
-rw-r--r--test/ELF/dynamic-reloc.s6
-rw-r--r--test/ELF/dynamic.s4
-rw-r--r--test/ELF/edata-etext.s115
-rw-r--r--test/ELF/eh-align-cie.s4
-rw-r--r--test/ELF/eh-frame-dyn-rel.s2
-rw-r--r--test/ELF/eh-frame-gc2.s15
-rw-r--r--test/ELF/eh-frame-hdr-abs-fde.s8
-rw-r--r--test/ELF/eh-frame-hdr-augmentation.s2
-rw-r--r--test/ELF/eh-frame-hdr-icf.s8
-rw-r--r--test/ELF/eh-frame-hdr-no-out.s6
-rw-r--r--test/ELF/eh-frame-hdr.s111
-rw-r--r--test/ELF/eh-frame-marker.s13
-rw-r--r--test/ELF/eh-frame-merge.s6
-rw-r--r--test/ELF/ehdr_start.s16
-rw-r--r--test/ELF/ehframe-relocation.s10
-rw-r--r--test/ELF/empty-pt-load.s11
-rw-r--r--test/ELF/empty-ver.s22
-rw-r--r--test/ELF/emulation.s73
-rw-r--r--test/ELF/end-update.s4
-rw-r--r--test/ELF/end.s52
-rw-r--r--test/ELF/entry.s46
-rw-r--r--test/ELF/error-limit.test26
-rw-r--r--test/ELF/exclude.s19
-rw-r--r--test/ELF/format-binary.test56
-rw-r--r--test/ELF/gc-debuginfo-tls.s23
-rw-r--r--test/ELF/gc-sections-alloc.s31
-rw-r--r--test/ELF/gc-sections-implicit-addend.s26
-rw-r--r--test/ELF/gc-sections-keep-shared-start.s30
-rw-r--r--test/ELF/gc-sections-lsda.s2
-rw-r--r--test/ELF/gc-sections-non-alloc-to-merge.s27
-rw-r--r--test/ELF/gc-sections-shared.s42
-rw-r--r--test/ELF/gc-sections-synthetic.s16
-rw-r--r--test/ELF/gc-sections.s18
-rw-r--r--test/ELF/gdb-index.s49
-rw-r--r--test/ELF/gnu-ifunc-gotpcrel.s2
-rw-r--r--test/ELF/gnu-ifunc-i386.s36
-rw-r--r--test/ELF/gnu-ifunc-plt-i386.s76
-rw-r--r--test/ELF/gnu-ifunc-plt.s74
-rw-r--r--test/ELF/gnu-ifunc-shared.s66
-rw-r--r--test/ELF/gnu-ifunc.s45
-rw-r--r--test/ELF/got-aarch64.s12
-rw-r--r--test/ELF/got.s18
-rw-r--r--test/ELF/gotpc-relax-nopic.s56
-rw-r--r--test/ELF/gotpc-relax.s14
-rw-r--r--test/ELF/i386-gotoff-shared.s23
-rw-r--r--test/ELF/i386-gotpc.s2
-rw-r--r--test/ELF/i386-pc16.test40
-rw-r--r--test/ELF/i386-tls-got.s7
-rw-r--r--test/ELF/i386-tls-ie-shared.s47
-rw-r--r--test/ELF/icf-non-mergeable.s28
-rw-r--r--test/ELF/icf4.s2
-rw-r--r--test/ELF/icf5.s2
-rw-r--r--test/ELF/icf7.s4
-rw-r--r--test/ELF/image-base.s4
-rw-r--r--test/ELF/incompatible-ar-first.s2
-rw-r--r--test/ELF/incompatible.s2
-rw-r--r--test/ELF/invalid-cie-length.s2
-rw-r--r--test/ELF/invalid-cie-length2.s2
-rw-r--r--test/ELF/invalid-cie-length3.s2
-rw-r--r--test/ELF/invalid-cie-length4.s2
-rw-r--r--test/ELF/invalid-dynamic-list.test12
-rw-r--r--test/ELF/invalid-elf.test35
-rw-r--r--test/ELF/invalid-linkerscript.test2
-rw-r--r--test/ELF/invalid/Inputs/binding.elf (renamed from test/ELF/Inputs/invalid-binding.elf)bin536 -> 536 bytes
-rw-r--r--test/ELF/invalid/Inputs/broken-relaxation-x64.elfbin0 -> 688 bytes
-rw-r--r--test/ELF/invalid/Inputs/cie-version2.elf (renamed from test/ELF/Inputs/invalid-cie-version2.elf)bin1128 -> 1128 bytes
-rw-r--r--test/ELF/invalid/Inputs/common-symbol-alignment.elfbin0 -> 456 bytes
-rw-r--r--test/ELF/invalid/Inputs/common-symbol-alignment2.elfbin0 -> 456 bytes
-rw-r--r--test/ELF/invalid/Inputs/data-encoding.a (renamed from test/ELF/Inputs/invalid-data-encoding.a)bin156 -> 156 bytes
-rw-r--r--test/ELF/invalid/Inputs/dynamic-section-sh_size.elfbin0 -> 482 bytes
-rw-r--r--test/ELF/invalid/Inputs/file-class.a (renamed from test/ELF/Inputs/invalid-file-class.a)bin156 -> 156 bytes
-rw-r--r--test/ELF/invalid/Inputs/invalid-e_shnum.elfbin0 -> 64 bytes
-rw-r--r--test/ELF/invalid/Inputs/invalid-relocation-x64.elfbin0 -> 559 bytes
-rw-r--r--test/ELF/invalid/Inputs/mips-invalid-options-descriptor.elfbin0 -> 480 bytes
-rw-r--r--test/ELF/invalid/Inputs/multiple-eh-relocs.elf (renamed from test/ELF/Inputs/invalid-multiple-eh-relocs.elf)bin784 -> 784 bytes
-rw-r--r--test/ELF/invalid/Inputs/section-alignment-notpow2.elfbin0 -> 960 bytes
-rw-r--r--test/ELF/invalid/Inputs/section-index.elf (renamed from test/ELF/Inputs/invalid-section-index.elf)bin544 -> 544 bytes
-rw-r--r--test/ELF/invalid/Inputs/section-index2.elfbin0 -> 474 bytes
-rw-r--r--test/ELF/invalid/Inputs/shentsize-zero.elf (renamed from test/ELF/Inputs/invalid-shentsize-zero.elf)bin512 -> 512 bytes
-rw-r--r--test/ELF/invalid/Inputs/sht-group.elfbin0 -> 480 bytes
-rw-r--r--test/ELF/invalid/Inputs/symbol-index.elfbin0 -> 480 bytes
-rw-r--r--test/ELF/invalid/Inputs/symbol-name-offset.elfbin0 -> 480 bytes
-rw-r--r--test/ELF/invalid/Inputs/symtab-sh_info.elf (renamed from test/ELF/Inputs/invalid-symtab-sh_info.elf)bin512 -> 512 bytes
-rw-r--r--test/ELF/invalid/Inputs/symtab-sh_info2.elfbin0 -> 470 bytes
-rw-r--r--test/ELF/invalid/Inputs/symtab-sh_info3.elfbin0 -> 470 bytes
-rw-r--r--test/ELF/invalid/Inputs/tls-symbol.elfbin0 -> 456 bytes
-rw-r--r--test/ELF/invalid/Inputs/too-short.elfbin0 -> 44 bytes
-rw-r--r--test/ELF/invalid/broken-relaxation-x64.s46
-rw-r--r--test/ELF/invalid/common-symbol-alignment.s12
-rw-r--r--test/ELF/invalid/dynamic-section-size.s4
-rw-r--r--test/ELF/invalid/eh-frame-hdr-no-out.s6
-rw-r--r--test/ELF/invalid/invalid-e_shnum.s3
-rw-r--r--test/ELF/invalid/invalid-elf.test31
-rw-r--r--test/ELF/invalid/invalid-relocation-x64.s30
-rw-r--r--test/ELF/invalid/merge-invalid-size.s (renamed from test/ELF/merge-invalid-size.s)7
-rw-r--r--test/ELF/invalid/mips-invalid-options-descriptor.s5
-rw-r--r--test/ELF/invalid/section-alignment.test19
-rw-r--r--test/ELF/invalid/section-alignment2.s5
-rw-r--r--test/ELF/invalid/sht-group.s3
-rw-r--r--test/ELF/invalid/symbol-index.s10
-rw-r--r--test/ELF/invalid/symbol-name.s7
-rw-r--r--test/ELF/invalid/symtab-sh-info.s9
-rw-r--r--test/ELF/invalid/symtab-symbols.test25
-rw-r--r--test/ELF/invalid/tls-symbol.s5
-rw-r--r--test/ELF/invalid/too-short.s5
-rw-r--r--test/ELF/invalid/verdef-no-symtab.test26
-rw-r--r--test/ELF/libsearch.s17
-rw-r--r--test/ELF/linkerscript-align.s41
-rw-r--r--test/ELF/linkerscript-diagnostic.s66
-rw-r--r--test/ELF/linkerscript-locationcounter.s340
-rw-r--r--test/ELF/linkerscript-sections-padding.s44
-rw-r--r--test/ELF/linkerscript.s120
-rw-r--r--test/ELF/linkerscript/Inputs/comdat-gc.s5
-rw-r--r--test/ELF/linkerscript/Inputs/exclude-multiple1.s8
-rw-r--r--test/ELF/linkerscript/Inputs/exclude-multiple2.s8
-rw-r--r--test/ELF/linkerscript/Inputs/filename-spec.s2
-rw-r--r--test/ELF/linkerscript/Inputs/implicit-program-header.script12
-rw-r--r--test/ELF/linkerscript/Inputs/include.s5
-rw-r--r--test/ELF/linkerscript/Inputs/keep.s2
-rw-r--r--test/ELF/linkerscript/Inputs/libsearch-dyn.s3
-rw-r--r--test/ELF/linkerscript/Inputs/libsearch-st.s3
-rw-r--r--test/ELF/linkerscript/Inputs/merge-sections-reloc.s3
-rw-r--r--test/ELF/linkerscript/Inputs/notinclude.s4
-rw-r--r--test/ELF/linkerscript/Inputs/segment-start.script7
-rw-r--r--test/ELF/linkerscript/Inputs/shared.s10
-rw-r--r--test/ELF/linkerscript/Inputs/sort-nested.s7
-rw-r--r--test/ELF/linkerscript/Inputs/sort.s19
-rw-r--r--test/ELF/linkerscript/absolute-expr.s82
-rw-r--r--test/ELF/linkerscript/absolute.s18
-rw-r--r--test/ELF/linkerscript/addr.s32
-rw-r--r--test/ELF/linkerscript/align-empty.s18
-rw-r--r--test/ELF/linkerscript/align.s83
-rw-r--r--test/ELF/linkerscript/alignof.s41
-rw-r--r--test/ELF/linkerscript/alternate-sections.s34
-rw-r--r--test/ELF/linkerscript/arm-exidx-phdrs.s16
-rw-r--r--test/ELF/linkerscript/assert.s43
-rw-r--r--test/ELF/linkerscript/at.s119
-rw-r--r--test/ELF/linkerscript/comdat-gc.s14
-rw-r--r--test/ELF/linkerscript/common.s49
-rw-r--r--test/ELF/linkerscript/data-commands.s81
-rw-r--r--test/ELF/linkerscript/data-segment-relro.s70
-rw-r--r--test/ELF/linkerscript/define.s25
-rw-r--r--test/ELF/linkerscript/diagnostic.s106
-rw-r--r--test/ELF/linkerscript/discard-interp.s12
-rw-r--r--test/ELF/linkerscript/discard-section.s14
-rw-r--r--test/ELF/linkerscript/dot-is-not-abs.s53
-rw-r--r--test/ELF/linkerscript/double-bss.s21
-rw-r--r--test/ELF/linkerscript/dynamic-sym.s17
-rw-r--r--test/ELF/linkerscript/dynamic.s28
-rw-r--r--test/ELF/linkerscript/edata-etext.s20
-rw-r--r--test/ELF/linkerscript/eh-frame-hdr.s20
-rw-r--r--test/ELF/linkerscript/ehdr_start.s11
-rw-r--r--test/ELF/linkerscript/empty-load.s22
-rw-r--r--test/ELF/linkerscript/empty-tls.s14
-rw-r--r--test/ELF/linkerscript/entry.s42
-rw-r--r--test/ELF/linkerscript/exclude-multiple.s37
-rw-r--r--test/ELF/linkerscript/excludefile.s46
-rw-r--r--test/ELF/linkerscript/extend-pt-load.s69
-rw-r--r--test/ELF/linkerscript/filename-spec.s59
-rw-r--r--test/ELF/linkerscript/fill.s30
-rw-r--r--test/ELF/linkerscript/group.s56
-rw-r--r--test/ELF/linkerscript/header-addr.s47
-rw-r--r--test/ELF/linkerscript/implicit-program-header.s13
-rw-r--r--test/ELF/linkerscript/input-order.s38
-rw-r--r--test/ELF/linkerscript/input-sec-dup.s18
-rw-r--r--test/ELF/linkerscript/linkerscript.s54
-rw-r--r--test/ELF/linkerscript/loadaddr.s42
-rw-r--r--test/ELF/linkerscript/locationcounter.s189
-rw-r--r--test/ELF/linkerscript/locationcountererr.s9
-rw-r--r--test/ELF/linkerscript/merge-sections-reloc.s16
-rw-r--r--test/ELF/linkerscript/merge-sections.s94
-rw-r--r--test/ELF/linkerscript/multi-sections-constraint.s34
-rw-r--r--test/ELF/linkerscript/multiple-tbss.s45
-rw-r--r--test/ELF/linkerscript/no-pt-load.s5
-rw-r--r--test/ELF/linkerscript/no-space.s26
-rw-r--r--test/ELF/linkerscript/non-alloc.s25
-rw-r--r--test/ELF/linkerscript/numbers.s72
-rw-r--r--test/ELF/linkerscript/openbsd-bootdata.s7
-rw-r--r--test/ELF/linkerscript/openbsd-randomize.s23
-rw-r--r--test/ELF/linkerscript/openbsd-wxneeded.s17
-rw-r--r--test/ELF/linkerscript/orphan-align.s28
-rw-r--r--test/ELF/linkerscript/orphan-first-cmd.s18
-rw-r--r--test/ELF/linkerscript/orphan.s36
-rw-r--r--test/ELF/linkerscript/orphans.s (renamed from test/ELF/linkerscript-orphans.s)4
-rw-r--r--test/ELF/linkerscript/ouputformat.s (renamed from test/ELF/linkerscript-ouputformat.s)1
-rw-r--r--test/ELF/linkerscript/outputarch.s (renamed from test/ELF/linkerscript-outputarch.s)0
-rw-r--r--test/ELF/linkerscript/outsections-addr.s122
-rw-r--r--test/ELF/linkerscript/page-size-align.s22
-rw-r--r--test/ELF/linkerscript/page-size.s68
-rw-r--r--test/ELF/linkerscript/phdr-check.s (renamed from test/ELF/linkerscript-phdr-check.s)0
-rw-r--r--test/ELF/linkerscript/phdrs-flags.s58
-rw-r--r--test/ELF/linkerscript/phdrs.s143
-rw-r--r--test/ELF/linkerscript/repsection-symbol.s28
-rw-r--r--test/ELF/linkerscript/repsection-va.s (renamed from test/ELF/linkerscript-repsection-va.s)8
-rw-r--r--test/ELF/linkerscript/rosegment.s24
-rw-r--r--test/ELF/linkerscript/searchdir.s (renamed from test/ELF/linkerscript2.s)7
-rw-r--r--test/ELF/linkerscript/sections-constraint.s46
-rw-r--r--test/ELF/linkerscript/sections-constraint2.s14
-rw-r--r--test/ELF/linkerscript/sections-constraint3.s11
-rw-r--r--test/ELF/linkerscript/sections-constraint4.s20
-rw-r--r--test/ELF/linkerscript/sections-constraint5.s32
-rw-r--r--test/ELF/linkerscript/sections-keep.s (renamed from test/ELF/linkerscript-sections-keep.s)65
-rw-r--r--test/ELF/linkerscript/sections-padding.s49
-rw-r--r--test/ELF/linkerscript/sections-sort.s30
-rw-r--r--test/ELF/linkerscript/sections.s (renamed from test/ELF/linkerscript-sections.s)33
-rw-r--r--test/ELF/linkerscript/segment-start.s27
-rw-r--r--test/ELF/linkerscript/sizeof.s53
-rw-r--r--test/ELF/linkerscript/sizeofheaders.s18
-rw-r--r--test/ELF/linkerscript/sort-constructors.s5
-rw-r--r--test/ELF/linkerscript/sort-init.s24
-rw-r--r--test/ELF/linkerscript/sort-nested.s50
-rw-r--r--test/ELF/linkerscript/sort-non-script.s16
-rw-r--r--test/ELF/linkerscript/sort.s120
-rw-r--r--test/ELF/linkerscript/sort2.s39
-rw-r--r--test/ELF/linkerscript/start-end.s16
-rw-r--r--test/ELF/linkerscript/subalign.s43
-rw-r--r--test/ELF/linkerscript/symbol-assignexpr.s48
-rw-r--r--test/ELF/linkerscript/symbol-conflict.s (renamed from test/ELF/linkerscript-symbols.s)4
-rw-r--r--test/ELF/linkerscript/symbol-only.s21
-rw-r--r--test/ELF/linkerscript/symbolreferenced.s22
-rw-r--r--test/ELF/linkerscript/symbols-synthetic.s98
-rw-r--r--test/ELF/linkerscript/symbols.s84
-rw-r--r--test/ELF/linkerscript/tbss.s42
-rw-r--r--test/ELF/linkerscript/undef.s (renamed from test/ELF/linkerscript-symbol-conflict.s)6
-rw-r--r--test/ELF/linkerscript/va.s (renamed from test/ELF/linkerscript-va.s)6
-rw-r--r--test/ELF/linkerscript/visibility.s22
-rw-r--r--test/ELF/linkerscript/wildcards.s83
-rw-r--r--test/ELF/linkerscript/wildcards2.s25
-rw-r--r--test/ELF/local-dynamic.s12
-rw-r--r--test/ELF/local-got-pie.s11
-rw-r--r--test/ELF/local-got-shared.s11
-rw-r--r--test/ELF/local-got.s18
-rw-r--r--test/ELF/local.s24
-rw-r--r--test/ELF/lto/Inputs/common3.ll3
-rw-r--r--test/ELF/lto/Inputs/thin1.ll12
-rw-r--r--test/ELF/lto/Inputs/thin2.ll11
-rw-r--r--test/ELF/lto/Inputs/thinlto.ll7
-rw-r--r--test/ELF/lto/Inputs/unnamed-addr-drop.ll4
-rw-r--r--test/ELF/lto/archive-3.ll4
-rw-r--r--test/ELF/lto/archive.ll1
-rw-r--r--test/ELF/lto/asmundef.ll2
-rw-r--r--test/ELF/lto/available-externally.ll2
-rw-r--r--test/ELF/lto/bitcode-nodatalayout.ll (renamed from test/ELF/lto/invalid-bitcode.ll)5
-rw-r--r--test/ELF/lto/combined-lto-object-name.ll2
-rw-r--r--test/ELF/lto/common2.ll15
-rw-r--r--test/ELF/lto/common3.ll14
-rw-r--r--test/ELF/lto/discard-value-names.ll2
-rw-r--r--test/ELF/lto/drop-debug-info.ll2
-rw-r--r--test/ELF/lto/drop-linkage.ll2
-rw-r--r--test/ELF/lto/duplicated.ll2
-rw-r--r--test/ELF/lto/dynsym.ll5
-rw-r--r--test/ELF/lto/internalize-basic.ll2
-rw-r--r--test/ELF/lto/internalize-exportdyn.ll2
-rw-r--r--test/ELF/lto/internalize-llvmused.ll2
-rw-r--r--test/ELF/lto/internalize-undef.ll2
-rw-r--r--test/ELF/lto/internalize-version-script.ll2
-rw-r--r--test/ELF/lto/irmover-error.ll2
-rw-r--r--test/ELF/lto/linkonce-odr.ll2
-rw-r--r--test/ELF/lto/linkonce.ll2
-rw-r--r--test/ELF/lto/ltopasses-basic.ll3
-rw-r--r--test/ELF/lto/ltopasses-custom.ll11
-rw-r--r--test/ELF/lto/metadata.ll4
-rw-r--r--test/ELF/lto/parallel-internalize.ll11
-rw-r--r--test/ELF/lto/parallel.ll2
-rw-r--r--test/ELF/lto/save-temps.ll4
-rw-r--r--test/ELF/lto/shlib-undefined.ll2
-rw-r--r--test/ELF/lto/thin-archivecollision.ll29
-rw-r--r--test/ELF/lto/thinlto.ll35
-rw-r--r--test/ELF/lto/timepasses.ll15
-rw-r--r--test/ELF/lto/type-merge.ll2
-rw-r--r--test/ELF/lto/type-merge2.ll2
-rw-r--r--test/ELF/lto/undefined-puts.ll2
-rw-r--r--test/ELF/lto/unnamed-addr-comdat.ll2
-rw-r--r--test/ELF/lto/unnamed-addr-drop.ll12
-rw-r--r--test/ELF/lto/unnamed-addr-lib.ll2
-rw-r--r--test/ELF/lto/unnamed-addr.ll2
-rw-r--r--test/ELF/lto/version-script.ll2
-rw-r--r--test/ELF/merge-reloc.s92
-rw-r--r--test/ELF/merge-string-empty.s12
-rw-r--r--test/ELF/merge-string-error.s2
-rw-r--r--test/ELF/merge-string.s2
-rw-r--r--test/ELF/merge.s48
-rw-r--r--test/ELF/mips-26-mask.s16
-rw-r--r--test/ELF/mips-32.s9
-rw-r--r--test/ELF/mips-64-disp.s2
-rw-r--r--test/ELF/mips-64-got.s19
-rw-r--r--test/ELF/mips-64-gprel-so.s2
-rw-r--r--test/ELF/mips-64-rels.s2
-rw-r--r--test/ELF/mips-align-err.s2
-rw-r--r--test/ELF/mips-call-hilo.s62
-rw-r--r--test/ELF/mips-elf-flags-err.s87
-rw-r--r--test/ELF/mips-elf-flags.s155
-rw-r--r--test/ELF/mips-got-and-copy.s2
-rw-r--r--test/ELF/mips-got-hilo.s64
-rw-r--r--test/ELF/mips-got-page.s40
-rw-r--r--test/ELF/mips-got-redundant.s6
-rw-r--r--test/ELF/mips-got-relocs.s4
-rw-r--r--test/ELF/mips-got16.s40
-rw-r--r--test/ELF/mips-gp-disp.s2
-rw-r--r--test/ELF/mips-gp-ext.s73
-rw-r--r--test/ELF/mips-gp-local.s2
-rw-r--r--test/ELF/mips-gp-lowest.s44
-rw-r--r--test/ELF/mips-gprel32-relocs-gp0.s48
-rw-r--r--test/ELF/mips-gprel32-relocs-gp0.test31
-rw-r--r--test/ELF/mips-gprel32-relocs.s2
-rw-r--r--test/ELF/mips-higher-highest.s21
-rw-r--r--test/ELF/mips-hilo-gp-disp.s4
-rw-r--r--test/ELF/mips-merge-abiflags.s63
-rw-r--r--test/ELF/mips-n32-emul.s14
-rw-r--r--test/ELF/mips-n32-rels.s71
-rw-r--r--test/ELF/mips-no-objects.s5
-rw-r--r--test/ELF/mips-npic-call-pic.s50
-rw-r--r--test/ELF/mips-options-r.test7
-rw-r--r--test/ELF/mips-options.s13
-rw-r--r--test/ELF/mips-plt-r6.s38
-rw-r--r--test/ELF/mips-relocatable.s21
-rw-r--r--test/ELF/mips-sto-pic-flag.s58
-rw-r--r--test/ELF/mips-tls-64.s116
-rw-r--r--test/ELF/mips-tls-static-64.s37
-rw-r--r--test/ELF/mips-tls-static.s42
-rw-r--r--test/ELF/mips-tls.s99
-rw-r--r--test/ELF/mips-xgot-order.s49
-rw-r--r--test/ELF/no-inhibit-exec.s2
-rw-r--r--test/ELF/no-merge.s25
-rw-r--r--test/ELF/no-obj.s2
-rw-r--r--test/ELF/non-abs-reloc.s11
-rw-r--r--test/ELF/noplt-pie.s4
-rw-r--r--test/ELF/oformat-binary-ttext.s18
-rw-r--r--test/ELF/oformat-binary.s32
-rw-r--r--test/ELF/openbsd-randomize.s20
-rw-r--r--test/ELF/openbsd-wxneeded.s17
-rw-r--r--test/ELF/phdr-align.s1
-rw-r--r--test/ELF/pie.s78
-rw-r--r--test/ELF/plt-aarch64.s158
-rw-r--r--test/ELF/plt.s46
-rw-r--r--test/ELF/ppc-relocs.s34
-rw-r--r--test/ELF/pre_init_fini_array.s30
-rw-r--r--test/ELF/pre_init_fini_array_missing.s24
-rw-r--r--test/ELF/program-header-layout.s17
-rw-r--r--test/ELF/relative-dynamic-reloc-ppc64.s2
-rw-r--r--test/ELF/relative-dynamic-reloc.s2
-rw-r--r--test/ELF/relocatable-comment.s27
-rw-r--r--test/ELF/relocatable-local-sym.s16
-rw-r--r--test/ELF/relocatable-sections.s30
-rw-r--r--test/ELF/relocatable-symbols.s42
-rw-r--r--test/ELF/relocatable-tls.s15
-rw-r--r--test/ELF/relocatable-visibility.s19
-rw-r--r--test/ELF/relocatable.s10
-rw-r--r--test/ELF/relocation-common.s4
-rw-r--r--test/ELF/relocation-copy-flags.s10
-rw-r--r--test/ELF/relocation-copy.s20
-rw-r--r--test/ELF/relocation-dtrace.test24
-rw-r--r--test/ELF/relocation-i686.s6
-rw-r--r--test/ELF/relocation-local.s6
-rw-r--r--test/ELF/relocation-past-merge-end.s9
-rw-r--r--test/ELF/relocation-relative-absolute.s12
-rw-r--r--test/ELF/relocation-size-shared.s72
-rw-r--r--test/ELF/relocation-size.s60
-rw-r--r--test/ELF/relocation-undefined-weak.s6
-rw-r--r--test/ELF/relocation.s40
-rw-r--r--test/ELF/relro-omagic.s34
-rw-r--r--test/ELF/relro.s239
-rw-r--r--test/ELF/reproduce-backslash.s9
-rw-r--r--test/ELF/reproduce-windows.s6
-rw-r--r--test/ELF/reproduce-windows2.s11
-rw-r--r--test/ELF/reproduce.s8
-rw-r--r--test/ELF/resolution.s18
-rw-r--r--test/ELF/retain-symbols-file.s44
-rw-r--r--test/ELF/section-name.s50
-rw-r--r--test/ELF/section-symbols.test35
-rw-r--r--test/ELF/sectionstart.s67
-rw-r--r--test/ELF/segments.s108
-rw-r--r--test/ELF/shared-be.s4
-rw-r--r--test/ELF/shared.s8
-rw-r--r--test/ELF/shf-info-link.test32
-rw-r--r--test/ELF/sort-norosegment.s15
-rw-r--r--test/ELF/startstop-gccollect.s6
-rw-r--r--test/ELF/startstop-shared.s16
-rw-r--r--test/ELF/startstop-shared2.s14
-rw-r--r--test/ELF/startstop.s24
-rw-r--r--test/ELF/string-gc.s6
-rw-r--r--test/ELF/string-table.s7
-rw-r--r--test/ELF/symbol-ordering-file.s44
-rw-r--r--test/ELF/symbol-override.s4
-rw-r--r--test/ELF/symbols.s24
-rw-r--r--test/ELF/synthetic-got.s32
-rw-r--r--test/ELF/sysroot.s3
-rw-r--r--test/ELF/tls-got.s22
-rw-r--r--test/ELF/tls-mismatch.s2
-rw-r--r--test/ELF/tls-offset.s6
-rw-r--r--test/ELF/tls-opt-gdie.s18
-rw-r--r--test/ELF/tls-opt-local.s24
-rw-r--r--test/ELF/tls-opt.s46
-rw-r--r--test/ELF/tls-relocatable.s21
-rw-r--r--test/ELF/tls-static.s2
-rw-r--r--test/ELF/tls-weak-undef.s16
-rw-r--r--test/ELF/tls.s12
-rw-r--r--test/ELF/ttext-tdata-tbss.s63
-rw-r--r--test/ELF/undef-shared.s11
-rw-r--r--test/ELF/undef-start.s3
-rw-r--r--test/ELF/undef-version-script.s6
-rw-r--r--test/ELF/undef-with-plt-addr.s8
-rw-r--r--test/ELF/undef.s22
-rw-r--r--test/ELF/undefined-opt.s2
-rw-r--r--test/ELF/unresolved-symbols.s4
-rw-r--r--test/ELF/verdef-defaultver.s60
-rw-r--r--test/ELF/verdef-dependency.s12
-rw-r--r--test/ELF/verdef.s22
-rw-r--r--test/ELF/verneed-local.s2
-rw-r--r--test/ELF/verneed.s34
-rw-r--r--test/ELF/version-script-complex-wildcards.s62
-rw-r--r--test/ELF/version-script-err.s3
-rw-r--r--test/ELF/version-script-extern-exact.s22
-rw-r--r--test/ELF/version-script-extern-wildcards-anon.s62
-rw-r--r--test/ELF/version-script-extern-wildcards.s29
-rw-r--r--test/ELF/version-script-extern.s61
-rw-r--r--test/ELF/version-script-glob.s45
-rw-r--r--test/ELF/version-script-locals-extern.s45
-rw-r--r--test/ELF/version-script-locals.s45
-rw-r--r--test/ELF/version-script-missing.s7
-rw-r--r--test/ELF/version-script-no-warn.s12
-rw-r--r--test/ELF/version-script-noundef.s17
-rw-r--r--test/ELF/version-script.s89
-rw-r--r--test/ELF/version-symbol-error.s12
-rw-r--r--test/ELF/version-wildcard.test12
-rw-r--r--test/ELF/visibility.s2
-rw-r--r--test/ELF/weak-undef-shared.s2
-rw-r--r--test/ELF/weak-undef.s2
-rw-r--r--test/ELF/wildcards.s80
-rw-r--r--test/ELF/writable-merge.s1
-rw-r--r--test/ELF/x86-64-dyn-rel-error.s2
-rw-r--r--test/ELF/x86-64-dyn-rel-error2.s2
-rw-r--r--test/ELF/x86-64-relax-got-abs.s16
-rw-r--r--test/ELF/x86-64-reloc-32-fpic.s2
-rw-r--r--test/ELF/x86-64-reloc-error.s4
-rw-r--r--test/ELF/x86-64-reloc-pc32-fpic.s2
-rw-r--r--test/ELF/x86-64-reloc-range.s2
-rw-r--r--test/ELF/zdefs.s2
-rw-r--r--test/ELF/zstack-size.s20
-rw-r--r--test/lit.cfg17
-rw-r--r--test/mach-o/Inputs/arm64/libSystem.yaml13
-rw-r--r--test/mach-o/Inputs/armv7/libSystem.yaml13
-rw-r--r--test/mach-o/Inputs/libSystem.yaml13
-rw-r--r--test/mach-o/Inputs/x86/libSystem.yaml13
-rw-r--r--test/mach-o/Inputs/x86_64/libSystem.yaml13
-rw-r--r--test/mach-o/arm-interworking-movw.yaml2
-rw-r--r--test/mach-o/arm-interworking.yaml2
-rw-r--r--test/mach-o/arm-shims.yaml2
-rw-r--r--test/mach-o/arm64-reloc-negDelta32-fixup.yaml38
-rw-r--r--test/mach-o/arm64-relocs-errors-delta64-offset.yaml12
-rw-r--r--test/mach-o/bind-opcodes.yaml143
-rw-r--r--test/mach-o/data-in-code-load-command.yaml16
-rw-r--r--test/mach-o/data-only-dylib.yaml2
-rw-r--r--test/mach-o/dead-strip-globals.yaml10
-rw-r--r--test/mach-o/debug-syms.yaml249
-rw-r--r--test/mach-o/demangle.yaml4
-rw-r--r--test/mach-o/dso_handle.yaml8
-rw-r--r--test/mach-o/dylib-install-names.yaml12
-rw-r--r--test/mach-o/eh-frame-relocs-arm64.yaml92
-rw-r--r--test/mach-o/exe-offsets.yaml2
-rw-r--r--test/mach-o/exe-segment-overlap.yaml2
-rw-r--r--test/mach-o/executable-exports.yaml2
-rw-r--r--test/mach-o/export-trie-order.yaml62
-rw-r--r--test/mach-o/exported_symbols_list-dylib.yaml8
-rw-r--r--test/mach-o/exported_symbols_list-undef.yaml2
-rw-r--r--test/mach-o/fat-archive.yaml2
-rw-r--r--test/mach-o/flat_namespace_undef_error.yaml2
-rw-r--r--test/mach-o/flat_namespace_undef_suppress.yaml2
-rw-r--r--test/mach-o/force_load-dylib.yaml4
-rw-r--r--test/mach-o/force_load-x86_64.yaml4
-rw-r--r--test/mach-o/function-starts-load-command.yaml10
-rw-r--r--test/mach-o/gcc_except_tab-got-arm64.yaml30
-rw-r--r--test/mach-o/got-order.yaml2
-rw-r--r--test/mach-o/hello-world-arm64.yaml8
-rw-r--r--test/mach-o/image-base.yaml2
-rw-r--r--test/mach-o/interposing-section.yaml2
-rw-r--r--test/mach-o/lazy-bind-x86_64.yaml2
-rw-r--r--test/mach-o/lc_segment_filesize.yaml31
-rw-r--r--test/mach-o/library-order.yaml2
-rw-r--r--test/mach-o/library-rescan.yaml2
-rw-r--r--test/mach-o/linker-as-ld.yaml4
-rw-r--r--test/mach-o/lit.local.cfg2
-rw-r--r--test/mach-o/mh_bundle_header.yaml4
-rw-r--r--test/mach-o/mh_dylib_header.yaml2
-rw-r--r--test/mach-o/objc-category-list-atom.yaml14
-rw-r--r--test/mach-o/objc_export_list.yaml2
-rw-r--r--test/mach-o/order_file-basic.yaml2
-rw-r--r--test/mach-o/parse-data-in-code-armv7.yaml2
-rw-r--r--test/mach-o/parse-data-relocs-x86_64.yaml6
-rw-r--r--test/mach-o/rpath.yaml2
-rw-r--r--test/mach-o/run-tlv-pass-x86-64.yaml18
-rw-r--r--test/mach-o/sectalign.yaml2
-rw-r--r--test/mach-o/sectattrs.yaml2
-rw-r--r--test/mach-o/source-version.yaml2
-rw-r--r--test/mach-o/stack-size.yaml4
-rw-r--r--test/mach-o/string-table.yaml66
-rw-r--r--test/mach-o/twolevel_namespace_undef_dynamic_lookup.yaml2
-rw-r--r--test/mach-o/twolevel_namespace_undef_warning_suppress.yaml4
-rw-r--r--test/mach-o/unwind-info-simple-arm64.yaml2
-rw-r--r--test/mach-o/unwind-info-simple-x86_64.yaml2
-rw-r--r--test/mach-o/upward-dylib-load-command.yaml4
-rw-r--r--test/mach-o/use-dylib.yaml39
-rw-r--r--test/mach-o/version-min-load-command.yaml14
-rw-r--r--tools/lld/CMakeLists.txt7
-rw-r--r--tools/lld/lld.cpp6
-rw-r--r--unittests/CMakeLists.txt2
-rw-r--r--unittests/MachOTests/MachONormalizedFileBinaryWriterTests.cpp4
806 files changed, 27950 insertions, 11087 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 46ca748f8fac..23cef2e9fc67 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,3 +1,54 @@
+# Check if lld is built as a standalone project.
+if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
+ project(lld)
+ cmake_minimum_required(VERSION 3.4.3)
+
+ set(CMAKE_INCLUDE_CURRENT_DIR ON)
+ set(LLD_BUILT_STANDALONE TRUE)
+
+ find_program(LLVM_CONFIG_PATH "llvm-config" DOC "Path to llvm-config binary")
+ if(NOT LLVM_CONFIG_PATH)
+ message(FATAL_ERROR "llvm-config not found: specify LLVM_CONFIG_PATH")
+ endif()
+
+ execute_process(COMMAND "${LLVM_CONFIG_PATH}" "--obj-root" "--includedir"
+ RESULT_VARIABLE HAD_ERROR
+ OUTPUT_VARIABLE LLVM_CONFIG_OUTPUT
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ if(HAD_ERROR)
+ message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}")
+ endif()
+
+ string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" LLVM_CONFIG_OUTPUT "${LLVM_CONFIG_OUTPUT}")
+
+ list(GET LLVM_CONFIG_OUTPUT 0 OBJ_ROOT)
+ list(GET LLVM_CONFIG_OUTPUT 1 MAIN_INCLUDE_DIR)
+
+ set(LLVM_OBJ_ROOT ${OBJ_ROOT} CACHE PATH "path to LLVM build tree")
+ set(LLVM_MAIN_INCLUDE_DIR ${MAIN_INCLUDE_DIR} CACHE PATH "path to llvm/include")
+
+ file(TO_CMAKE_PATH ${LLVM_OBJ_ROOT} LLVM_BINARY_DIR)
+ set(LLVM_CMAKE_PATH "${LLVM_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm")
+
+ if(NOT EXISTS "${LLVM_CMAKE_PATH}/LLVMConfig.cmake")
+ message(FATAL_ERROR "LLVMConfig.cmake not found")
+ endif()
+ include("${LLVM_CMAKE_PATH}/LLVMConfig.cmake")
+
+ list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}")
+
+ set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}")
+ include_directories("${LLVM_BINARY_DIR}/include" ${LLVM_INCLUDE_DIRS})
+ link_directories(${LLVM_LIBRARY_DIRS})
+
+ set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin)
+ find_program(LLVM_TABLEGEN_EXE "llvm-tblgen" ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH)
+
+ include(AddLLVM)
+ include(TableGen)
+ include(HandleLLVMOptions)
+endif()
+
set(LLD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(LLD_INCLUDE_DIR ${LLD_SOURCE_DIR}/include )
set(LLD_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
@@ -55,6 +106,8 @@ endif()
list (APPEND CMAKE_MODULE_PATH "${LLD_SOURCE_DIR}/cmake/modules")
+include(AddLLD)
+
option(LLD_USE_VTUNE
"Enable VTune user task tracking."
OFF)
@@ -67,6 +120,8 @@ if (LLD_USE_VTUNE)
endif()
endif()
+option(LLD_BUILD_TOOLS
+ "Build the lld tools. If OFF, just generate build targets." ON)
if (MSVC)
add_definitions(-wd4530) # Suppress 'warning C4530: C++ exception handler used, but unwind semantics are not enabled.'
@@ -87,12 +142,6 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
)
endif()
-macro(add_lld_library name)
- add_llvm_library(${name} ${ARGN})
- set_target_properties(${name} PROPERTIES FOLDER "lld libraries")
-endmacro(add_lld_library)
-
-
add_subdirectory(lib)
add_subdirectory(tools/lld)
diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt
index ad5b6fda1693..70a33b9fdd81 100644
--- a/COFF/CMakeLists.txt
+++ b/COFF/CMakeLists.txt
@@ -2,6 +2,10 @@ set(LLVM_TARGET_DEFINITIONS Options.td)
tablegen(LLVM Options.inc -gen-opt-parser-defs)
add_public_tablegen_target(COFFOptionsTableGen)
+if(NOT LLD_BUILT_STANDALONE)
+ set(tablegen_deps intrinsics_gen)
+endif()
+
add_lld_library(lldCOFF
Chunks.cpp
DLL.cpp
@@ -14,6 +18,7 @@ add_lld_library(lldCOFF
MarkLive.cpp
ModuleDef.cpp
PDB.cpp
+ Strings.cpp
SymbolTable.cpp
Symbols.cpp
Writer.cpp
@@ -21,6 +26,9 @@ add_lld_library(lldCOFF
LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
Core
+ DebugInfoCodeView
+ DebugInfoMSF
+ DebugInfoPDB
LTO
LibDriver
Object
@@ -30,7 +38,11 @@ add_lld_library(lldCOFF
Option
Support
- LINK_LIBS ${PTHREAD_LIB}
- )
+ LINK_LIBS
+ lldCore
+ ${PTHREAD_LIB}
-add_dependencies(lldCOFF COFFOptionsTableGen intrinsics_gen)
+ DEPENDS
+ COFFOptionsTableGen
+ ${tablegen_deps}
+ )
diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp
index 1c1b18176aa2..7f0dfa92ec10 100644
--- a/COFF/Chunks.cpp
+++ b/COFF/Chunks.cpp
@@ -28,7 +28,7 @@ namespace lld {
namespace coff {
SectionChunk::SectionChunk(ObjectFile *F, const coff_section *H)
- : Chunk(SectionKind), Repl(this), File(F), Header(H),
+ : Chunk(SectionKind), Repl(this), Header(H), File(F),
Relocs(File->getCOFFObj()->getRelocations(Header)),
NumRelocs(std::distance(Relocs.begin(), Relocs.end())) {
// Initialize SectionName.
@@ -81,11 +81,23 @@ void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, Defined *Sym,
}
static void applyMOV(uint8_t *Off, uint16_t V) {
- or16(Off, ((V & 0x800) >> 1) | ((V >> 12) & 0xf));
- or16(Off + 2, ((V & 0x700) << 4) | (V & 0xff));
+ write16le(Off, (read16le(Off) & 0xfbf0) | ((V & 0x800) >> 1) | ((V >> 12) & 0xf));
+ write16le(Off + 2, (read16le(Off + 2) & 0x8f00) | ((V & 0x700) << 4) | (V & 0xff));
+}
+
+static uint16_t readMOV(uint8_t *Off) {
+ uint16_t Opcode1 = read16le(Off);
+ uint16_t Opcode2 = read16le(Off + 2);
+ uint16_t Imm = (Opcode2 & 0x00ff) | ((Opcode2 >> 4) & 0x0700);
+ Imm |= ((Opcode1 << 1) & 0x0800) | ((Opcode1 & 0x000f) << 12);
+ return Imm;
}
static void applyMOV32T(uint8_t *Off, uint32_t V) {
+ uint16_t ImmW = readMOV(Off); // read MOVW operand
+ uint16_t ImmT = readMOV(Off + 4); // read MOVT operand
+ uint32_t Imm = ImmW | (ImmT << 16);
+ V += Imm; // add the immediate offset
applyMOV(Off, V); // set MOVW operand
applyMOV(Off + 4, V >> 16); // set MOVT operand
}
@@ -99,11 +111,14 @@ static void applyBranch20T(uint8_t *Off, int32_t V) {
}
static void applyBranch24T(uint8_t *Off, int32_t V) {
+ if (!isInt<25>(V))
+ fatal("relocation out of range");
uint32_t S = V < 0 ? 1 : 0;
uint32_t J1 = ((~V >> 23) & 1) ^ S;
uint32_t J2 = ((~V >> 22) & 1) ^ S;
or16(Off, (S << 10) | ((V >> 12) & 0x3ff));
- or16(Off + 2, (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff));
+ // Clear out the J1 and J2 bits which may be set.
+ write16le(Off + 2, (read16le(Off + 2) & 0xd000) | (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff));
}
void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym,
@@ -119,6 +134,7 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym,
case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(Off, S - P - 4); break;
case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(Off, S - P - 4); break;
case IMAGE_REL_ARM_BLX23T: applyBranch24T(Off, S - P - 4); break;
+ case IMAGE_REL_ARM_SECREL: add32(Off, Sym->getSecrel()); break;
default:
fatal("unsupported relocation type");
}
@@ -134,7 +150,7 @@ void SectionChunk::writeTo(uint8_t *Buf) const {
// Apply relocations.
for (const coff_relocation &Rel : Relocs) {
uint8_t *Off = Buf + OutputSectionOff + Rel.VirtualAddress;
- SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex)->repl();
+ SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex);
Defined *Sym = cast<Defined>(Body);
uint64_t P = RVA + Rel.VirtualAddress;
switch (Config->Machine) {
@@ -187,7 +203,7 @@ void SectionChunk::getBaserels(std::vector<Baserel> *Res) {
uint8_t Ty = getBaserelType(Rel);
if (Ty == IMAGE_REL_BASED_ABSOLUTE)
continue;
- SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex)->repl();
+ SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex);
if (isa<DefinedAbsolute>(Body))
continue;
Res->emplace_back(RVA + Rel.VirtualAddress, Ty);
@@ -210,7 +226,7 @@ void SectionChunk::printDiscardedMessage() const {
// Removed by dead-stripping. If it's removed by ICF, ICF already
// printed out the name, so don't repeat that here.
if (Sym && this == Repl)
- llvm::outs() << "Discarded " << Sym->getName() << "\n";
+ outs() << "Discarded " << Sym->getName() << "\n";
}
StringRef SectionChunk::getDebugName() {
@@ -233,7 +249,7 @@ void SectionChunk::replace(SectionChunk *Other) {
CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) {
// Common symbols are aligned on natural boundaries up to 32 bytes.
// This is what MSVC link.exe does.
- Align = std::min(uint64_t(32), NextPowerOf2(Sym.getValue()));
+ Align = std::min(uint64_t(32), PowerOf2Ceil(Sym.getValue()));
}
uint32_t CommonChunk::getPermissions() const {
diff --git a/COFF/Chunks.h b/COFF/Chunks.h
index cd0e2e69ef5d..59e36b84c9b0 100644
--- a/COFF/Chunks.h
+++ b/COFF/Chunks.h
@@ -17,7 +17,6 @@
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Object/COFF.h"
-#include <atomic>
#include <utility>
#include <vector>
@@ -29,7 +28,6 @@ using llvm::object::COFFSymbolRef;
using llvm::object::SectionRef;
using llvm::object::coff_relocation;
using llvm::object::coff_section;
-using llvm::sys::fs::file_magic;
class Baserel;
class Defined;
@@ -187,11 +185,12 @@ public:
// Auxiliary Format 5: Section Definitions. Used for ICF.
uint32_t Checksum = 0;
+ const coff_section *Header;
+
private:
// A file this chunk was created from.
ObjectFile *File;
- const coff_section *Header;
StringRef SectionName;
std::vector<SectionChunk *> AssocChildren;
llvm::iterator_range<const coff_relocation *> Relocs;
@@ -202,7 +201,7 @@ private:
// Used for ICF (Identical COMDAT Folding)
void replace(SectionChunk *Other);
- std::atomic<uint64_t> GroupID = { 0 };
+ uint32_t Color[2] = {0, 0};
// Sym points to a section symbol if this is a COMDAT chunk.
DefinedRegular *Sym = nullptr;
diff --git a/COFF/Config.h b/COFF/Config.h
index a5472e937fa1..0fa3338aa28c 100644
--- a/COFF/Config.h
+++ b/COFF/Config.h
@@ -26,7 +26,8 @@ using llvm::StringRef;
class DefinedAbsolute;
class DefinedRelative;
class StringChunk;
-class Undefined;
+struct Symbol;
+class SymbolBody;
// Short aliases.
static const auto AMD64 = llvm::COFF::IMAGE_FILE_MACHINE_AMD64;
@@ -37,7 +38,7 @@ static const auto I386 = llvm::COFF::IMAGE_FILE_MACHINE_I386;
struct Export {
StringRef Name; // N in /export:N or /export:E=N
StringRef ExtName; // E in /export:E=N
- Undefined *Sym = nullptr;
+ SymbolBody *Sym = nullptr;
uint16_t Ordinal = 0;
bool Noname = false;
bool Data = false;
@@ -61,6 +62,13 @@ struct Export {
}
};
+enum class DebugType {
+ None = 0x0,
+ CV = 0x1, /// CodeView
+ PData = 0x2, /// Procedure Data
+ Fixup = 0x4, /// Relocation Table
+};
+
// Global configuration.
struct Configuration {
enum ManifestKind { SideBySide, Embed, No };
@@ -69,7 +77,7 @@ struct Configuration {
llvm::COFF::MachineTypes Machine = IMAGE_FILE_MACHINE_UNKNOWN;
bool Verbose = false;
WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
- Undefined *Entry = nullptr;
+ SymbolBody *Entry = nullptr;
bool NoEntry = false;
std::string OutputFile;
bool DoGC = true;
@@ -78,9 +86,11 @@ struct Configuration {
bool Force = false;
bool Debug = false;
bool WriteSymtab = true;
+ unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
+ StringRef PDBPath;
// Symbols in this set are considered as live by the garbage collector.
- std::set<Undefined *> GCRoot;
+ std::set<SymbolBody *> GCRoot;
std::set<StringRef> NoDefaultLibs;
bool NoDefaultLibAll = false;
@@ -91,11 +101,11 @@ struct Configuration {
std::vector<Export> Exports;
std::set<std::string> DelayLoads;
std::map<std::string, int> DLLOrder;
- Undefined *DelayLoadHelper = nullptr;
+ SymbolBody *DelayLoadHelper = nullptr;
// Used for SafeSEH.
- DefinedRelative *SEHTable = nullptr;
- DefinedAbsolute *SEHCount = nullptr;
+ Symbol *SEHTable = nullptr;
+ Symbol *SEHCount = nullptr;
// Used for /opt:lldlto=N
unsigned LTOOptLevel = 2;
@@ -141,6 +151,10 @@ struct Configuration {
bool TerminalServerAware = true;
bool LargeAddressAware = false;
bool HighEntropyVA = false;
+
+ // This is for debugging.
+ bool DebugPdb = false;
+ bool DumpPdb = false;
};
extern Configuration *Config;
diff --git a/COFF/DLL.cpp b/COFF/DLL.cpp
index 9ac370c11d59..f93dc5cde44c 100644
--- a/COFF/DLL.cpp
+++ b/COFF/DLL.cpp
@@ -324,7 +324,7 @@ public:
if (E.ForwardChunk) {
write32le(P, E.ForwardChunk->getRVA());
} else {
- write32le(P, cast<Defined>(E.Sym->repl())->getRVA());
+ write32le(P, cast<Defined>(E.Sym)->getRVA());
}
}
}
diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp
index bb6a60e4fc4c..dc3a00ba55ed 100644
--- a/COFF/Driver.cpp
+++ b/COFF/Driver.cpp
@@ -7,15 +7,17 @@
//
//===----------------------------------------------------------------------===//
-#include "Config.h"
#include "Driver.h"
+#include "Config.h"
#include "Error.h"
#include "InputFiles.h"
+#include "Memory.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Writer.h"
#include "lld/Driver/Driver.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringSwitch.h"
#include "llvm/LibDriver/LibDriver.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
@@ -28,6 +30,13 @@
#include <algorithm>
#include <memory>
+#ifdef _MSC_VER
+// <future> depends on <eh.h> for __uncaught_exception.
+#include <eh.h>
+#endif
+
+#include <future>
+
using namespace llvm;
using namespace llvm::COFF;
using llvm::sys::Process;
@@ -41,11 +50,13 @@ namespace coff {
Configuration *Config;
LinkerDriver *Driver;
-bool link(llvm::ArrayRef<const char *> Args) {
- Configuration C;
- LinkerDriver D;
- Config = &C;
- Driver = &D;
+BumpPtrAllocator BAlloc;
+StringSaver Saver{BAlloc};
+std::vector<SpecificAllocBase *> SpecificAllocBase::Instances;
+
+bool link(ArrayRef<const char *> Args) {
+ Config = make<Configuration>();
+ Driver = make<LinkerDriver>();
Driver->link(Args);
return true;
}
@@ -58,26 +69,123 @@ static std::string getOutputPath(StringRef Path) {
return (S.substr(0, S.rfind('.')) + E).str();
}
-// Opens a file. Path has to be resolved already.
-// Newly created memory buffers are owned by this driver.
-MemoryBufferRef LinkerDriver::openFile(StringRef Path) {
- std::unique_ptr<MemoryBuffer> MB =
- check(MemoryBuffer::getFile(Path), "could not open " + Path);
- MemoryBufferRef MBRef = MB->getMemBufferRef();
- OwningMBs.push_back(std::move(MB)); // take ownership
+// ErrorOr is not default constructible, so it cannot be used as the type
+// parameter of a future.
+// FIXME: We could open the file in createFutureForFile and avoid needing to
+// return an error here, but for the moment that would cost us a file descriptor
+// (a limited resource on Windows) for the duration that the future is pending.
+typedef std::pair<std::unique_ptr<MemoryBuffer>, std::error_code> MBErrPair;
+
+// Create a std::future that opens and maps a file using the best strategy for
+// the host platform.
+static std::future<MBErrPair> createFutureForFile(std::string Path) {
+#if LLVM_ON_WIN32
+ // On Windows, file I/O is relatively slow so it is best to do this
+ // asynchronously.
+ auto Strategy = std::launch::async;
+#else
+ auto Strategy = std::launch::deferred;
+#endif
+ return std::async(Strategy, [=]() {
+ auto MBOrErr = MemoryBuffer::getFile(Path);
+ if (!MBOrErr)
+ return MBErrPair{nullptr, MBOrErr.getError()};
+ return MBErrPair{std::move(*MBOrErr), std::error_code()};
+ });
+}
+
+MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> MB) {
+ MemoryBufferRef MBRef = *MB;
+ OwningMBs.push_back(std::move(MB));
+
+ if (Driver->Cpio)
+ Driver->Cpio->append(relativeToRoot(MBRef.getBufferIdentifier()),
+ MBRef.getBuffer());
+
return MBRef;
}
-static std::unique_ptr<InputFile> createFile(MemoryBufferRef MB) {
+void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> MB) {
+ MemoryBufferRef MBRef = takeBuffer(std::move(MB));
+
// File type is detected by contents, not by file extension.
- file_magic Magic = identify_magic(MB.getBuffer());
+ file_magic Magic = identify_magic(MBRef.getBuffer());
+ if (Magic == file_magic::windows_resource) {
+ Resources.push_back(MBRef);
+ return;
+ }
+
+ FilePaths.push_back(MBRef.getBufferIdentifier());
if (Magic == file_magic::archive)
- return std::unique_ptr<InputFile>(new ArchiveFile(MB));
+ return Symtab.addFile(make<ArchiveFile>(MBRef));
if (Magic == file_magic::bitcode)
- return std::unique_ptr<InputFile>(new BitcodeFile(MB));
+ return Symtab.addFile(make<BitcodeFile>(MBRef));
+ if (Magic == file_magic::coff_cl_gl_object)
+ fatal(MBRef.getBufferIdentifier() + ": is not a native COFF file. "
+ "Recompile without /GL");
+ Symtab.addFile(make<ObjectFile>(MBRef));
+}
+
+void LinkerDriver::enqueuePath(StringRef Path) {
+ auto Future =
+ std::make_shared<std::future<MBErrPair>>(createFutureForFile(Path));
+ std::string PathStr = Path;
+ enqueueTask([=]() {
+ auto MBOrErr = Future->get();
+ if (MBOrErr.second)
+ fatal(MBOrErr.second, "could not open " + PathStr);
+ Driver->addBuffer(std::move(MBOrErr.first));
+ });
+
if (Config->OutputFile == "")
- Config->OutputFile = getOutputPath(MB.getBufferIdentifier());
- return std::unique_ptr<InputFile>(new ObjectFile(MB));
+ Config->OutputFile = getOutputPath(Path);
+}
+
+void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName,
+ StringRef ParentName) {
+ file_magic Magic = identify_magic(MB.getBuffer());
+ if (Magic == file_magic::coff_import_library) {
+ Symtab.addFile(make<ImportFile>(MB));
+ return;
+ }
+
+ InputFile *Obj;
+ if (Magic == file_magic::coff_object)
+ Obj = make<ObjectFile>(MB);
+ else if (Magic == file_magic::bitcode)
+ Obj = make<BitcodeFile>(MB);
+ else
+ fatal("unknown file type: " + MB.getBufferIdentifier());
+
+ Obj->ParentName = ParentName;
+ Symtab.addFile(Obj);
+ if (Config->Verbose)
+ outs() << "Loaded " << toString(Obj) << " for " << SymName << "\n";
+}
+
+void LinkerDriver::enqueueArchiveMember(const Archive::Child &C,
+ StringRef SymName,
+ StringRef ParentName) {
+ if (!C.getParent()->isThin()) {
+ MemoryBufferRef MB = check(
+ C.getMemoryBufferRef(),
+ "could not get the buffer for the member defining symbol " + SymName);
+ enqueueTask([=]() { Driver->addArchiveBuffer(MB, SymName, ParentName); });
+ return;
+ }
+
+ auto Future = std::make_shared<std::future<MBErrPair>>(createFutureForFile(
+ check(C.getFullName(),
+ "could not get the filename for the member defining symbol " +
+ SymName)));
+ enqueueTask([=]() {
+ auto MBOrErr = Future->get();
+ if (MBOrErr.second)
+ fatal(MBOrErr.second,
+ "could not get the buffer for the member defining " + SymName);
+ Driver->addArchiveBuffer(takeBuffer(std::move(MBOrErr.first)), SymName,
+ ParentName);
+ });
}
static bool isDecorated(StringRef Sym) {
@@ -87,7 +195,7 @@ static bool isDecorated(StringRef Sym) {
// Parses .drectve section contents and returns a list of files
// specified by /defaultlib.
void LinkerDriver::parseDirectives(StringRef S) {
- llvm::opt::InputArgList Args = Parser.parse(S);
+ opt::InputArgList Args = Parser.parse(S);
for (auto *Arg : Args) {
switch (Arg->getOption().getID()) {
@@ -95,10 +203,8 @@ void LinkerDriver::parseDirectives(StringRef S) {
parseAlternateName(Arg->getValue());
break;
case OPT_defaultlib:
- if (Optional<StringRef> Path = findLib(Arg->getValue())) {
- MemoryBufferRef MB = openFile(*Path);
- Symtab.addFile(createFile(MB));
- }
+ if (Optional<StringRef> Path = findLib(Arg->getValue()))
+ enqueuePath(*Path);
break;
case OPT_export: {
Export E = parseExport(Arg->getValue());
@@ -135,19 +241,19 @@ void LinkerDriver::parseDirectives(StringRef S) {
// Find file from search paths. You can omit ".obj", this function takes
// care of that. Note that the returned path is not guaranteed to exist.
StringRef LinkerDriver::doFindFile(StringRef Filename) {
- bool hasPathSep = (Filename.find_first_of("/\\") != StringRef::npos);
- if (hasPathSep)
+ bool HasPathSep = (Filename.find_first_of("/\\") != StringRef::npos);
+ if (HasPathSep)
return Filename;
- bool hasExt = (Filename.find('.') != StringRef::npos);
+ bool HasExt = (Filename.find('.') != StringRef::npos);
for (StringRef Dir : SearchPaths) {
SmallString<128> Path = Dir;
- llvm::sys::path::append(Path, Filename);
- if (llvm::sys::fs::exists(Path.str()))
- return Alloc.save(Path.str());
- if (!hasExt) {
+ sys::path::append(Path, Filename);
+ if (sys::fs::exists(Path.str()))
+ return Saver.save(Path.str());
+ if (!HasExt) {
Path.append(".obj");
- if (llvm::sys::fs::exists(Path.str()))
- return Alloc.save(Path.str());
+ if (sys::fs::exists(Path.str()))
+ return Saver.save(Path.str());
}
}
return Filename;
@@ -166,9 +272,9 @@ Optional<StringRef> LinkerDriver::findFile(StringRef Filename) {
// Find library file from search path.
StringRef LinkerDriver::doFindLib(StringRef Filename) {
// Add ".lib" to Filename if that has no file extension.
- bool hasExt = (Filename.find('.') != StringRef::npos);
- if (!hasExt)
- Filename = Alloc.save(Filename + ".lib");
+ bool HasExt = (Filename.find('.') != StringRef::npos);
+ if (!HasExt)
+ Filename = Saver.save(Filename + ".lib");
return doFindFile(Filename);
}
@@ -178,11 +284,12 @@ StringRef LinkerDriver::doFindLib(StringRef Filename) {
Optional<StringRef> LinkerDriver::findLib(StringRef Filename) {
if (Config->NoDefaultLibAll)
return None;
+ if (!VisitedLibs.insert(Filename.lower()).second)
+ return None;
StringRef Path = doFindLib(Filename);
if (Config->NoDefaultLibs.count(Path))
return None;
- bool Seen = !VisitedFiles.insert(Path.lower()).second;
- if (Seen)
+ if (!VisitedFiles.insert(Path.lower()).second)
return None;
return Path;
}
@@ -192,7 +299,7 @@ void LinkerDriver::addLibSearchPaths() {
Optional<std::string> EnvOpt = Process::GetEnv("LIB");
if (!EnvOpt.hasValue())
return;
- StringRef Env = Alloc.save(*EnvOpt);
+ StringRef Env = Saver.save(*EnvOpt);
while (!Env.empty()) {
StringRef Path;
std::tie(Path, Env) = Env.split(';');
@@ -200,17 +307,17 @@ void LinkerDriver::addLibSearchPaths() {
}
}
-Undefined *LinkerDriver::addUndefined(StringRef Name) {
- Undefined *U = Symtab.addUndefined(Name);
- Config->GCRoot.insert(U);
- return U;
+SymbolBody *LinkerDriver::addUndefined(StringRef Name) {
+ SymbolBody *B = Symtab.addUndefined(Name);
+ Config->GCRoot.insert(B);
+ return B;
}
// Symbol names are mangled by appending "_" prefix on x86.
StringRef LinkerDriver::mangle(StringRef Sym) {
assert(Config->Machine != IMAGE_FILE_MACHINE_UNKNOWN);
if (Config->Machine == I386)
- return Alloc.save("_" + Sym);
+ return Saver.save("_" + Sym);
return Sym;
}
@@ -225,7 +332,7 @@ StringRef LinkerDriver::findDefaultEntry() {
};
for (auto E : Entries) {
StringRef Entry = Symtab.findMangle(mangle(E[0]));
- if (!Entry.empty() && !isa<Undefined>(Symtab.find(Entry)->Body))
+ if (!Entry.empty() && !isa<Undefined>(Symtab.find(Entry)->body()))
return mangle(E[1]);
}
return "";
@@ -247,7 +354,83 @@ static uint64_t getDefaultImageBase() {
return Config->DLL ? 0x10000000 : 0x400000;
}
-void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
+static std::string createResponseFile(const opt::InputArgList &Args,
+ ArrayRef<StringRef> FilePaths,
+ ArrayRef<StringRef> SearchPaths) {
+ SmallString<0> Data;
+ raw_svector_ostream OS(Data);
+
+ for (auto *Arg : Args) {
+ switch (Arg->getOption().getID()) {
+ case OPT_linkrepro:
+ case OPT_INPUT:
+ case OPT_defaultlib:
+ case OPT_libpath:
+ break;
+ default:
+ OS << stringize(Arg) << "\n";
+ }
+ }
+
+ for (StringRef Path : SearchPaths) {
+ std::string RelPath = relativeToRoot(Path);
+ OS << "/libpath:" << quote(RelPath) << "\n";
+ }
+
+ for (StringRef Path : FilePaths)
+ OS << quote(relativeToRoot(Path)) << "\n";
+
+ return Data.str();
+}
+
+static unsigned getDefaultDebugType(const opt::InputArgList &Args) {
+ unsigned DebugTypes = static_cast<unsigned>(DebugType::CV);
+ if (Args.hasArg(OPT_driver))
+ DebugTypes |= static_cast<unsigned>(DebugType::PData);
+ if (Args.hasArg(OPT_profile))
+ DebugTypes |= static_cast<unsigned>(DebugType::Fixup);
+ return DebugTypes;
+}
+
+static unsigned parseDebugType(StringRef Arg) {
+ SmallVector<StringRef, 3> Types;
+ Arg.split(Types, ',', /*KeepEmpty=*/false);
+
+ unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
+ for (StringRef Type : Types)
+ DebugTypes |= StringSwitch<unsigned>(Type.lower())
+ .Case("cv", static_cast<unsigned>(DebugType::CV))
+ .Case("pdata", static_cast<unsigned>(DebugType::PData))
+ .Case("fixup", static_cast<unsigned>(DebugType::Fixup));
+ return DebugTypes;
+}
+
+static std::string getMapFile(const opt::InputArgList &Args) {
+ auto *Arg = Args.getLastArg(OPT_lldmap, OPT_lldmap_file);
+ if (!Arg)
+ return "";
+ if (Arg->getOption().getID() == OPT_lldmap_file)
+ return Arg->getValue();
+
+ assert(Arg->getOption().getID() == OPT_lldmap);
+ StringRef OutFile = Config->OutputFile;
+ return (OutFile.substr(0, OutFile.rfind('.')) + ".map").str();
+}
+
+void LinkerDriver::enqueueTask(std::function<void()> Task) {
+ TaskQueue.push_back(std::move(Task));
+}
+
+bool LinkerDriver::run() {
+ bool DidWork = !TaskQueue.empty();
+ while (!TaskQueue.empty()) {
+ TaskQueue.front()();
+ TaskQueue.pop_front();
+ }
+ return DidWork;
+}
+
+void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// If the first command line argument is "/lib", link.exe acts like lib.exe.
// We call our own implementation of lib.exe that understands bitcode files.
if (ArgsArr.size() > 1 && StringRef(ArgsArr[1]).equals_lower("/lib")) {
@@ -257,15 +440,15 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
}
// Needed for LTO.
- llvm::InitializeAllTargetInfos();
- llvm::InitializeAllTargets();
- llvm::InitializeAllTargetMCs();
- llvm::InitializeAllAsmParsers();
- llvm::InitializeAllAsmPrinters();
- llvm::InitializeAllDisassemblers();
+ InitializeAllTargetInfos();
+ InitializeAllTargets();
+ InitializeAllTargetMCs();
+ InitializeAllAsmParsers();
+ InitializeAllAsmPrinters();
+ InitializeAllDisassemblers();
// Parse command line options.
- llvm::opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1));
+ opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1));
// Handle /help
if (Args.hasArg(OPT_help)) {
@@ -273,6 +456,17 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
return;
}
+ if (auto *Arg = Args.getLastArg(OPT_linkrepro)) {
+ SmallString<64> Path = StringRef(Arg->getValue());
+ sys::path::append(Path, "repro");
+ ErrorOr<CpioFile *> F = CpioFile::create(Path);
+ if (F)
+ Cpio.reset(*F);
+ else
+ errs() << "/linkrepro: failed to open " << Path
+ << ".cpio: " << F.getError().message() << '\n';
+ }
+
if (Args.filtered_begin(OPT_INPUT) == Args.filtered_end())
fatal("no input files");
@@ -295,8 +489,17 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
Config->Force = true;
// Handle /debug
- if (Args.hasArg(OPT_debug))
+ if (Args.hasArg(OPT_debug)) {
Config->Debug = true;
+ Config->DebugTypes =
+ Args.hasArg(OPT_debugtype)
+ ? parseDebugType(Args.getLastArg(OPT_debugtype)->getValue())
+ : getDefaultDebugType(Args);
+ }
+
+ // Create a dummy PDB file to satisfy build sytem rules.
+ if (auto *Arg = Args.getLastArg(OPT_pdb))
+ Config->PDBPath = Arg->getValue();
// Handle /noentry
if (Args.hasArg(OPT_noentry)) {
@@ -447,72 +650,43 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
Config->TerminalServerAware = false;
if (Args.hasArg(OPT_nosymtab))
Config->WriteSymtab = false;
+ Config->DumpPdb = Args.hasArg(OPT_dumppdb);
+ Config->DebugPdb = Args.hasArg(OPT_debugpdb);
// Create a list of input files. Files can be given as arguments
// for /defaultlib option.
- std::vector<StringRef> Paths;
std::vector<MemoryBufferRef> MBs;
for (auto *Arg : Args.filtered(OPT_INPUT))
if (Optional<StringRef> Path = findFile(Arg->getValue()))
- Paths.push_back(*Path);
+ enqueuePath(*Path);
for (auto *Arg : Args.filtered(OPT_defaultlib))
if (Optional<StringRef> Path = findLib(Arg->getValue()))
- Paths.push_back(*Path);
- for (StringRef Path : Paths)
- MBs.push_back(openFile(Path));
+ enqueuePath(*Path);
// Windows specific -- Create a resource file containing a manifest file.
- if (Config->Manifest == Configuration::Embed) {
- std::unique_ptr<MemoryBuffer> MB = createManifestRes();
- MBs.push_back(MB->getMemBufferRef());
- OwningMBs.push_back(std::move(MB)); // take ownership
- }
+ if (Config->Manifest == Configuration::Embed)
+ addBuffer(createManifestRes());
- // Windows specific -- Input files can be Windows resource files (.res files).
- // We invoke cvtres.exe to convert resource files to a regular COFF file
- // then link the result file normally.
- std::vector<MemoryBufferRef> Resources;
- auto NotResource = [](MemoryBufferRef MB) {
- return identify_magic(MB.getBuffer()) != file_magic::windows_resource;
- };
- auto It = std::stable_partition(MBs.begin(), MBs.end(), NotResource);
- if (It != MBs.end()) {
- Resources.insert(Resources.end(), It, MBs.end());
- MBs.erase(It, MBs.end());
- }
+ // Read all input files given via the command line.
+ run();
- // Read all input files given via the command line. Note that step()
- // doesn't read files that are specified by directive sections.
- for (MemoryBufferRef MB : MBs)
- Symtab.addFile(createFile(MB));
- Symtab.step();
-
- // Determine machine type and check if all object files are
- // for the same CPU type. Note that this needs to be done before
- // any call to mangle().
- for (std::unique_ptr<InputFile> &File : Symtab.getFiles()) {
- MachineTypes MT = File->getMachineType();
- if (MT == IMAGE_FILE_MACHINE_UNKNOWN)
- continue;
- if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
- Config->Machine = MT;
- continue;
- }
- if (Config->Machine != MT)
- fatal(File->getShortName() + ": machine type " + machineToStr(MT) +
- " conflicts with " + machineToStr(Config->Machine));
- }
+ // We should have inferred a machine type by now from the input files, but if
+ // not we assume x64.
if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
- llvm::errs() << "warning: /machine is not specified. x64 is assumed.\n";
+ errs() << "warning: /machine is not specified. x64 is assumed.\n";
Config->Machine = AMD64;
}
- // Windows specific -- Convert Windows resource files to a COFF file.
- if (!Resources.empty()) {
- std::unique_ptr<MemoryBuffer> MB = convertResToCOFF(Resources);
- Symtab.addFile(createFile(MB->getMemBufferRef()));
- OwningMBs.push_back(std::move(MB)); // take ownership
- }
+ // Windows specific -- Input files can be Windows resource files (.res files).
+ // We invoke cvtres.exe to convert resource files to a regular COFF file
+ // then link the result file normally.
+ if (!Resources.empty())
+ addBuffer(convertResToCOFF(Resources));
+
+ if (Cpio)
+ Cpio->append("response.txt",
+ createResponseFile(Args, FilePaths,
+ ArrayRef<StringRef>(SearchPaths).slice(1)));
// Handle /largeaddressaware
if (Config->is64() || Args.hasArg(OPT_largeaddressaware))
@@ -537,7 +711,7 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
fatal("entry point must be defined");
Config->Entry = addUndefined(S);
if (Config->Verbose)
- llvm::outs() << "Entry name inferred: " << S << "\n";
+ outs() << "Entry name inferred: " << S << "\n";
}
// Handle /export
@@ -545,18 +719,19 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
Export E = parseExport(Arg->getValue());
if (Config->Machine == I386) {
if (!isDecorated(E.Name))
- E.Name = Alloc.save("_" + E.Name);
+ E.Name = Saver.save("_" + E.Name);
if (!E.ExtName.empty() && !isDecorated(E.ExtName))
- E.ExtName = Alloc.save("_" + E.ExtName);
+ E.ExtName = Saver.save("_" + E.ExtName);
}
Config->Exports.push_back(E);
}
// Handle /def
if (auto *Arg = Args.getLastArg(OPT_deffile)) {
- MemoryBufferRef MB = openFile(Arg->getValue());
// parseModuleDefs mutates Config object.
- parseModuleDefs(MB, &Alloc);
+ parseModuleDefs(
+ takeBuffer(check(MemoryBuffer::getFile(Arg->getValue()),
+ Twine("could not open ") + Arg->getValue())));
}
// Handle /delayload
@@ -585,14 +760,10 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
Symtab.addAbsolute(mangle("__guard_fids_count"), 0);
Symtab.addAbsolute(mangle("__guard_flags"), 0x100);
- // Read as much files as we can from directives sections.
- Symtab.run();
-
- // Resolve auxiliary symbols until we get a convergence.
- // (Trying to resolve a symbol may trigger a Lazy symbol to load a new file.
- // A new file may contain a directive section to add new command line options.
- // That's why we have to repeat until converge.)
- for (;;) {
+ // This code may add new undefined symbols to the link, which may enqueue more
+ // symbol resolution tasks, so we need to continue executing tasks until we
+ // converge.
+ do {
// Windows specific -- if entry point is not found,
// search for its mangled names.
if (Config->Entry)
@@ -615,7 +786,7 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
Symbol *Sym = Symtab.find(From);
if (!Sym)
continue;
- if (auto *U = dyn_cast<Undefined>(Sym->Body))
+ if (auto *U = dyn_cast<Undefined>(Sym->body()))
if (!U->WeakAlias)
U->WeakAlias = Symtab.addUndefined(To);
}
@@ -623,18 +794,15 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
// Windows specific -- if __load_config_used can be resolved, resolve it.
if (Symtab.findUnderscore("_load_config_used"))
addUndefined(mangle("_load_config_used"));
-
- if (Symtab.queueEmpty())
- break;
- Symtab.run();
- }
+ } while (run());
// Do LTO by compiling bitcode input files to a set of native COFF files then
// link those files.
Symtab.addCombinedLTOObjects();
+ run();
// Make sure we have resolved all symbols.
- Symtab.reportRemainingUndefines(/*Resolve=*/true);
+ Symtab.reportRemainingUndefines();
// Windows specific -- if no /subsystem is given, we need to infer
// that from entry point name.
@@ -662,10 +830,6 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
if (Config->Manifest == Configuration::SideBySide)
createSideBySideManifest();
- // Create a dummy PDB file to satisfy build sytem rules.
- if (auto *Arg = Args.getLastArg(OPT_pdb))
- createPDB(Arg->getValue());
-
// Identify unreferenced COMDAT sections.
if (Config->DoGC)
markLive(Symtab.getChunks());
@@ -679,13 +843,15 @@ void LinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) {
// Create a symbol map file containing symbol VAs and their names
// to help debugging.
- if (auto *Arg = Args.getLastArg(OPT_lldmap)) {
+ std::string MapFile = getMapFile(Args);
+ if (!MapFile.empty()) {
std::error_code EC;
- llvm::raw_fd_ostream Out(Arg->getValue(), EC, OpenFlags::F_Text);
+ raw_fd_ostream Out(MapFile, EC, OpenFlags::F_Text);
if (EC)
- fatal(EC, "could not create the symbol map");
+ fatal(EC, "could not create the symbol map " + MapFile);
Symtab.printMap(Out);
}
+
// Call exit to avoid calling destructors.
exit(0);
}
diff --git a/COFF/Driver.h b/COFF/Driver.h
index 23969ee802fb..e8114640edec 100644
--- a/COFF/Driver.h
+++ b/COFF/Driver.h
@@ -13,12 +13,13 @@
#include "Config.h"
#include "SymbolTable.h"
#include "lld/Core/LLVM.h"
+#include "lld/Core/Reproduce.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/Archive.h"
#include "llvm/Object/COFF.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
-#include "llvm/Support/StringSaver.h"
#include <memory>
#include <set>
#include <vector>
@@ -42,7 +43,6 @@ void doICF(const std::vector<Chunk *> &Chunks);
class ArgParser {
public:
- ArgParser() : Alloc(AllocAux) {}
// Parses command line options.
llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> Args);
@@ -56,25 +56,26 @@ private:
std::vector<const char *> tokenize(StringRef S);
std::vector<const char *> replaceResponseFiles(std::vector<const char *>);
-
- llvm::BumpPtrAllocator AllocAux;
- llvm::StringSaver Alloc;
};
class LinkerDriver {
public:
- LinkerDriver() : Alloc(AllocAux) {}
+ LinkerDriver() { coff::Symtab = &Symtab; }
void link(llvm::ArrayRef<const char *> Args);
// Used by the resolver to parse .drectve section contents.
void parseDirectives(StringRef S);
+ // Used by ArchiveFile to enqueue members.
+ void enqueueArchiveMember(const Archive::Child &C, StringRef SymName,
+ StringRef ParentName);
+
private:
- llvm::BumpPtrAllocator AllocAux;
- llvm::StringSaver Alloc;
ArgParser Parser;
SymbolTable Symtab;
+ std::unique_ptr<CpioFile> Cpio; // for /linkrepro
+
// Opens a file. Path has to be resolved already.
MemoryBufferRef openFile(StringRef Path);
@@ -90,8 +91,9 @@ private:
// Library search path. The first element is always "" (current directory).
std::vector<StringRef> SearchPaths;
std::set<std::string> VisitedFiles;
+ std::set<std::string> VisitedLibs;
- Undefined *addUndefined(StringRef Sym);
+ SymbolBody *addUndefined(StringRef Sym);
StringRef mangle(StringRef Sym);
// Windows specific -- "main" is not the only main function in Windows.
@@ -104,12 +106,26 @@ private:
StringRef findDefaultEntry();
WindowsSubsystem inferSubsystem();
+ MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> MB);
+ void addBuffer(std::unique_ptr<MemoryBuffer> MB);
+ void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName,
+ StringRef ParentName);
+
+ void enqueuePath(StringRef Path);
+
+ void enqueueTask(std::function<void()> Task);
+ bool run();
+
// Driver is the owner of all opened files.
// InputFiles have MemoryBufferRefs to them.
std::vector<std::unique_ptr<MemoryBuffer>> OwningMBs;
+
+ std::list<std::function<void()>> TaskQueue;
+ std::vector<StringRef> FilePaths;
+ std::vector<MemoryBufferRef> Resources;
};
-void parseModuleDefs(MemoryBufferRef MB, llvm::StringSaver *Alloc);
+void parseModuleDefs(MemoryBufferRef MB);
void writeImportLibrary();
// Functions below this line are defined in DriverUtils.cpp.
@@ -161,8 +177,6 @@ void checkFailIfMismatch(StringRef Arg);
std::unique_ptr<MemoryBuffer>
convertResToCOFF(const std::vector<MemoryBufferRef> &MBs);
-void createPDB(StringRef Path);
-
// Create enum with OPT_xxx values for each option in Options.td
enum {
OPT_INVALID = 0,
diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp
index 5d7dc2bc65af..14dd004f1c04 100644
--- a/COFF/DriverUtils.cpp
+++ b/COFF/DriverUtils.cpp
@@ -16,6 +16,7 @@
#include "Config.h"
#include "Driver.h"
#include "Error.h"
+#include "Memory.h"
#include "Symbols.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSwitch.h"
@@ -43,29 +44,29 @@ namespace {
class Executor {
public:
explicit Executor(StringRef S) : Saver(Alloc), Prog(Saver.save(S)) {}
- void add(StringRef S) { Args.push_back(Saver.save(S)); }
- void add(std::string &S) { Args.push_back(Saver.save(S)); }
- void add(Twine S) { Args.push_back(Saver.save(S)); }
- void add(const char *S) { Args.push_back(Saver.save(S)); }
+ void add(StringRef S) { Args.push_back(Saver.save(S).data()); }
+ void add(std::string &S) { Args.push_back(Saver.save(S).data()); }
+ void add(Twine S) { Args.push_back(Saver.save(S).data()); }
+ void add(const char *S) { Args.push_back(Saver.save(S).data()); }
void run() {
- ErrorOr<std::string> ExeOrErr = llvm::sys::findProgramByName(Prog);
+ ErrorOr<std::string> ExeOrErr = sys::findProgramByName(Prog);
if (auto EC = ExeOrErr.getError())
fatal(EC, "unable to find " + Prog + " in PATH: ");
- const char *Exe = Saver.save(*ExeOrErr);
+ const char *Exe = Saver.save(*ExeOrErr).data();
Args.insert(Args.begin(), Exe);
Args.push_back(nullptr);
- if (llvm::sys::ExecuteAndWait(Args[0], Args.data()) != 0) {
+ if (sys::ExecuteAndWait(Args[0], Args.data()) != 0) {
for (const char *S : Args)
if (S)
- llvm::errs() << S << " ";
+ errs() << S << " ";
fatal("ExecuteAndWait failed");
}
}
private:
- llvm::BumpPtrAllocator Alloc;
- llvm::StringSaver Saver;
+ BumpPtrAllocator Alloc;
+ StringSaver Saver;
StringRef Prog;
std::vector<const char *> Args;
};
@@ -75,10 +76,8 @@ private:
// Returns /machine's value.
MachineTypes getMachineType(StringRef S) {
MachineTypes MT = StringSwitch<MachineTypes>(S.lower())
- .Case("x64", AMD64)
- .Case("amd64", AMD64)
- .Case("x86", I386)
- .Case("i386", I386)
+ .Cases("x64", "amd64", AMD64)
+ .Cases("x86", "i386", I386)
.Case("arm", ARMNT)
.Default(IMAGE_FILE_MACHINE_UNKNOWN);
if (MT != IMAGE_FILE_MACHINE_UNKNOWN)
@@ -168,8 +167,8 @@ void parseMerge(StringRef S) {
if (!Inserted) {
StringRef Existing = Pair.first->second;
if (Existing != To)
- llvm::errs() << "warning: " << S << ": already merged into "
- << Existing << "\n";
+ errs() << "warning: " << S << ": already merged into " << Existing
+ << "\n";
}
}
@@ -279,18 +278,54 @@ static void quoteAndPrint(raw_ostream &Out, StringRef S) {
}
}
+// An RAII temporary file class that automatically removes a temporary file.
+namespace {
+class TemporaryFile {
+public:
+ TemporaryFile(StringRef Prefix, StringRef Extn) {
+ SmallString<128> S;
+ if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S))
+ fatal(EC, "cannot create a temporary file");
+ Path = S.str();
+ }
+
+ TemporaryFile(TemporaryFile &&Obj) {
+ std::swap(Path, Obj.Path);
+ }
+
+ ~TemporaryFile() {
+ if (Path.empty())
+ return;
+ if (sys::fs::remove(Path))
+ fatal("failed to remove " + Path);
+ }
+
+ // Returns a memory buffer of this temporary file.
+ // Note that this function does not leave the file open,
+ // so it is safe to remove the file immediately after this function
+ // is called (you cannot remove an opened file on Windows.)
+ std::unique_ptr<MemoryBuffer> getMemoryBuffer() {
+ // IsVolatileSize=true forces MemoryBuffer to not use mmap().
+ return check(MemoryBuffer::getFile(Path, /*FileSize=*/-1,
+ /*RequiresNullTerminator=*/false,
+ /*IsVolatileSize=*/true),
+ "could not open " + Path);
+ }
+
+ std::string Path;
+};
+}
+
// Create the default manifest file as a temporary file.
-static std::string createDefaultXml() {
+TemporaryFile createDefaultXml() {
// Create a temporary file.
- SmallString<128> Path;
- if (auto EC = sys::fs::createTemporaryFile("tmp", "manifest", Path))
- fatal(EC, "cannot create a temporary file");
+ TemporaryFile File("defaultxml", "manifest");
// Open the temporary file for writing.
std::error_code EC;
- llvm::raw_fd_ostream OS(Path, EC, sys::fs::F_Text);
+ raw_fd_ostream OS(File.Path, EC, sys::fs::F_Text);
if (EC)
- fatal(EC, "failed to open " + Path);
+ fatal(EC, "failed to open " + File.Path);
// Emit the XML. Note that we do *not* verify that the XML attributes are
// syntactically correct. This is intentional for link.exe compatibility.
@@ -316,56 +351,48 @@ static std::string createDefaultXml() {
}
OS << "</assembly>\n";
OS.close();
- return StringRef(Path);
+ return File;
}
static std::string readFile(StringRef Path) {
std::unique_ptr<MemoryBuffer> MB =
check(MemoryBuffer::getFile(Path), "could not open " + Path);
- std::unique_ptr<MemoryBuffer> Buf(std::move(MB));
- return Buf->getBuffer();
+ return MB->getBuffer();
}
static std::string createManifestXml() {
// Create the default manifest file.
- std::string Path1 = createDefaultXml();
+ TemporaryFile File1 = createDefaultXml();
if (Config->ManifestInput.empty())
- return readFile(Path1);
+ return readFile(File1.Path);
// If manifest files are supplied by the user using /MANIFESTINPUT
// option, we need to merge them with the default manifest.
- SmallString<128> Path2;
- if (auto EC = sys::fs::createTemporaryFile("tmp", "manifest", Path2))
- fatal(EC, "cannot create a temporary file");
- FileRemover Remover1(Path1);
- FileRemover Remover2(Path2);
+ TemporaryFile File2("user", "manifest");
Executor E("mt.exe");
E.add("/manifest");
- E.add(Path1);
+ E.add(File1.Path);
for (StringRef Filename : Config->ManifestInput) {
E.add("/manifest");
E.add(Filename);
}
E.add("/nologo");
- E.add("/out:" + StringRef(Path2));
+ E.add("/out:" + StringRef(File2.Path));
E.run();
- return readFile(Path2);
+ return readFile(File2.Path);
}
// Create a resource file containing a manifest XML.
std::unique_ptr<MemoryBuffer> createManifestRes() {
// Create a temporary file for the resource script file.
- SmallString<128> RCPath;
- if (auto EC = sys::fs::createTemporaryFile("tmp", "rc", RCPath))
- fatal(EC, "cannot create a temporary file");
- FileRemover RCRemover(RCPath);
+ TemporaryFile RCFile("manifest", "rc");
// Open the temporary file for writing.
std::error_code EC;
- llvm::raw_fd_ostream Out(RCPath, EC, sys::fs::F_Text);
+ raw_fd_ostream Out(RCFile.Path, EC, sys::fs::F_Text);
if (EC)
- fatal(EC, "failed to open " + RCPath);
+ fatal(EC, "failed to open " + RCFile.Path);
// Write resource script to the RC file.
Out << "#define LANG_ENGLISH 9\n"
@@ -379,17 +406,15 @@ std::unique_ptr<MemoryBuffer> createManifestRes() {
Out.close();
// Create output resource file.
- SmallString<128> ResPath;
- if (auto EC = sys::fs::createTemporaryFile("tmp", "res", ResPath))
- fatal(EC, "cannot create a temporary file");
+ TemporaryFile ResFile("output-resource", "res");
Executor E("rc.exe");
E.add("/fo");
- E.add(ResPath.str());
+ E.add(ResFile.Path);
E.add("/nologo");
- E.add(RCPath.str());
+ E.add(RCFile.Path);
E.run();
- return check(MemoryBuffer::getFile(ResPath), "could not open " + ResPath);
+ return ResFile.getMemoryBuffer();
}
void createSideBySideManifest() {
@@ -397,7 +422,7 @@ void createSideBySideManifest() {
if (Path == "")
Path = Config->OutputFile + ".manifest";
std::error_code EC;
- llvm::raw_fd_ostream Out(Path, EC, llvm::sys::fs::F_Text);
+ raw_fd_ostream Out(Path, EC, sys::fs::F_Text);
if (EC)
fatal(EC, "failed to create manifest");
Out << createManifestXml();
@@ -485,12 +510,14 @@ void fixupExports() {
}
for (Export &E : Config->Exports) {
+ SymbolBody *Sym = E.Sym;
if (!E.ForwardTo.empty()) {
E.SymbolName = E.Name;
- } else if (Undefined *U = cast_or_null<Undefined>(E.Sym->WeakAlias)) {
- E.SymbolName = U->getName();
} else {
- E.SymbolName = E.Sym->getName();
+ if (auto *U = dyn_cast<Undefined>(Sym))
+ if (U->WeakAlias)
+ Sym = U->WeakAlias;
+ E.SymbolName = Sym->getName();
}
}
@@ -515,7 +542,7 @@ void fixupExports() {
Export *Existing = Pair.first->second;
if (E == *Existing || E.Name != Existing->Name)
continue;
- llvm::errs() << "warning: duplicate /export option: " << E.Name << "\n";
+ errs() << "warning: duplicate /export option: " << E.Name << "\n";
}
Config->Exports = std::move(V);
@@ -555,20 +582,39 @@ void checkFailIfMismatch(StringRef Arg) {
std::unique_ptr<MemoryBuffer>
convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) {
// Create an output file path.
- SmallString<128> Path;
- if (auto EC = llvm::sys::fs::createTemporaryFile("resource", "obj", Path))
- fatal(EC, "could not create temporary file");
+ TemporaryFile File("resource-file", "obj");
// Execute cvtres.exe.
Executor E("cvtres.exe");
E.add("/machine:" + machineToStr(Config->Machine));
E.add("/readonly");
E.add("/nologo");
- E.add("/out:" + Path);
- for (MemoryBufferRef MB : MBs)
- E.add(MB.getBufferIdentifier());
+ E.add("/out:" + Twine(File.Path));
+
+ // We must create new files because the memory buffers we have may have no
+ // underlying file still existing on the disk.
+ // It happens if it was created from a TemporaryFile, which usually delete
+ // the file just after creating the MemoryBuffer.
+ std::vector<TemporaryFile> ResFiles;
+ ResFiles.reserve(MBs.size());
+ for (MemoryBufferRef MB : MBs) {
+ // We store the temporary file in a vector to avoid deletion
+ // before running cvtres
+ ResFiles.emplace_back("resource-file", "res");
+ TemporaryFile& ResFile = ResFiles.back();
+ // Write the content of the resource in a temporary file
+ std::error_code EC;
+ raw_fd_ostream OS(ResFile.Path, EC, sys::fs::F_None);
+ if (EC)
+ fatal(EC, "failed to open " + ResFile.Path);
+ OS << MB.getBuffer();
+ OS.close();
+
+ E.add(ResFile.Path);
+ }
+
E.run();
- return check(MemoryBuffer::getFile(Path), "could not open " + Path);
+ return File.getMemoryBuffer();
}
// Create OptTable
@@ -595,7 +641,7 @@ public:
};
// Parses a given list of options.
-llvm::opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) {
+opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) {
// First, replace respnose files (@<file>-style options).
std::vector<const char *> Argv = replaceResponseFiles(ArgsArr);
@@ -603,28 +649,28 @@ llvm::opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) {
COFFOptTable Table;
unsigned MissingIndex;
unsigned MissingCount;
- llvm::opt::InputArgList Args =
- Table.ParseArgs(Argv, MissingIndex, MissingCount);
+ opt::InputArgList Args = Table.ParseArgs(Argv, MissingIndex, MissingCount);
// Print the real command line if response files are expanded.
if (Args.hasArg(OPT_verbose) && ArgsArr.size() != Argv.size()) {
- llvm::outs() << "Command line:";
+ outs() << "Command line:";
for (const char *S : Argv)
- llvm::outs() << " " << S;
- llvm::outs() << "\n";
+ outs() << " " << S;
+ outs() << "\n";
}
if (MissingCount)
- fatal("missing arg value for \"" + Twine(Args.getArgString(MissingIndex)) +
- "\", expected " + Twine(MissingCount) +
- (MissingCount == 1 ? " argument." : " arguments."));
+ fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
for (auto *Arg : Args.filtered(OPT_UNKNOWN))
- llvm::errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n";
+ errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n";
return Args;
}
-llvm::opt::InputArgList ArgParser::parseLINK(ArrayRef<const char *> Args) {
- // Concatenate LINK env and given arguments and parse them.
+// link.exe has an interesting feature. If LINK environment exists,
+// its contents are handled as a command line string. So you can pass
+// extra arguments using the environment variable.
+opt::InputArgList ArgParser::parseLINK(ArrayRef<const char *> Args) {
+ // Concatenate LINK env and command line arguments, and then parse them.
Optional<std::string> Env = Process::GetEnv("LINK");
if (!Env)
return parse(Args);
@@ -635,8 +681,7 @@ llvm::opt::InputArgList ArgParser::parseLINK(ArrayRef<const char *> Args) {
std::vector<const char *> ArgParser::tokenize(StringRef S) {
SmallVector<const char *, 16> Tokens;
- StringSaver Saver(AllocAux);
- llvm::cl::TokenizeWindowsCommandLine(S, Saver, Tokens);
+ cl::TokenizeWindowsCommandLine(S, Saver, Tokens);
return std::vector<const char *>(Tokens.begin(), Tokens.end());
}
@@ -645,14 +690,13 @@ std::vector<const char *> ArgParser::tokenize(StringRef S) {
std::vector<const char *>
ArgParser::replaceResponseFiles(std::vector<const char *> Argv) {
SmallVector<const char *, 256> Tokens(Argv.data(), Argv.data() + Argv.size());
- StringSaver Saver(AllocAux);
ExpandResponseFiles(Saver, TokenizeWindowsCommandLine, Tokens);
return std::vector<const char *>(Tokens.begin(), Tokens.end());
}
void printHelp(const char *Argv0) {
COFFOptTable Table;
- Table.PrintHelp(llvm::outs(), Argv0, "LLVM Linker", false);
+ Table.PrintHelp(outs(), Argv0, "LLVM Linker", false);
}
} // namespace coff
diff --git a/COFF/Error.cpp b/COFF/Error.cpp
index 602a8544ce2b..b2bd557413df 100644
--- a/COFF/Error.cpp
+++ b/COFF/Error.cpp
@@ -11,14 +11,31 @@
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/Process.h"
#include "llvm/Support/raw_ostream.h"
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+#include <unistd.h>
+#endif
+
+using namespace llvm;
+
namespace lld {
namespace coff {
void fatal(const Twine &Msg) {
- llvm::errs() << Msg << "\n";
- exit(1);
+ if (sys::Process::StandardErrHasColors()) {
+ errs().changeColor(raw_ostream::RED, /*bold=*/true);
+ errs() << "error: ";
+ errs().resetColor();
+ } else {
+ errs() << "error: ";
+ }
+ errs() << Msg << "\n";
+
+ outs().flush();
+ errs().flush();
+ _exit(1);
}
void fatal(std::error_code EC, const Twine &Msg) {
diff --git a/COFF/Error.h b/COFF/Error.h
index c9f64c662580..47549327db2b 100644
--- a/COFF/Error.h
+++ b/COFF/Error.h
@@ -32,6 +32,23 @@ template <class T> T check(Expected<T> E, const Twine &Prefix) {
return std::move(*E);
}
+template <class T> T check(ErrorOr<T> EO) {
+ if (!EO)
+ fatal(EO.getError().message());
+ return std::move(*EO);
+}
+
+template <class T> T check(Expected<T> E) {
+ if (!E) {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ logAllUnhandledErrors(E.takeError(), OS, "");
+ OS.flush();
+ fatal(Buf);
+ }
+ return std::move(*E);
+}
+
} // namespace coff
} // namespace lld
diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp
index a2c5a90334d0..196fbe2610ea 100644
--- a/COFF/ICF.cpp
+++ b/COFF/ICF.cpp
@@ -7,43 +7,19 @@
//
//===----------------------------------------------------------------------===//
//
-// Identical COMDAT Folding is a feature to merge COMDAT sections not by
-// name (which is regular COMDAT handling) but by contents. If two COMDAT
-// sections have the same data, relocations, attributes, etc., then the two
-// are considered identical and merged by the linker. This optimization
-// makes outputs smaller.
+// ICF is short for Identical Code Folding. That is a size optimization to
+// identify and merge two or more read-only sections (typically functions)
+// that happened to have the same contents. It usually reduces output size
+// by a few percent.
//
-// ICF is theoretically a problem of reducing graphs by merging as many
-// identical subgraphs as possible, if we consider sections as vertices and
-// relocations as edges. This may be a bit more complicated problem than you
-// might think. The order of processing sections matters since merging two
-// sections can make other sections, whose relocations now point to the same
-// section, mergeable. Graphs may contain cycles, which is common in COFF.
-// We need a sophisticated algorithm to do this properly and efficiently.
+// On Windows, ICF is enabled by default.
//
-// What we do in this file is this. We split sections into groups. Sections
-// in the same group are considered identical.
-//
-// First, all sections are grouped by their "constant" values. Constant
-// values are values that are never changed by ICF, such as section contents,
-// section name, number of relocations, type and offset of each relocation,
-// etc. Because we do not care about some relocation targets in this step,
-// two sections in the same group may not be identical, but at least two
-// sections in different groups can never be identical.
-//
-// Then, we try to split each group by relocation targets. Relocations are
-// considered identical if and only if the relocation targets are in the
-// same group. Splitting a group may make more groups to be splittable,
-// because two relocations that were previously considered identical might
-// now point to different groups. We repeat this step until the convergence
-// is obtained.
-//
-// This algorithm is so-called "optimistic" algorithm described in
-// http://research.google.com/pubs/pub36912.html.
+// See ELF/ICF.cpp for the details about the algortihm.
//
//===----------------------------------------------------------------------===//
#include "Chunks.h"
+#include "Error.h"
#include "Symbols.h"
#include "lld/Core/Parallel.h"
#include "llvm/ADT/Hashing.h"
@@ -58,29 +34,34 @@ using namespace llvm;
namespace lld {
namespace coff {
-typedef std::vector<SectionChunk *>::iterator ChunkIterator;
-typedef bool (*Comparator)(const SectionChunk *, const SectionChunk *);
-
class ICF {
public:
void run(const std::vector<Chunk *> &V);
private:
- static uint64_t getHash(SectionChunk *C);
- static bool equalsConstant(const SectionChunk *A, const SectionChunk *B);
- static bool equalsVariable(const SectionChunk *A, const SectionChunk *B);
- bool forEachGroup(std::vector<SectionChunk *> &Chunks, Comparator Eq);
- bool segregate(ChunkIterator Begin, ChunkIterator End, Comparator Eq);
+ void segregate(size_t Begin, size_t End, bool Constant);
- std::atomic<uint64_t> NextID = { 1 };
-};
+ bool equalsConstant(const SectionChunk *A, const SectionChunk *B);
+ bool equalsVariable(const SectionChunk *A, const SectionChunk *B);
-// Entry point to ICF.
-void doICF(const std::vector<Chunk *> &Chunks) {
- ICF().run(Chunks);
-}
+ uint32_t getHash(SectionChunk *C);
+ bool isEligible(SectionChunk *C);
+
+ size_t findBoundary(size_t Begin, size_t End);
+
+ void forEachColorRange(size_t Begin, size_t End,
+ std::function<void(size_t, size_t)> Fn);
+
+ void forEachColor(std::function<void(size_t, size_t)> Fn);
+
+ std::vector<SectionChunk *> Chunks;
+ int Cnt = 0;
+ std::atomic<uint32_t> NextId = {1};
+ std::atomic<bool> Repeat = {false};
+};
-uint64_t ICF::getHash(SectionChunk *C) {
+// Returns a hash value for S.
+uint32_t ICF::getHash(SectionChunk *C) {
return hash_combine(C->getPermissions(),
hash_value(C->SectionName),
C->NumRelocs,
@@ -89,16 +70,44 @@ uint64_t ICF::getHash(SectionChunk *C) {
C->Checksum);
}
-bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
- if (A->AssocChildren.size() != B->AssocChildren.size() ||
- A->NumRelocs != B->NumRelocs) {
- return false;
+// Returns true if section S is subject of ICF.
+bool ICF::isEligible(SectionChunk *C) {
+ bool Global = C->Sym && C->Sym->isExternal();
+ bool Writable = C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
+ return C->isCOMDAT() && C->isLive() && Global && !Writable;
+}
+
+// Split a range into smaller ranges by recoloring sections
+void ICF::segregate(size_t Begin, size_t End, bool Constant) {
+ while (Begin < End) {
+ // Divide [Begin, End) into two. Let Mid be the start index of the
+ // second group.
+ auto Bound = std::stable_partition(
+ Chunks.begin() + Begin + 1, Chunks.begin() + End, [&](SectionChunk *S) {
+ if (Constant)
+ return equalsConstant(Chunks[Begin], S);
+ return equalsVariable(Chunks[Begin], S);
+ });
+ size_t Mid = Bound - Chunks.begin();
+
+ // Split [Begin, End) into [Begin, Mid) and [Mid, End).
+ uint32_t Id = NextId++;
+ for (size_t I = Begin; I < Mid; ++I)
+ Chunks[I]->Color[(Cnt + 1) % 2] = Id;
+
+ // If we created a group, we need to iterate the main loop again.
+ if (Mid != End)
+ Repeat = true;
+
+ Begin = Mid;
}
+}
- // Compare associative sections.
- for (size_t I = 0, E = A->AssocChildren.size(); I != E; ++I)
- if (A->AssocChildren[I]->GroupID != B->AssocChildren[I]->GroupID)
- return false;
+// Compare "non-moving" part of two sections, namely everything
+// except relocation targets.
+bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
+ if (A->NumRelocs != B->NumRelocs)
+ return false;
// Compare relocations.
auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) {
@@ -106,14 +115,14 @@ bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
R1.VirtualAddress != R2.VirtualAddress) {
return false;
}
- SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex)->repl();
- SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex)->repl();
+ SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex);
+ SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex);
if (B1 == B2)
return true;
if (auto *D1 = dyn_cast<DefinedRegular>(B1))
if (auto *D2 = dyn_cast<DefinedRegular>(B2))
return D1->getValue() == D2->getValue() &&
- D1->getChunk()->GroupID == D2->getChunk()->GroupID;
+ D1->getChunk()->Color[Cnt % 2] == D2->getChunk()->Color[Cnt % 2];
return false;
};
if (!std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq))
@@ -128,54 +137,57 @@ bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
A->getContents() == B->getContents();
}
+// Compare "moving" part of two sections, namely relocation targets.
bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) {
- // Compare associative sections.
- for (size_t I = 0, E = A->AssocChildren.size(); I != E; ++I)
- if (A->AssocChildren[I]->GroupID != B->AssocChildren[I]->GroupID)
- return false;
-
// Compare relocations.
auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) {
- SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex)->repl();
- SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex)->repl();
+ SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex);
+ SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex);
if (B1 == B2)
return true;
if (auto *D1 = dyn_cast<DefinedRegular>(B1))
if (auto *D2 = dyn_cast<DefinedRegular>(B2))
- return D1->getChunk()->GroupID == D2->getChunk()->GroupID;
+ return D1->getChunk()->Color[Cnt % 2] == D2->getChunk()->Color[Cnt % 2];
return false;
};
return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq);
}
-bool ICF::segregate(ChunkIterator Begin, ChunkIterator End, Comparator Eq) {
- bool R = false;
- for (auto It = Begin;;) {
- SectionChunk *Head = *It;
- auto Bound = std::partition(It + 1, End, [&](SectionChunk *SC) {
- return Eq(Head, SC);
- });
- if (Bound == End)
- return R;
- uint64_t ID = NextID++;
- std::for_each(It, Bound, [&](SectionChunk *SC) { SC->GroupID = ID; });
- It = Bound;
- R = true;
+size_t ICF::findBoundary(size_t Begin, size_t End) {
+ for (size_t I = Begin + 1; I < End; ++I)
+ if (Chunks[Begin]->Color[Cnt % 2] != Chunks[I]->Color[Cnt % 2])
+ return I;
+ return End;
+}
+
+void ICF::forEachColorRange(size_t Begin, size_t End,
+ std::function<void(size_t, size_t)> Fn) {
+ if (Begin > 0)
+ Begin = findBoundary(Begin - 1, End);
+
+ while (Begin < End) {
+ size_t Mid = findBoundary(Begin, Chunks.size());
+ Fn(Begin, Mid);
+ Begin = Mid;
}
}
-bool ICF::forEachGroup(std::vector<SectionChunk *> &Chunks, Comparator Eq) {
- bool R = false;
- for (auto It = Chunks.begin(), End = Chunks.end(); It != End;) {
- SectionChunk *Head = *It;
- auto Bound = std::find_if(It + 1, End, [&](SectionChunk *SC) {
- return SC->GroupID != Head->GroupID;
- });
- if (segregate(It, Bound, Eq))
- R = true;
- It = Bound;
+// Call Fn on each color group.
+void ICF::forEachColor(std::function<void(size_t, size_t)> Fn) {
+ // If the number of sections are too small to use threading,
+ // call Fn sequentially.
+ if (Chunks.size() < 1024) {
+ forEachColorRange(0, Chunks.size(), Fn);
+ return;
}
- return R;
+
+ // Split sections into 256 shards and call Fn in parallel.
+ size_t NumShards = 256;
+ size_t Step = Chunks.size() / NumShards;
+ parallel_for(size_t(0), NumShards, [&](size_t I) {
+ forEachColorRange(I * Step, (I + 1) * Step, Fn);
+ });
+ forEachColorRange(Step * NumShards, Chunks.size(), Fn);
}
// Merge identical COMDAT sections.
@@ -183,62 +195,62 @@ bool ICF::forEachGroup(std::vector<SectionChunk *> &Chunks, Comparator Eq) {
// contents and relocations are all the same.
void ICF::run(const std::vector<Chunk *> &Vec) {
// Collect only mergeable sections and group by hash value.
- parallel_for_each(Vec.begin(), Vec.end(), [&](Chunk *C) {
- if (auto *SC = dyn_cast<SectionChunk>(C)) {
- bool Global = SC->Sym && SC->Sym->isExternal();
- bool Writable = SC->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
- if (SC->isCOMDAT() && SC->isLive() && Global && !Writable)
- SC->GroupID = getHash(SC) | (uint64_t(1) << 63);
- }
- });
- std::vector<SectionChunk *> Chunks;
for (Chunk *C : Vec) {
- if (auto *SC = dyn_cast<SectionChunk>(C)) {
- if (SC->GroupID) {
- Chunks.push_back(SC);
- } else {
- SC->GroupID = NextID++;
- }
+ auto *SC = dyn_cast<SectionChunk>(C);
+ if (!SC)
+ continue;
+
+ if (isEligible(SC)) {
+ // Set MSB to 1 to avoid collisions with non-hash colors.
+ SC->Color[0] = getHash(SC) | (1 << 31);
+ Chunks.push_back(SC);
+ } else {
+ SC->Color[0] = NextId++;
}
}
+ if (Chunks.empty())
+ return;
+
// From now on, sections in Chunks are ordered so that sections in
// the same group are consecutive in the vector.
- std::sort(Chunks.begin(), Chunks.end(),
- [](SectionChunk *A, SectionChunk *B) {
- return A->GroupID < B->GroupID;
- });
-
- // Split groups until we get a convergence.
- int Cnt = 1;
- forEachGroup(Chunks, equalsConstant);
-
- for (;;) {
- if (!forEachGroup(Chunks, equalsVariable))
- break;
+ std::stable_sort(Chunks.begin(), Chunks.end(),
+ [](SectionChunk *A, SectionChunk *B) {
+ return A->Color[0] < B->Color[0];
+ });
+
+ // Compare static contents and assign unique IDs for each static content.
+ forEachColor([&](size_t Begin, size_t End) { segregate(Begin, End, true); });
+ ++Cnt;
+
+ // Split groups by comparing relocations until convergence is obtained.
+ do {
+ Repeat = false;
+ forEachColor(
+ [&](size_t Begin, size_t End) { segregate(Begin, End, false); });
++Cnt;
- }
+ } while (Repeat);
+
if (Config->Verbose)
- llvm::outs() << "\nICF needed " << Cnt << " iterations.\n";
-
- // Merge sections in the same group.
- for (auto It = Chunks.begin(), End = Chunks.end(); It != End;) {
- SectionChunk *Head = *It++;
- auto Bound = std::find_if(It, End, [&](SectionChunk *SC) {
- return Head->GroupID != SC->GroupID;
- });
- if (It == Bound)
- continue;
+ outs() << "\nICF needed " << Cnt << " iterations\n";
+
+ // Merge sections in the same colors.
+ forEachColor([&](size_t Begin, size_t End) {
+ if (End - Begin == 1)
+ return;
+
if (Config->Verbose)
- llvm::outs() << "Selected " << Head->getDebugName() << "\n";
- while (It != Bound) {
- SectionChunk *SC = *It++;
+ outs() << "Selected " << Chunks[Begin]->getDebugName() << "\n";
+ for (size_t I = Begin + 1; I < End; ++I) {
if (Config->Verbose)
- llvm::outs() << " Removed " << SC->getDebugName() << "\n";
- Head->replace(SC);
+ outs() << " Removed " << Chunks[I]->getDebugName() << "\n";
+ Chunks[Begin]->replace(Chunks[I]);
}
- }
+ });
}
+// Entry point to ICF.
+void doICF(const std::vector<Chunk *> &Chunks) { ICF().run(Chunks); }
+
} // namespace coff
} // namespace lld
diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp
index ff26826371fa..0a97c2185f89 100644
--- a/COFF/InputFiles.cpp
+++ b/COFF/InputFiles.cpp
@@ -7,11 +7,15 @@
//
//===----------------------------------------------------------------------===//
+#include "InputFiles.h"
#include "Chunks.h"
#include "Config.h"
+#include "Driver.h"
#include "Error.h"
-#include "InputFiles.h"
+#include "Memory.h"
+#include "SymbolTable.h"
#include "Symbols.h"
+#include "llvm-c/lto.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/Twine.h"
@@ -26,88 +30,58 @@
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Target/TargetOptions.h"
-#include "llvm-c/lto.h"
#include <cstring>
#include <system_error>
#include <utility>
+using namespace llvm;
using namespace llvm::COFF;
using namespace llvm::object;
using namespace llvm::support::endian;
using llvm::Triple;
using llvm::support::ulittle32_t;
+using llvm::sys::fs::file_magic;
+using llvm::sys::fs::identify_magic;
namespace lld {
namespace coff {
-int InputFile::NextIndex = 0;
-llvm::LLVMContext BitcodeFile::Context;
-
-// Returns the last element of a path, which is supposed to be a filename.
-static StringRef getBasename(StringRef Path) {
- size_t Pos = Path.find_last_of("\\/");
- if (Pos == StringRef::npos)
- return Path;
- return Path.substr(Pos + 1);
-}
+LLVMContext BitcodeFile::Context;
-// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
-std::string InputFile::getShortName() {
- if (ParentName == "")
- return getName().lower();
- std::string Res = (getBasename(ParentName) + "(" +
- getBasename(getName()) + ")").str();
- return StringRef(Res).lower();
-}
+ArchiveFile::ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
void ArchiveFile::parse() {
// Parse a MemoryBufferRef as an archive file.
- File = check(Archive::create(MB), "failed to parse static library");
-
- // Allocate a buffer for Lazy objects.
- size_t NumSyms = File->getNumberOfSymbols();
- LazySymbols.reserve(NumSyms);
+ File = check(Archive::create(MB), toString(this));
// Read the symbol table to construct Lazy objects.
for (const Archive::Symbol &Sym : File->symbols())
- LazySymbols.emplace_back(this, Sym);
-
- // Seen is a map from member files to boolean values. Initially
- // all members are mapped to false, which indicates all these files
- // are not read yet.
- Error Err;
- for (auto &Child : File->children(Err))
- Seen[Child.getChildOffset()].clear();
- if (Err)
- fatal(Err, "failed to parse static library");
+ Symtab->addLazy(this, Sym);
}
// Returns a buffer pointing to a member file containing a given symbol.
-// This function is thread-safe.
-MemoryBufferRef ArchiveFile::getMember(const Archive::Symbol *Sym) {
+void ArchiveFile::addMember(const Archive::Symbol *Sym) {
const Archive::Child &C =
check(Sym->getMember(),
"could not get the member for symbol " + Sym->getName());
// Return an empty buffer if we have already returned the same buffer.
- if (Seen[C.getChildOffset()].test_and_set())
- return MemoryBufferRef();
- return check(C.getMemoryBufferRef(),
- "could not get the buffer for the member defining symbol " +
- Sym->getName());
+ if (!Seen.insert(C.getChildOffset()).second)
+ return;
+
+ Driver->enqueueArchiveMember(C, Sym->getName(), getName());
}
void ObjectFile::parse() {
// Parse a memory buffer as a COFF file.
- std::unique_ptr<Binary> Bin =
- check(createBinary(MB), "failed to parse object file");
+ std::unique_ptr<Binary> Bin = check(createBinary(MB), toString(this));
if (auto *Obj = dyn_cast<COFFObjectFile>(Bin.get())) {
Bin.release();
COFFObj.reset(Obj);
} else {
- fatal(getName() + " is not a COFF file");
+ fatal(toString(this) + " is not a COFF file");
}
// Read section and symbol tables.
@@ -137,13 +111,28 @@ void ObjectFile::initializeChunks() {
Directives = std::string((const char *)Data.data(), Data.size());
continue;
}
- // Skip non-DWARF debug info. MSVC linker converts the sections into
- // a PDB file, but we don't support that.
- if (Name == ".debug" || Name.startswith(".debug$"))
- continue;
- // We want to preserve DWARF debug sections only when /debug is on.
+
+ // Object files may have DWARF debug info or MS CodeView debug info
+ // (or both).
+ //
+ // DWARF sections don't need any special handling from the perspective
+ // of the linker; they are just a data section containing relocations.
+ // We can just link them to complete debug info.
+ //
+ // CodeView needs a linker support. We need to interpret and debug
+ // info, and then write it to a separate .pdb file.
+
+ // Ignore debug info unless /debug is given.
if (!Config->Debug && Name.startswith(".debug"))
continue;
+
+ // CodeView sections are stored to a different vector because they are
+ // not linked in the regular manner.
+ if (Name == ".debug" || Name.startswith(".debug$")) {
+ DebugChunks.push_back(new (Alloc) SectionChunk(this, Sec));
+ continue;
+ }
+
if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
continue;
auto *C = new (Alloc) SectionChunk(this, Sec);
@@ -156,12 +145,14 @@ void ObjectFile::initializeSymbols() {
uint32_t NumSymbols = COFFObj->getNumberOfSymbols();
SymbolBodies.reserve(NumSymbols);
SparseSymbolBodies.resize(NumSymbols);
- llvm::SmallVector<std::pair<Undefined *, uint32_t>, 8> WeakAliases;
+ SmallVector<std::pair<SymbolBody *, uint32_t>, 8> WeakAliases;
int32_t LastSectionNumber = 0;
for (uint32_t I = 0; I < NumSymbols; ++I) {
// Get a COFFSymbolRef object.
- COFFSymbolRef Sym =
- check(COFFObj->getSymbol(I), "broken object file: " + getName());
+ ErrorOr<COFFSymbolRef> SymOrErr = COFFObj->getSymbol(I);
+ if (!SymOrErr)
+ fatal(SymOrErr.getError(), "broken object file: " + toString(this));
+ COFFSymbolRef Sym = *SymOrErr;
const void *AuxP = nullptr;
if (Sym.getNumberOfAuxSymbols())
@@ -175,7 +166,7 @@ void ObjectFile::initializeSymbols() {
Body = createUndefined(Sym);
uint32_t TagIndex =
static_cast<const coff_aux_weak_external *>(AuxP)->TagIndex;
- WeakAliases.emplace_back((Undefined *)Body, TagIndex);
+ WeakAliases.emplace_back(Body, TagIndex);
} else {
Body = createDefined(Sym, AuxP, IsFirst);
}
@@ -186,23 +177,30 @@ void ObjectFile::initializeSymbols() {
I += Sym.getNumberOfAuxSymbols();
LastSectionNumber = Sym.getSectionNumber();
}
- for (auto WeakAlias : WeakAliases)
- WeakAlias.first->WeakAlias = SparseSymbolBodies[WeakAlias.second];
+ for (auto WeakAlias : WeakAliases) {
+ auto *U = dyn_cast<Undefined>(WeakAlias.first);
+ if (!U)
+ continue;
+ // Report an error if two undefined symbols have different weak aliases.
+ if (U->WeakAlias && U->WeakAlias != SparseSymbolBodies[WeakAlias.second])
+ Symtab->reportDuplicate(U->symbol(), this);
+ U->WeakAlias = SparseSymbolBodies[WeakAlias.second];
+ }
}
-Undefined *ObjectFile::createUndefined(COFFSymbolRef Sym) {
+SymbolBody *ObjectFile::createUndefined(COFFSymbolRef Sym) {
StringRef Name;
COFFObj->getSymbolName(Sym, Name);
- return new (Alloc) Undefined(Name);
+ return Symtab->addUndefined(Name, this, Sym.isWeakExternal())->body();
}
-Defined *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
- bool IsFirst) {
+SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
+ bool IsFirst) {
StringRef Name;
if (Sym.isCommon()) {
auto *C = new (Alloc) CommonChunk(Sym);
Chunks.push_back(C);
- return new (Alloc) DefinedCommon(this, Sym, C);
+ return Symtab->addCommon(this, Sym, C)->body();
}
if (Sym.isAbsolute()) {
COFFObj->getSymbolName(Sym, Name);
@@ -215,7 +213,10 @@ Defined *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
SEHCompat = true;
return nullptr;
}
- return new (Alloc) DefinedAbsolute(Name, Sym);
+ if (Sym.isExternal())
+ return Symtab->addAbsolute(Name, Sym)->body();
+ else
+ return new (Alloc) DefinedAbsolute(Name, Sym);
}
int32_t SectionNumber = Sym.getSectionNumber();
if (SectionNumber == llvm::COFF::IMAGE_SYM_DEBUG)
@@ -223,12 +224,12 @@ Defined *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
// Reserved sections numbers don't have contents.
if (llvm::COFF::isReservedSectionNumber(SectionNumber))
- fatal("broken object file: " + getName());
+ fatal("broken object file: " + toString(this));
// This symbol references a section which is not present in the section
// header.
if ((uint32_t)SectionNumber >= SparseChunks.size())
- fatal("broken object file: " + getName());
+ fatal("broken object file: " + toString(this));
// Nothing else to do without a section chunk.
auto *SC = cast_or_null<SectionChunk>(SparseChunks[SectionNumber]);
@@ -245,7 +246,11 @@ Defined *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
SC->Checksum = Aux->CheckSum;
}
- auto *B = new (Alloc) DefinedRegular(this, Sym, SC);
+ DefinedRegular *B;
+ if (Sym.isExternal())
+ B = cast<DefinedRegular>(Symtab->addRegular(this, Sym, SC)->body());
+ else
+ B = new (Alloc) DefinedRegular(this, Sym, SC);
if (SC->isCOMDAT() && Sym.getValue() == 0 && !AuxP)
SC->setSymbol(B);
@@ -307,28 +312,29 @@ void ImportFile::parse() {
ExtName = ExtName.substr(0, ExtName.find('@'));
break;
}
- ImpSym = new (Alloc) DefinedImportData(DLLName, ImpName, ExtName, Hdr);
- SymbolBodies.push_back(ImpSym);
+
+ this->Hdr = Hdr;
+ ExternalName = ExtName;
+
+ ImpSym = cast<DefinedImportData>(
+ Symtab->addImportData(ImpName, this)->body());
// If type is function, we need to create a thunk which jump to an
// address pointed by the __imp_ symbol. (This allows you to call
// DLL functions just like regular non-DLL functions.)
if (Hdr->getType() != llvm::COFF::IMPORT_CODE)
return;
- ThunkSym = new (Alloc) DefinedImportThunk(Name, ImpSym, Hdr->Machine);
- SymbolBodies.push_back(ThunkSym);
+ ThunkSym = cast<DefinedImportThunk>(
+ Symtab->addImportThunk(Name, ImpSym, Hdr->Machine)->body());
}
void BitcodeFile::parse() {
- // Usually parse() is thread-safe, but bitcode file is an exception.
- std::lock_guard<std::mutex> Lock(Mu);
-
Context.enableDebugTypeODRUniquing();
ErrorOr<std::unique_ptr<LTOModule>> ModOrErr = LTOModule::createFromBuffer(
Context, MB.getBufferStart(), MB.getBufferSize(), llvm::TargetOptions());
M = check(std::move(ModOrErr), "could not create LTO module");
- llvm::StringSaver Saver(Alloc);
+ StringSaver Saver(Alloc);
for (unsigned I = 0, E = M->getSymbolCount(); I != E; ++I) {
lto_symbol_attributes Attrs = M->getSymbolAttributes(I);
if ((Attrs & LTO_SYMBOL_SCOPE_MASK) == LTO_SYMBOL_SCOPE_INTERNAL)
@@ -337,15 +343,15 @@ void BitcodeFile::parse() {
StringRef SymName = Saver.save(M->getSymbolName(I));
int SymbolDef = Attrs & LTO_SYMBOL_DEFINITION_MASK;
if (SymbolDef == LTO_SYMBOL_DEFINITION_UNDEFINED) {
- SymbolBodies.push_back(new (Alloc) Undefined(SymName));
+ SymbolBodies.push_back(Symtab->addUndefined(SymName, this, false)->body());
} else {
bool Replaceable =
(SymbolDef == LTO_SYMBOL_DEFINITION_TENTATIVE || // common
(Attrs & LTO_SYMBOL_COMDAT) || // comdat
(SymbolDef == LTO_SYMBOL_DEFINITION_WEAK && // weak external
(Attrs & LTO_SYMBOL_ALIAS)));
- SymbolBodies.push_back(new (Alloc) DefinedBitcode(this, SymName,
- Replaceable));
+ SymbolBodies.push_back(
+ Symtab->addBitcode(this, SymName, Replaceable)->body());
}
}
@@ -367,7 +373,26 @@ MachineTypes BitcodeFile::getMachineType() {
}
}
-std::mutex BitcodeFile::Mu;
+// Returns the last element of a path, which is supposed to be a filename.
+static StringRef getBasename(StringRef Path) {
+ size_t Pos = Path.find_last_of("\\/");
+ if (Pos == StringRef::npos)
+ return Path;
+ return Path.substr(Pos + 1);
+}
+
+// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
+std::string toString(InputFile *File) {
+ if (!File)
+ return "(internal)";
+ if (File->ParentName.empty())
+ return File->getName().lower();
+
+ std::string Res =
+ (getBasename(File->ParentName) + "(" + getBasename(File->getName()) + ")")
+ .str();
+ return StringRef(Res).lower();
+}
} // namespace coff
} // namespace lld
diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h
index 0ec01b5075f9..498a1743e985 100644
--- a/COFF/InputFiles.h
+++ b/COFF/InputFiles.h
@@ -12,13 +12,13 @@
#include "lld/Core/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/LTO/legacy/LTOModule.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/StringSaver.h"
#include <memory>
-#include <mutex>
#include <set>
#include <vector>
@@ -31,6 +31,7 @@ using llvm::COFF::MachineTypes;
using llvm::object::Archive;
using llvm::object::COFFObjectFile;
using llvm::object::COFFSymbolRef;
+using llvm::object::coff_import_header;
using llvm::object::coff_section;
class Chunk;
@@ -38,6 +39,8 @@ class Defined;
class DefinedImportData;
class DefinedImportThunk;
class Lazy;
+class SectionChunk;
+struct Symbol;
class SymbolBody;
class Undefined;
@@ -51,67 +54,44 @@ public:
// Returns the filename.
StringRef getName() { return MB.getBufferIdentifier(); }
- // Returns symbols defined by this file.
- virtual std::vector<SymbolBody *> &getSymbols() = 0;
-
// Reads a file (the constructor doesn't do that).
virtual void parse() = 0;
// Returns the CPU type this file was compiled to.
virtual MachineTypes getMachineType() { return IMAGE_FILE_MACHINE_UNKNOWN; }
- // Returns a short, human-friendly filename. If this is a member of
- // an archive file, a returned value includes parent's filename.
- // Used for logging or debugging.
- std::string getShortName();
-
- // Sets a parent filename if this file is created from an archive.
- void setParentName(StringRef N) { ParentName = N; }
+ // An archive file name if this file is created from an archive.
+ StringRef ParentName;
// Returns .drectve section contents if exist.
StringRef getDirectives() { return StringRef(Directives).trim(); }
- // Each file has a unique index. The index number is used to
- // resolve ties in symbol resolution.
- int Index;
- static int NextIndex;
-
protected:
- InputFile(Kind K, MemoryBufferRef M)
- : Index(NextIndex++), MB(M), FileKind(K) {}
+ InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
MemoryBufferRef MB;
std::string Directives;
private:
const Kind FileKind;
- StringRef ParentName;
};
// .lib or .a file.
class ArchiveFile : public InputFile {
public:
- explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
+ explicit ArchiveFile(MemoryBufferRef M);
static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
void parse() override;
- // Returns a memory buffer for a given symbol. An empty memory buffer
- // is returned if we have already returned the same memory buffer.
- // (So that we don't instantiate same members more than once.)
- MemoryBufferRef getMember(const Archive::Symbol *Sym);
-
- llvm::MutableArrayRef<Lazy> getLazySymbols() { return LazySymbols; }
-
- // All symbols returned by ArchiveFiles are of Lazy type.
- std::vector<SymbolBody *> &getSymbols() override {
- llvm_unreachable("internal fatal");
- }
+ // Enqueues an archive member load for the given symbol. If we've already
+ // enqueued a load for the same archive member, this function does nothing,
+ // which ensures that we don't load the same member more than once.
+ void addMember(const Archive::Symbol *Sym);
private:
std::unique_ptr<Archive> File;
std::string Filename;
- std::vector<Lazy> LazySymbols;
- std::map<uint64_t, std::atomic_flag> Seen;
+ llvm::DenseSet<uint64_t> Seen;
};
// .obj or .o file. This may be a member of an archive file.
@@ -122,7 +102,8 @@ public:
void parse() override;
MachineTypes getMachineType() override;
std::vector<Chunk *> &getChunks() { return Chunks; }
- std::vector<SymbolBody *> &getSymbols() override { return SymbolBodies; }
+ std::vector<SectionChunk *> &getDebugChunks() { return DebugChunks; }
+ std::vector<SymbolBody *> &getSymbols() { return SymbolBodies; }
// Returns a SymbolBody object for the SymbolIndex'th symbol in the
// underlying object file.
@@ -146,8 +127,8 @@ private:
void initializeSymbols();
void initializeSEH();
- Defined *createDefined(COFFSymbolRef Sym, const void *Aux, bool IsFirst);
- Undefined *createUndefined(COFFSymbolRef Sym);
+ SymbolBody *createDefined(COFFSymbolRef Sym, const void *Aux, bool IsFirst);
+ SymbolBody *createUndefined(COFFSymbolRef Sym);
std::unique_ptr<COFFObjectFile> COFFObj;
llvm::BumpPtrAllocator Alloc;
@@ -157,6 +138,9 @@ private:
// chunks and non-section chunks for common symbols.
std::vector<Chunk *> Chunks;
+ // CodeView debug info sections.
+ std::vector<SectionChunk *> DebugChunks;
+
// This vector contains the same chunks as Chunks, but they are
// indexed such that you can get a SectionChunk by section index.
// Nonexistent section indices are filled with null pointers.
@@ -182,7 +166,6 @@ public:
explicit ImportFile(MemoryBufferRef M)
: InputFile(ImportKind, M), StringAlloc(StringAllocAux) {}
static bool classof(const InputFile *F) { return F->kind() == ImportKind; }
- std::vector<SymbolBody *> &getSymbols() override { return SymbolBodies; }
DefinedImportData *ImpSym = nullptr;
DefinedImportThunk *ThunkSym = nullptr;
@@ -191,10 +174,14 @@ public:
private:
void parse() override;
- std::vector<SymbolBody *> SymbolBodies;
llvm::BumpPtrAllocator Alloc;
llvm::BumpPtrAllocator StringAllocAux;
llvm::StringSaver StringAlloc;
+
+public:
+ StringRef ExternalName;
+ const coff_import_header *Hdr;
+ Chunk *Location = nullptr;
};
// Used for LTO.
@@ -202,7 +189,7 @@ class BitcodeFile : public InputFile {
public:
explicit BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
- std::vector<SymbolBody *> &getSymbols() override { return SymbolBodies; }
+ std::vector<SymbolBody *> &getSymbols() { return SymbolBodies; }
MachineTypes getMachineType() override;
std::unique_ptr<LTOModule> takeModule() { return std::move(M); }
@@ -214,9 +201,10 @@ private:
std::vector<SymbolBody *> SymbolBodies;
llvm::BumpPtrAllocator Alloc;
std::unique_ptr<LTOModule> M;
- static std::mutex Mu;
};
+std::string toString(InputFile *File);
+
} // namespace coff
} // namespace lld
diff --git a/COFF/Librarian.cpp b/COFF/Librarian.cpp
index 25fb4a87b3eb..4c597fad7345 100644
--- a/COFF/Librarian.cpp
+++ b/COFF/Librarian.cpp
@@ -54,7 +54,7 @@ static uint16_t getImgRelRelocation() {
}
}
-template <class T> void append(std::vector<uint8_t> &B, const T &Data) {
+template <class T> static void append(std::vector<uint8_t> &B, const T &Data) {
size_t S = B.size();
B.resize(S + sizeof(T));
memcpy(&B[S], &Data, sizeof(T));
@@ -352,15 +352,16 @@ ObjectFactory::createNullImportDescriptor(std::vector<uint8_t> &Buffer) {
NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) {
static const uint32_t NumberOfSections = 2;
static const uint32_t NumberOfSymbols = 1;
+ uint32_t VASize = is32bit() ? 4 : 8;
// COFF Header
coff_file_header Header{
u16(Config->Machine), u16(NumberOfSections), u32(0),
u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) +
// .idata$5
- sizeof(export_address_table_entry) +
+ VASize +
// .idata$4
- sizeof(export_address_table_entry)),
+ VASize),
u32(NumberOfSymbols), u16(0),
u16(is32bit() ? IMAGE_FILE_32BIT_MACHINE : 0),
};
@@ -371,36 +372,40 @@ NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) {
{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'},
u32(0),
u32(0),
- u32(sizeof(export_address_table_entry)),
+ u32(VASize),
u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)),
u32(0),
u32(0),
u16(0),
u16(0),
- u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA |
- IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)},
+ u32((is32bit() ? IMAGE_SCN_ALIGN_4BYTES : IMAGE_SCN_ALIGN_8BYTES) |
+ IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |
+ IMAGE_SCN_MEM_WRITE)},
{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'},
u32(0),
u32(0),
- u32(sizeof(export_address_table_entry)),
+ u32(VASize),
u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) +
- sizeof(export_address_table_entry)),
+ VASize),
u32(0),
u32(0),
u16(0),
u16(0),
- u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA |
- IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)},
+ u32((is32bit() ? IMAGE_SCN_ALIGN_4BYTES : IMAGE_SCN_ALIGN_8BYTES) |
+ IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ |
+ IMAGE_SCN_MEM_WRITE)},
};
append(Buffer, SectionTable);
- // .idata$5
- static const export_address_table_entry ILT{u32(0)};
- append(Buffer, ILT);
+ // .idata$5, ILT
+ append(Buffer, u32(0));
+ if (!is32bit())
+ append(Buffer, u32(0));
- // .idata$4
- static const export_address_table_entry IAT{u32(0)};
- append(Buffer, IAT);
+ // .idata$4, IAT
+ append(Buffer, u32(0));
+ if (!is32bit())
+ append(Buffer, u32(0));
// Symbol Table
coff_symbol16 SymbolTable[NumberOfSymbols] = {
@@ -458,7 +463,7 @@ void lld::coff::writeImportLibrary() {
std::vector<NewArchiveMember> Members;
std::string Path = getImplibPath();
- std::string DLLName = llvm::sys::path::filename(Config->OutputFile);
+ std::string DLLName = sys::path::filename(Config->OutputFile);
ObjectFactory OF(DLLName);
std::vector<uint8_t> ImportDescriptor;
diff --git a/COFF/MarkLive.cpp b/COFF/MarkLive.cpp
index 0870986ad81a..0156d238b672 100644
--- a/COFF/MarkLive.cpp
+++ b/COFF/MarkLive.cpp
@@ -38,8 +38,8 @@ void markLive(const std::vector<Chunk *> &Chunks) {
};
// Add GC root chunks.
- for (Undefined *U : Config->GCRoot)
- if (auto *D = dyn_cast<DefinedRegular>(U->repl()))
+ for (SymbolBody *B : Config->GCRoot)
+ if (auto *D = dyn_cast<DefinedRegular>(B))
Enqueue(D->getChunk());
while (!Worklist.empty()) {
@@ -48,7 +48,7 @@ void markLive(const std::vector<Chunk *> &Chunks) {
// Mark all symbols listed in the relocation table for this section.
for (SymbolBody *S : SC->symbols())
- if (auto *D = dyn_cast<DefinedRegular>(S->repl()))
+ if (auto *D = dyn_cast<DefinedRegular>(S))
Enqueue(D->getChunk());
// Mark associative sections if any.
diff --git a/COFF/Memory.h b/COFF/Memory.h
new file mode 100644
index 000000000000..526f11344a09
--- /dev/null
+++ b/COFF/Memory.h
@@ -0,0 +1,52 @@
+//===- Memory.h -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// See ELF/Memory.h
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_COFF_MEMORY_H
+#define LLD_COFF_MEMORY_H
+
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/StringSaver.h"
+#include <vector>
+
+namespace lld {
+namespace coff {
+
+extern llvm::BumpPtrAllocator BAlloc;
+extern llvm::StringSaver Saver;
+
+struct SpecificAllocBase {
+ SpecificAllocBase() { Instances.push_back(this); }
+ virtual ~SpecificAllocBase() = default;
+ virtual void reset() = 0;
+ static std::vector<SpecificAllocBase *> Instances;
+};
+
+template <class T> struct SpecificAlloc : public SpecificAllocBase {
+ void reset() override { Alloc.DestroyAll(); }
+ llvm::SpecificBumpPtrAllocator<T> Alloc;
+};
+
+template <typename T, typename... U> T *make(U &&... Args) {
+ static SpecificAlloc<T> Alloc;
+ return new (Alloc.Alloc.Allocate()) T(std::forward<U>(Args)...);
+}
+
+inline void freeArena() {
+ for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances)
+ Alloc->reset();
+ BAlloc.Reset();
+}
+}
+}
+
+#endif
diff --git a/COFF/ModuleDef.cpp b/COFF/ModuleDef.cpp
index 5e393f45d184..a273b6f535db 100644
--- a/COFF/ModuleDef.cpp
+++ b/COFF/ModuleDef.cpp
@@ -18,6 +18,7 @@
#include "Config.h"
#include "Error.h"
+#include "Memory.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/StringSaver.h"
@@ -113,7 +114,7 @@ private:
class Parser {
public:
- explicit Parser(StringRef S, StringSaver *A) : Lex(S), Alloc(A) {}
+ explicit Parser(StringRef S) : Lex(S) {}
void parse() {
do {
@@ -197,9 +198,9 @@ private:
if (Config->Machine == I386) {
if (!isDecorated(E.Name))
- E.Name = Alloc->save("_" + E.Name);
+ E.Name = Saver.save("_" + E.Name);
if (!E.ExtName.empty() && !isDecorated(E.ExtName))
- E.ExtName = Alloc->save("_" + E.ExtName);
+ E.ExtName = Saver.save("_" + E.ExtName);
}
for (;;) {
@@ -278,14 +279,11 @@ private:
Lexer Lex;
Token Tok;
std::vector<Token> Stack;
- StringSaver *Alloc;
};
} // anonymous namespace
-void parseModuleDefs(MemoryBufferRef MB, StringSaver *Alloc) {
- Parser(MB.getBuffer(), Alloc).parse();
-}
+void parseModuleDefs(MemoryBufferRef MB) { Parser(MB.getBuffer()).parse(); }
} // namespace coff
} // namespace lld
diff --git a/COFF/Options.td b/COFF/Options.td
index e5c9c5b4635b..9dfbcc8e188c 100644
--- a/COFF/Options.td
+++ b/COFF/Options.td
@@ -27,6 +27,7 @@ def failifmismatch : P<"failifmismatch", "">;
def heap : P<"heap", "Size of the heap">;
def implib : P<"implib", "Import library name">;
def libpath : P<"libpath", "Additional library search path">;
+def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">;
def machine : P<"machine", "Specify target platform">;
def merge : P<"merge", "Combine sections">;
def mllvm : P<"mllvm", "Options to pass to LLVM">;
@@ -61,7 +62,9 @@ def deffile : Joined<["/", "-"], "def:">,
HelpText<"Use module-definition file">;
def debug : F<"debug">, HelpText<"Embed a symbol table in the image">;
+def debugtype : P<"debugtype", "Debug Info Options">;
def dll : F<"dll">, HelpText<"Create a DLL">;
+def driver : P<"driver", "Generate a Windows NT Kernel Mode Driver">;
def nodefaultlib_all : F<"nodefaultlib">;
def noentry : F<"noentry">;
def profile : F<"profile">;
@@ -91,7 +94,10 @@ def help_q : Flag<["/?", "-?"], "">, Alias<help>;
def nosymtab : F<"nosymtab">;
// Flags for debugging
-def lldmap : Joined<["/", "-"], "lldmap:">;
+def debugpdb : F<"debugpdb">;
+def dumppdb : Joined<["/", "-"], "dumppdb">;
+def lldmap : F<"lldmap">;
+def lldmap_file : Joined<["/", "-"], "lldmap:">;
//==============================================================================
// The flags below do nothing. They are defined only for link.exe compatibility.
diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp
index 7606ccc680d3..56d5a3651143 100644
--- a/COFF/PDB.cpp
+++ b/COFF/PDB.cpp
@@ -7,55 +7,187 @@
//
//===----------------------------------------------------------------------===//
-#include "Driver.h"
+#include "PDB.h"
+#include "Chunks.h"
+#include "Config.h"
#include "Error.h"
+#include "SymbolTable.h"
#include "Symbols.h"
+#include "llvm/DebugInfo/CodeView/SymbolDumper.h"
+#include "llvm/DebugInfo/CodeView/TypeDumper.h"
+#include "llvm/DebugInfo/MSF/ByteStream.h"
+#include "llvm/DebugInfo/MSF/MSFBuilder.h"
+#include "llvm/DebugInfo/MSF/MSFCommon.h"
+#include "llvm/DebugInfo/PDB/Raw/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Raw/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Raw/PDBFileBuilder.h"
+#include "llvm/DebugInfo/PDB/Raw/TpiStream.h"
+#include "llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h"
+#include "llvm/Object/COFF.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/ScopedPrinter.h"
#include <memory>
+using namespace lld;
+using namespace lld::coff;
using namespace llvm;
+using namespace llvm::codeview;
using namespace llvm::support;
using namespace llvm::support::endian;
-const int PageSize = 4096;
-const uint8_t Magic[32] = "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0";
-
-namespace {
-struct PDBHeader {
- uint8_t Magic[32];
- ulittle32_t PageSize;
- ulittle32_t FpmPage;
- ulittle32_t PageCount;
- ulittle32_t RootSize;
- ulittle32_t Reserved;
- ulittle32_t RootPointer;
-};
-}
-
-void lld::coff::createPDB(StringRef Path) {
- // Create a file.
- size_t FileSize = PageSize * 3;
- ErrorOr<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
- FileOutputBuffer::create(Path, FileSize);
- if (auto EC = BufferOrErr.getError())
- fatal(EC, "failed to open " + Path);
- std::unique_ptr<FileOutputBuffer> Buffer = std::move(*BufferOrErr);
-
- // Write the file header.
- uint8_t *Buf = Buffer->getBufferStart();
- auto *Hdr = reinterpret_cast<PDBHeader *>(Buf);
- memcpy(Hdr->Magic, Magic, sizeof(Magic));
- Hdr->PageSize = PageSize;
- // I don't know what FpmPage field means, but it must not be 0.
- Hdr->FpmPage = 1;
- Hdr->PageCount = FileSize / PageSize;
- // Root directory is empty, containing only the length field.
- Hdr->RootSize = 4;
- // Root directory is on page 1.
- Hdr->RootPointer = 1;
-
- // Write the root directory. Root stream is on page 2.
- write32le(Buf + PageSize, 2);
- Buffer->commit();
+using llvm::object::coff_section;
+
+static ExitOnError ExitOnErr;
+
+// Returns a list of all SectionChunks.
+static std::vector<coff_section> getInputSections(SymbolTable *Symtab) {
+ std::vector<coff_section> V;
+ for (Chunk *C : Symtab->getChunks())
+ if (auto *SC = dyn_cast<SectionChunk>(C))
+ V.push_back(*SC->Header);
+ return V;
+}
+
+static SectionChunk *findByName(std::vector<SectionChunk *> &Sections,
+ StringRef Name) {
+ for (SectionChunk *C : Sections)
+ if (C->getSectionName() == Name)
+ return C;
+ return nullptr;
+}
+
+static ArrayRef<uint8_t> getDebugT(ObjectFile *File) {
+ SectionChunk *Sec = findByName(File->getDebugChunks(), ".debug$T");
+ if (!Sec)
+ return {};
+
+ // First 4 bytes are section magic.
+ ArrayRef<uint8_t> Data = Sec->getContents();
+ if (Data.size() < 4)
+ fatal(".debug$T too short");
+ if (read32le(Data.data()) != COFF::DEBUG_SECTION_MAGIC)
+ fatal(".debug$T has an invalid magic");
+ return Data.slice(4);
+}
+
+static void dumpDebugT(ScopedPrinter &W, ObjectFile *File) {
+ ArrayRef<uint8_t> Data = getDebugT(File);
+ if (Data.empty())
+ return;
+
+ msf::ByteStream Stream(Data);
+ CVTypeDumper TypeDumper(&W, false);
+ if (auto EC = TypeDumper.dump(Data))
+ fatal(EC, "CVTypeDumper::dump failed");
+}
+
+static void dumpDebugS(ScopedPrinter &W, ObjectFile *File) {
+ SectionChunk *Sec = findByName(File->getDebugChunks(), ".debug$S");
+ if (!Sec)
+ return;
+
+ msf::ByteStream Stream(Sec->getContents());
+ CVSymbolArray Symbols;
+ msf::StreamReader Reader(Stream);
+ if (auto EC = Reader.readArray(Symbols, Reader.getLength()))
+ fatal(EC, "StreamReader.readArray<CVSymbolArray> failed");
+
+ CVTypeDumper TypeDumper(&W, false);
+ CVSymbolDumper SymbolDumper(W, TypeDumper, nullptr, false);
+ if (auto EC = SymbolDumper.dump(Symbols))
+ fatal(EC, "CVSymbolDumper::dump failed");
+}
+
+// Dump CodeView debug info. This is for debugging.
+static void dumpCodeView(SymbolTable *Symtab) {
+ ScopedPrinter W(outs());
+
+ for (ObjectFile *File : Symtab->ObjectFiles) {
+ dumpDebugT(W, File);
+ dumpDebugS(W, File);
+ }
+}
+
+static void addTypeInfo(SymbolTable *Symtab,
+ pdb::TpiStreamBuilder &TpiBuilder) {
+ for (ObjectFile *File : Symtab->ObjectFiles) {
+ ArrayRef<uint8_t> Data = getDebugT(File);
+ if (Data.empty())
+ continue;
+
+ msf::ByteStream Stream(Data);
+ codeview::CVTypeArray Records;
+ msf::StreamReader Reader(Stream);
+ if (auto EC = Reader.readArray(Records, Reader.getLength()))
+ fatal(EC, "Reader.readArray failed");
+ for (const codeview::CVType &Rec : Records)
+ TpiBuilder.addTypeRecord(Rec);
+ }
+}
+
+// Creates a PDB file.
+void coff::createPDB(StringRef Path, SymbolTable *Symtab,
+ ArrayRef<uint8_t> SectionTable) {
+ if (Config->DumpPdb)
+ dumpCodeView(Symtab);
+
+ BumpPtrAllocator Alloc;
+ pdb::PDBFileBuilder Builder(Alloc);
+ ExitOnErr(Builder.initialize(4096)); // 4096 is blocksize
+
+ // Create streams in MSF for predefined streams, namely
+ // PDB, TPI, DBI and IPI.
+ for (int I = 0; I < (int)pdb::kSpecialStreamCount; ++I)
+ ExitOnErr(Builder.getMsfBuilder().addStream(0));
+
+ // Add an Info stream.
+ auto &InfoBuilder = Builder.getInfoBuilder();
+ InfoBuilder.setAge(1);
+
+ // Should be a random number, 0 for now.
+ InfoBuilder.setGuid({});
+
+ // Should be the current time, but set 0 for reproducibilty.
+ InfoBuilder.setSignature(0);
+ InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70);
+
+ // Add an empty DPI stream.
+ auto &DbiBuilder = Builder.getDbiBuilder();
+ DbiBuilder.setVersionHeader(pdb::PdbDbiV110);
+
+ // Add an empty TPI stream.
+ auto &TpiBuilder = Builder.getTpiBuilder();
+ TpiBuilder.setVersionHeader(pdb::PdbTpiV80);
+ if (Config->DebugPdb)
+ addTypeInfo(Symtab, TpiBuilder);
+
+ // Add an empty IPI stream.
+ auto &IpiBuilder = Builder.getIpiBuilder();
+ IpiBuilder.setVersionHeader(pdb::PdbTpiV80);
+
+ // Add Section Contributions.
+ std::vector<pdb::SectionContrib> Contribs =
+ pdb::DbiStreamBuilder::createSectionContribs(getInputSections(Symtab));
+ DbiBuilder.setSectionContribs(Contribs);
+
+ // Add Section Map stream.
+ ArrayRef<object::coff_section> Sections = {
+ (const object::coff_section *)SectionTable.data(),
+ SectionTable.size() / sizeof(object::coff_section)};
+ std::vector<pdb::SecMapEntry> SectionMap =
+ pdb::DbiStreamBuilder::createSectionMap(Sections);
+ DbiBuilder.setSectionMap(SectionMap);
+
+ ExitOnErr(DbiBuilder.addModuleInfo("", "* Linker *"));
+
+ // Add COFF section header stream.
+ ExitOnErr(
+ DbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, SectionTable));
+
+ // Write to a file.
+ ExitOnErr(Builder.commit(Path));
}
diff --git a/COFF/PDB.h b/COFF/PDB.h
new file mode 100644
index 000000000000..091e90fa1ef1
--- /dev/null
+++ b/COFF/PDB.h
@@ -0,0 +1,25 @@
+//===- PDB.h ----------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_COFF_PDB_H
+#define LLD_COFF_PDB_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace lld {
+namespace coff {
+class SymbolTable;
+
+void createPDB(llvm::StringRef Path, SymbolTable *Symtab,
+ llvm::ArrayRef<uint8_t> SectionTable);
+}
+}
+
+#endif
diff --git a/COFF/Strings.cpp b/COFF/Strings.cpp
new file mode 100644
index 000000000000..d0558413f673
--- /dev/null
+++ b/COFF/Strings.cpp
@@ -0,0 +1,30 @@
+//===- Strings.cpp -------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Strings.h"
+
+#if defined(_MSC_VER)
+#include <Windows.h>
+#include <DbgHelp.h>
+#pragma comment(lib, "dbghelp.lib")
+#endif
+
+using namespace lld;
+using namespace lld::coff;
+using namespace llvm;
+
+Optional<std::string> coff::demangle(StringRef S) {
+#if defined(_MSC_VER)
+ char Buf[4096];
+ if (S.startswith("?"))
+ if (size_t Len = UnDecorateSymbolName(S.str().c_str(), Buf, sizeof(Buf), 0))
+ return std::string(Buf, Len);
+#endif
+ return None;
+}
diff --git a/COFF/Strings.h b/COFF/Strings.h
new file mode 100644
index 000000000000..1f85f3e2da5c
--- /dev/null
+++ b/COFF/Strings.h
@@ -0,0 +1,23 @@
+//===- Strings.h ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_COFF_STRINGS_H
+#define LLD_COFF_STRINGS_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace lld {
+namespace coff {
+llvm::Optional<std::string> demangle(llvm::StringRef S);
+}
+}
+
+#endif
diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp
index df9da4c36650..9cc0b75c1510 100644
--- a/COFF/SymbolTable.cpp
+++ b/COFF/SymbolTable.cpp
@@ -7,12 +7,12 @@
//
//===----------------------------------------------------------------------===//
+#include "SymbolTable.h"
#include "Config.h"
#include "Driver.h"
#include "Error.h"
-#include "SymbolTable.h"
+#include "Memory.h"
#include "Symbols.h"
-#include "lld/Core/Parallel.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/LTO/legacy/LTOCodeGenerator.h"
#include "llvm/Support/Debug.h"
@@ -24,222 +24,265 @@ using namespace llvm;
namespace lld {
namespace coff {
-void SymbolTable::addFile(std::unique_ptr<InputFile> FileP) {
-#if LLVM_ENABLE_THREADS
- std::launch Policy = std::launch::async;
-#else
- std::launch Policy = std::launch::deferred;
-#endif
+SymbolTable *Symtab;
- InputFile *File = FileP.get();
- Files.push_back(std::move(FileP));
- if (auto *F = dyn_cast<ArchiveFile>(File)) {
- ArchiveQueue.push_back(
- std::async(Policy, [=]() { F->parse(); return F; }));
- return;
+void SymbolTable::addFile(InputFile *File) {
+ if (Config->Verbose)
+ outs() << "Reading " << toString(File) << "\n";
+ File->parse();
+
+ MachineTypes MT = File->getMachineType();
+ if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
+ Config->Machine = MT;
+ } else if (MT != IMAGE_FILE_MACHINE_UNKNOWN && Config->Machine != MT) {
+ fatal(toString(File) + ": machine type " + machineToStr(MT) +
+ " conflicts with " + machineToStr(Config->Machine));
}
- ObjectQueue.push_back(
- std::async(Policy, [=]() { File->parse(); return File; }));
+
if (auto *F = dyn_cast<ObjectFile>(File)) {
ObjectFiles.push_back(F);
} else if (auto *F = dyn_cast<BitcodeFile>(File)) {
BitcodeFiles.push_back(F);
- } else {
- ImportFiles.push_back(cast<ImportFile>(File));
+ } else if (auto *F = dyn_cast<ImportFile>(File)) {
+ ImportFiles.push_back(F);
}
-}
-void SymbolTable::step() {
- if (queueEmpty())
+ StringRef S = File->getDirectives();
+ if (S.empty())
return;
- readObjects();
- readArchives();
-}
-void SymbolTable::run() {
- while (!queueEmpty())
- step();
-}
-
-void SymbolTable::readArchives() {
- if (ArchiveQueue.empty())
- return;
-
- // Add lazy symbols to the symbol table. Lazy symbols that conflict
- // with existing undefined symbols are accumulated in LazySyms.
- std::vector<Symbol *> LazySyms;
- for (std::future<ArchiveFile *> &Future : ArchiveQueue) {
- ArchiveFile *File = Future.get();
- if (Config->Verbose)
- llvm::outs() << "Reading " << File->getShortName() << "\n";
- for (Lazy &Sym : File->getLazySymbols())
- addLazy(&Sym, &LazySyms);
- }
- ArchiveQueue.clear();
-
- // Add archive member files to ObjectQueue that should resolve
- // existing undefined symbols.
- for (Symbol *Sym : LazySyms)
- addMemberFile(cast<Lazy>(Sym->Body));
-}
-
-void SymbolTable::readObjects() {
- if (ObjectQueue.empty())
- return;
-
- // Add defined and undefined symbols to the symbol table.
- std::vector<StringRef> Directives;
- for (size_t I = 0; I < ObjectQueue.size(); ++I) {
- InputFile *File = ObjectQueue[I].get();
- if (Config->Verbose)
- llvm::outs() << "Reading " << File->getShortName() << "\n";
- // Adding symbols may add more files to ObjectQueue
- // (but not to ArchiveQueue).
- for (SymbolBody *Sym : File->getSymbols())
- if (Sym->isExternal())
- addSymbol(Sym);
- StringRef S = File->getDirectives();
- if (!S.empty()) {
- Directives.push_back(S);
- if (Config->Verbose)
- llvm::outs() << "Directives: " << File->getShortName()
- << ": " << S << "\n";
- }
- }
- ObjectQueue.clear();
-
- // Parse directive sections. This may add files to
- // ArchiveQueue and ObjectQueue.
- for (StringRef S : Directives)
- Driver->parseDirectives(S);
-}
-
-bool SymbolTable::queueEmpty() {
- return ArchiveQueue.empty() && ObjectQueue.empty();
+ if (Config->Verbose)
+ outs() << "Directives: " << toString(File) << ": " << S << "\n";
+ Driver->parseDirectives(S);
}
-void SymbolTable::reportRemainingUndefines(bool Resolve) {
- llvm::SmallPtrSet<SymbolBody *, 8> Undefs;
+void SymbolTable::reportRemainingUndefines() {
+ SmallPtrSet<SymbolBody *, 8> Undefs;
for (auto &I : Symtab) {
Symbol *Sym = I.second;
- auto *Undef = dyn_cast<Undefined>(Sym->Body);
+ auto *Undef = dyn_cast<Undefined>(Sym->body());
if (!Undef)
continue;
+ if (!Sym->IsUsedInRegularObj)
+ continue;
StringRef Name = Undef->getName();
// A weak alias may have been resolved, so check for that.
if (Defined *D = Undef->getWeakAlias()) {
- if (Resolve)
- Sym->Body = D;
+ // We resolve weak aliases by replacing the alias's SymbolBody with the
+ // target's SymbolBody. This causes all SymbolBody pointers referring to
+ // the old symbol to instead refer to the new symbol. However, we can't
+ // just blindly copy sizeof(Symbol::Body) bytes from D to Sym->Body
+ // because D may be an internal symbol, and internal symbols are stored as
+ // "unparented" SymbolBodies. For that reason we need to check which type
+ // of symbol we are dealing with and copy the correct number of bytes.
+ if (isa<DefinedRegular>(D))
+ memcpy(Sym->Body.buffer, D, sizeof(DefinedRegular));
+ else if (isa<DefinedAbsolute>(D))
+ memcpy(Sym->Body.buffer, D, sizeof(DefinedAbsolute));
+ else
+ // No other internal symbols are possible.
+ Sym->Body = D->symbol()->Body;
continue;
}
// If we can resolve a symbol by removing __imp_ prefix, do that.
// This odd rule is for compatibility with MSVC linker.
if (Name.startswith("__imp_")) {
Symbol *Imp = find(Name.substr(strlen("__imp_")));
- if (Imp && isa<Defined>(Imp->Body)) {
- if (!Resolve)
- continue;
- auto *D = cast<Defined>(Imp->Body);
- auto *S = new (Alloc) DefinedLocalImport(Name, D);
- LocalImportChunks.push_back(S->getChunk());
- Sym->Body = S;
+ if (Imp && isa<Defined>(Imp->body())) {
+ auto *D = cast<Defined>(Imp->body());
+ replaceBody<DefinedLocalImport>(Sym, Name, D);
+ LocalImportChunks.push_back(
+ cast<DefinedLocalImport>(Sym->body())->getChunk());
continue;
}
}
// Remaining undefined symbols are not fatal if /force is specified.
// They are replaced with dummy defined symbols.
- if (Config->Force && Resolve)
- Sym->Body = new (Alloc) DefinedAbsolute(Name, 0);
- Undefs.insert(Sym->Body);
+ if (Config->Force)
+ replaceBody<DefinedAbsolute>(Sym, Name, 0);
+ Undefs.insert(Sym->body());
}
if (Undefs.empty())
return;
- for (Undefined *U : Config->GCRoot)
- if (Undefs.count(U->repl()))
- llvm::errs() << "<root>: undefined symbol: " << U->getName() << "\n";
- for (std::unique_ptr<InputFile> &File : Files)
- if (!isa<ArchiveFile>(File.get()))
- for (SymbolBody *Sym : File->getSymbols())
- if (Undefs.count(Sym->repl()))
- llvm::errs() << File->getShortName() << ": undefined symbol: "
- << Sym->getName() << "\n";
+ for (SymbolBody *B : Config->GCRoot)
+ if (Undefs.count(B))
+ errs() << "<root>: undefined symbol: " << B->getName() << "\n";
+ for (ObjectFile *File : ObjectFiles)
+ for (SymbolBody *Sym : File->getSymbols())
+ if (Undefs.count(Sym))
+ errs() << toString(File) << ": undefined symbol: " << Sym->getName()
+ << "\n";
if (!Config->Force)
fatal("link failed");
}
-void SymbolTable::addLazy(Lazy *New, std::vector<Symbol *> *Accum) {
- Symbol *Sym = insert(New);
- if (Sym->Body == New)
- return;
- SymbolBody *Existing = Sym->Body;
- if (isa<Defined>(Existing))
- return;
- if (Lazy *L = dyn_cast<Lazy>(Existing))
- if (L->getFileIndex() < New->getFileIndex())
- return;
- Sym->Body = New;
- New->setBackref(Sym);
- if (isa<Undefined>(Existing))
- Accum->push_back(Sym);
+std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
+ Symbol *&Sym = Symtab[CachedHashStringRef(Name)];
+ if (Sym)
+ return {Sym, false};
+ Sym = make<Symbol>();
+ Sym->IsUsedInRegularObj = false;
+ Sym->PendingArchiveLoad = false;
+ return {Sym, true};
}
-void SymbolTable::addSymbol(SymbolBody *New) {
- // Find an existing symbol or create and insert a new one.
- assert(isa<Defined>(New) || isa<Undefined>(New));
- Symbol *Sym = insert(New);
- if (Sym->Body == New)
- return;
- SymbolBody *Existing = Sym->Body;
-
- // If we have an undefined symbol and a lazy symbol,
- // let the lazy symbol to read a member file.
- if (auto *L = dyn_cast<Lazy>(Existing)) {
- // Undefined symbols with weak aliases need not to be resolved,
- // since they would be replaced with weak aliases if they remain
- // undefined.
- if (auto *U = dyn_cast<Undefined>(New)) {
- if (!U->WeakAlias) {
- addMemberFile(L);
- return;
- }
+Symbol *SymbolTable::addUndefined(StringRef Name, InputFile *F,
+ bool IsWeakAlias) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ if (!F || !isa<BitcodeFile>(F))
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || (isa<Lazy>(S->body()) && IsWeakAlias)) {
+ replaceBody<Undefined>(S, Name);
+ return S;
+ }
+ if (auto *L = dyn_cast<Lazy>(S->body())) {
+ if (!S->PendingArchiveLoad) {
+ S->PendingArchiveLoad = true;
+ L->File->addMember(&L->Sym);
}
- Sym->Body = New;
+ }
+ return S;
+}
+
+void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) {
+ StringRef Name = Sym.getName();
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ if (WasInserted) {
+ replaceBody<Lazy>(S, F, Sym);
return;
}
+ auto *U = dyn_cast<Undefined>(S->body());
+ if (!U || U->WeakAlias || S->PendingArchiveLoad)
+ return;
+ S->PendingArchiveLoad = true;
+ F->addMember(&Sym);
+}
+
+void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) {
+ fatal("duplicate symbol: " + toString(*Existing->body()) + " in " +
+ toString(Existing->body()->getFile()) + " and in " +
+ (NewFile ? toString(NewFile) : "(internal)"));
+}
- // compare() returns -1, 0, or 1 if the lhs symbol is less preferable,
- // equivalent (conflicting), or more preferable, respectively.
- int Comp = Existing->compare(New);
- if (Comp == 0)
- fatal("duplicate symbol: " + Existing->getDebugName() + " and " +
- New->getDebugName());
- if (Comp < 0)
- Sym->Body = New;
+Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(N);
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
+ replaceBody<DefinedAbsolute>(S, N, Sym);
+ else if (!isa<DefinedCOFF>(S->body()))
+ reportDuplicate(S, nullptr);
+ return S;
}
-Symbol *SymbolTable::insert(SymbolBody *New) {
- Symbol *&Sym = Symtab[New->getName()];
- if (Sym) {
- New->setBackref(Sym);
- return Sym;
+Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(N);
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
+ replaceBody<DefinedAbsolute>(S, N, VA);
+ else if (!isa<DefinedCOFF>(S->body()))
+ reportDuplicate(S, nullptr);
+ return S;
+}
+
+Symbol *SymbolTable::addRelative(StringRef N, uint64_t VA) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(N);
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
+ replaceBody<DefinedRelative>(S, N, VA);
+ else if (!isa<DefinedCOFF>(S->body()))
+ reportDuplicate(S, nullptr);
+ return S;
+}
+
+Symbol *SymbolTable::addRegular(ObjectFile *F, COFFSymbolRef Sym,
+ SectionChunk *C) {
+ StringRef Name;
+ F->getCOFFObj()->getSymbolName(Sym, Name);
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
+ replaceBody<DefinedRegular>(S, F, Sym, C);
+ else if (auto *R = dyn_cast<DefinedRegular>(S->body())) {
+ if (!C->isCOMDAT() || !R->isCOMDAT())
+ reportDuplicate(S, F);
+ } else if (auto *B = dyn_cast<DefinedBitcode>(S->body())) {
+ if (B->IsReplaceable)
+ replaceBody<DefinedRegular>(S, F, Sym, C);
+ else if (!C->isCOMDAT())
+ reportDuplicate(S, F);
+ } else
+ replaceBody<DefinedRegular>(S, F, Sym, C);
+ return S;
+}
+
+Symbol *SymbolTable::addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(N);
+ if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body())) {
+ replaceBody<DefinedBitcode>(S, F, N, IsReplaceable);
+ return S;
}
- Sym = new (Alloc) Symbol(New);
- New->setBackref(Sym);
- return Sym;
+ if (isa<DefinedCommon>(S->body()))
+ return S;
+ if (IsReplaceable)
+ if (isa<DefinedRegular>(S->body()) || isa<DefinedBitcode>(S->body()))
+ return S;
+ reportDuplicate(S, F);
+ return S;
}
-// Reads an archive member file pointed by a given symbol.
-void SymbolTable::addMemberFile(Lazy *Body) {
- std::unique_ptr<InputFile> File = Body->getMember();
+Symbol *SymbolTable::addCommon(ObjectFile *F, COFFSymbolRef Sym,
+ CommonChunk *C) {
+ StringRef Name;
+ F->getCOFFObj()->getSymbolName(Sym, Name);
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || !isa<DefinedCOFF>(S->body()))
+ replaceBody<DefinedCommon>(S, F, Sym, C);
+ else if (auto *DC = dyn_cast<DefinedCommon>(S->body()))
+ if (Sym.getValue() > DC->getSize())
+ replaceBody<DefinedCommon>(S, F, Sym, C);
+ return S;
+}
- // getMember returns an empty buffer if the member was already
- // read from the library.
- if (!File)
- return;
- if (Config->Verbose)
- llvm::outs() << "Loaded " << File->getShortName() << " for "
- << Body->getName() << "\n";
- addFile(std::move(File));
+Symbol *SymbolTable::addImportData(StringRef N, ImportFile *F) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(N);
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
+ replaceBody<DefinedImportData>(S, N, F);
+ else if (!isa<DefinedCOFF>(S->body()))
+ reportDuplicate(S, nullptr);
+ return S;
+}
+
+Symbol *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID,
+ uint16_t Machine) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
+ replaceBody<DefinedImportThunk>(S, Name, ID, Machine);
+ else if (!isa<DefinedCOFF>(S->body()))
+ reportDuplicate(S, nullptr);
+ return S;
}
std::vector<Chunk *> SymbolTable::getChunks() {
@@ -252,7 +295,7 @@ std::vector<Chunk *> SymbolTable::getChunks() {
}
Symbol *SymbolTable::find(StringRef Name) {
- auto It = Symtab.find(Name);
+ auto It = Symtab.find(CachedHashStringRef(Name));
if (It == Symtab.end())
return nullptr;
return It->second;
@@ -266,7 +309,7 @@ Symbol *SymbolTable::findUnderscore(StringRef Name) {
StringRef SymbolTable::findByPrefix(StringRef Prefix) {
for (auto Pair : Symtab) {
- StringRef Name = Pair.first;
+ StringRef Name = Pair.first.val();
if (Name.startswith(Prefix))
return Name;
}
@@ -275,7 +318,7 @@ StringRef SymbolTable::findByPrefix(StringRef Prefix) {
StringRef SymbolTable::findMangle(StringRef Name) {
if (Symbol *Sym = find(Name))
- if (!isa<Undefined>(Sym->Body))
+ if (!isa<Undefined>(Sym->body()))
return Name;
if (Config->Machine != I386)
return findByPrefix(("?" + Name + "@@Y").str());
@@ -289,39 +332,22 @@ StringRef SymbolTable::findMangle(StringRef Name) {
return findByPrefix(("?" + Name.substr(1) + "@@Y").str());
}
-void SymbolTable::mangleMaybe(Undefined *U) {
- if (U->WeakAlias)
- return;
- if (!isa<Undefined>(U->repl()))
+void SymbolTable::mangleMaybe(SymbolBody *B) {
+ auto *U = dyn_cast<Undefined>(B);
+ if (!U || U->WeakAlias)
return;
StringRef Alias = findMangle(U->getName());
if (!Alias.empty())
U->WeakAlias = addUndefined(Alias);
}
-Undefined *SymbolTable::addUndefined(StringRef Name) {
- auto *New = new (Alloc) Undefined(Name);
- addSymbol(New);
- if (auto *U = dyn_cast<Undefined>(New->repl()))
- return U;
- return New;
-}
-
-DefinedRelative *SymbolTable::addRelative(StringRef Name, uint64_t VA) {
- auto *New = new (Alloc) DefinedRelative(Name, VA);
- addSymbol(New);
- return New;
-}
-
-DefinedAbsolute *SymbolTable::addAbsolute(StringRef Name, uint64_t VA) {
- auto *New = new (Alloc) DefinedAbsolute(Name, VA);
- addSymbol(New);
- return New;
+SymbolBody *SymbolTable::addUndefined(StringRef Name) {
+ return addUndefined(Name, nullptr, false)->body();
}
void SymbolTable::printMap(llvm::raw_ostream &OS) {
for (ObjectFile *File : ObjectFiles) {
- OS << File->getShortName() << ":\n";
+ OS << toString(File) << ":\n";
for (SymbolBody *Body : File->getSymbols())
if (auto *R = dyn_cast<DefinedRegular>(Body))
if (R->getChunk()->isLive())
@@ -330,84 +356,32 @@ void SymbolTable::printMap(llvm::raw_ostream &OS) {
}
}
-void SymbolTable::addCombinedLTOObject(ObjectFile *Obj) {
- for (SymbolBody *Body : Obj->getSymbols()) {
- if (!Body->isExternal())
- continue;
- // We should not see any new undefined symbols at this point, but we'll
- // diagnose them later in reportRemainingUndefines().
- StringRef Name = Body->getName();
- Symbol *Sym = insert(Body);
- SymbolBody *Existing = Sym->Body;
-
- if (Existing == Body)
- continue;
-
- if (isa<DefinedBitcode>(Existing)) {
- Sym->Body = Body;
- continue;
- }
- if (auto *L = dyn_cast<Lazy>(Existing)) {
- // We may see new references to runtime library symbols such as __chkstk
- // here. These symbols must be wholly defined in non-bitcode files.
- addMemberFile(L);
- continue;
- }
-
- int Comp = Existing->compare(Body);
- if (Comp == 0)
- fatal("LTO: unexpected duplicate symbol: " + Name);
- if (Comp < 0)
- Sym->Body = Body;
- }
-}
-
void SymbolTable::addCombinedLTOObjects() {
if (BitcodeFiles.empty())
return;
- // Diagnose any undefined symbols early, but do not resolve weak externals,
- // as resolution breaks the invariant that each Symbol points to a unique
- // SymbolBody, which we rely on to replace DefinedBitcode symbols correctly.
- reportRemainingUndefines(/*Resolve=*/false);
-
// Create an object file and add it to the symbol table by replacing any
// DefinedBitcode symbols with the definitions in the object file.
LTOCodeGenerator CG(BitcodeFile::Context);
CG.setOptLevel(Config->LTOOptLevel);
- std::vector<ObjectFile *> Objs = createLTOObjects(&CG);
-
- for (ObjectFile *Obj : Objs)
- addCombinedLTOObject(Obj);
-
- size_t NumBitcodeFiles = BitcodeFiles.size();
- run();
- if (BitcodeFiles.size() != NumBitcodeFiles)
- fatal("LTO: late loaded symbol created new bitcode reference");
+ for (ObjectFile *Obj : createLTOObjects(&CG))
+ Obj->parse();
}
// Combine and compile bitcode files and then return the result
// as a vector of regular COFF object files.
std::vector<ObjectFile *> SymbolTable::createLTOObjects(LTOCodeGenerator *CG) {
- // All symbols referenced by non-bitcode objects must be preserved.
- for (ObjectFile *File : ObjectFiles)
- for (SymbolBody *Body : File->getSymbols())
- if (auto *S = dyn_cast<DefinedBitcode>(Body->repl()))
- CG->addMustPreserveSymbol(S->getName());
-
- // Likewise for bitcode symbols which we initially resolved to non-bitcode.
+ // All symbols referenced by non-bitcode objects, including GC roots, must be
+ // preserved. We must also replace bitcode symbols with undefined symbols so
+ // that they may be replaced with real definitions without conflicting.
for (BitcodeFile *File : BitcodeFiles)
- for (SymbolBody *Body : File->getSymbols())
- if (isa<DefinedBitcode>(Body) && !isa<DefinedBitcode>(Body->repl()))
+ for (SymbolBody *Body : File->getSymbols()) {
+ if (!isa<DefinedBitcode>(Body))
+ continue;
+ if (Body->symbol()->IsUsedInRegularObj)
CG->addMustPreserveSymbol(Body->getName());
-
- // Likewise for other symbols that must be preserved.
- for (Undefined *U : Config->GCRoot) {
- if (auto *S = dyn_cast<DefinedBitcode>(U->repl()))
- CG->addMustPreserveSymbol(S->getName());
- else if (auto *S = dyn_cast_or_null<DefinedBitcode>(U->getWeakAlias()))
- CG->addMustPreserveSymbol(S->getName());
- }
+ replaceBody<Undefined>(Body->symbol(), Body->getName());
+ }
CG->setModule(BitcodeFiles[0]->takeModule());
for (unsigned I = 1, E = BitcodeFiles.size(); I != E; ++I)
@@ -434,10 +408,8 @@ std::vector<ObjectFile *> SymbolTable::createLTOObjects(LTOCodeGenerator *CG) {
std::vector<ObjectFile *> ObjFiles;
for (SmallString<0> &Obj : Objs) {
- auto *ObjFile = new ObjectFile(MemoryBufferRef(Obj, "<LTO object>"));
- Files.emplace_back(ObjFile);
+ auto *ObjFile = make<ObjectFile>(MemoryBufferRef(Obj, "<LTO object>"));
ObjectFiles.push_back(ObjFile);
- ObjFile->parse();
ObjFiles.push_back(ObjFile);
}
diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h
index 8bf4387cdfff..703821f2e124 100644
--- a/COFF/SymbolTable.h
+++ b/COFF/SymbolTable.h
@@ -11,18 +11,12 @@
#define LLD_COFF_SYMBOL_TABLE_H
#include "InputFiles.h"
+#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/raw_ostream.h"
-#ifdef _MSC_VER
-// <future> depends on <eh.h> for __uncaught_exception.
-#include <eh.h>
-#endif
-
-#include <future>
-
namespace llvm {
struct LTOCodeGenerator;
}
@@ -31,8 +25,12 @@ namespace lld {
namespace coff {
class Chunk;
+class CommonChunk;
class Defined;
+class DefinedAbsolute;
+class DefinedRelative;
class Lazy;
+class SectionChunk;
class SymbolBody;
struct Symbol;
@@ -45,18 +43,17 @@ struct Symbol;
// conflicts. For example, obviously, a defined symbol is better than
// an undefined symbol. Or, if there's a conflict between a lazy and a
// undefined, it'll read an archive member to read a real definition
-// to replace the lazy symbol. The logic is implemented in resolve().
+// to replace the lazy symbol. The logic is implemented in the
+// add*() functions, which are called by input files as they are parsed.
+// There is one add* function per symbol type.
class SymbolTable {
public:
- void addFile(std::unique_ptr<InputFile> File);
- std::vector<std::unique_ptr<InputFile>> &getFiles() { return Files; }
- void step();
- void run();
- bool queueEmpty();
+ void addFile(InputFile *File);
- // Print an error message on undefined symbols. If Resolve is true, try to
- // resolve any undefined symbols and update the symbol table accordingly.
- void reportRemainingUndefines(bool Resolve);
+ // Try to resolve any undefined symbols and update the symbol table
+ // accordingly, then print an error message for any remaining undefined
+ // symbols.
+ void reportRemainingUndefines();
// Returns a list of chunks of selected symbols.
std::vector<Chunk *> getChunks();
@@ -69,7 +66,7 @@ public:
// mangled symbol. This function tries to find a mangled name
// for U from the symbol table, and if found, set the symbol as
// a weak alias for U.
- void mangleMaybe(Undefined *U);
+ void mangleMaybe(SymbolBody *B);
StringRef findMangle(StringRef Name);
// Print a layout map to OS.
@@ -88,37 +85,44 @@ public:
std::vector<ObjectFile *> ObjectFiles;
// Creates an Undefined symbol for a given name.
- Undefined *addUndefined(StringRef Name);
- DefinedRelative *addRelative(StringRef Name, uint64_t VA);
- DefinedAbsolute *addAbsolute(StringRef Name, uint64_t VA);
+ SymbolBody *addUndefined(StringRef Name);
+
+ Symbol *addRelative(StringRef N, uint64_t VA);
+ Symbol *addAbsolute(StringRef N, uint64_t VA);
+
+ Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias);
+ void addLazy(ArchiveFile *F, const Archive::Symbol Sym);
+ Symbol *addAbsolute(StringRef N, COFFSymbolRef S);
+ Symbol *addRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C);
+ Symbol *addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable);
+ Symbol *addCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C);
+ Symbol *addImportData(StringRef N, ImportFile *F);
+ Symbol *addImportThunk(StringRef Name, DefinedImportData *S,
+ uint16_t Machine);
+
+ void reportDuplicate(Symbol *Existing, InputFile *NewFile);
// A list of chunks which to be added to .rdata.
std::vector<Chunk *> LocalImportChunks;
private:
- void readArchives();
+ void readArchive();
void readObjects();
- void addSymbol(SymbolBody *New);
- void addLazy(Lazy *New, std::vector<Symbol *> *Accum);
- Symbol *insert(SymbolBody *New);
+ std::pair<Symbol *, bool> insert(StringRef Name);
StringRef findByPrefix(StringRef Prefix);
- void addMemberFile(Lazy *Body);
void addCombinedLTOObject(ObjectFile *Obj);
std::vector<ObjectFile *> createLTOObjects(llvm::LTOCodeGenerator *CG);
- llvm::DenseMap<StringRef, Symbol *> Symtab;
-
- std::vector<std::unique_ptr<InputFile>> Files;
- std::vector<std::future<ArchiveFile *>> ArchiveQueue;
- std::vector<std::future<InputFile *>> ObjectQueue;
+ llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> Symtab;
std::vector<BitcodeFile *> BitcodeFiles;
std::vector<SmallString<0>> Objs;
- llvm::BumpPtrAllocator Alloc;
};
+extern SymbolTable *Symtab;
+
} // namespace coff
} // namespace lld
diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp
index 6e2db6631ce7..6de85d581f49 100644
--- a/COFF/Symbols.cpp
+++ b/COFF/Symbols.cpp
@@ -7,16 +7,17 @@
//
//===----------------------------------------------------------------------===//
+#include "Symbols.h"
#include "Error.h"
#include "InputFiles.h"
-#include "Symbols.h"
+#include "Memory.h"
+#include "Strings.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
+using namespace llvm;
using namespace llvm::object;
-using llvm::sys::fs::identify_magic;
-using llvm::sys::fs::file_magic;
namespace lld {
namespace coff {
@@ -36,130 +37,14 @@ StringRef SymbolBody::getName() {
return Name;
}
-// Returns 1, 0 or -1 if this symbol should take precedence
-// over the Other, tie or lose, respectively.
-int SymbolBody::compare(SymbolBody *Other) {
- Kind LK = kind(), RK = Other->kind();
-
- // Normalize so that the smaller kind is on the left.
- if (LK > RK)
- return -Other->compare(this);
-
- // First handle comparisons between two different kinds.
- if (LK != RK) {
- if (RK > LastDefinedKind) {
- if (LK == LazyKind && cast<Undefined>(Other)->WeakAlias)
- return -1;
-
- // The LHS is either defined or lazy and so it wins.
- assert((LK <= LastDefinedKind || LK == LazyKind) && "Bad kind!");
- return 1;
- }
-
- // Bitcode has special complexities.
- if (RK == DefinedBitcodeKind) {
- auto *RHS = cast<DefinedBitcode>(Other);
-
- switch (LK) {
- case DefinedCommonKind:
- return 1;
-
- case DefinedRegularKind:
- // As an approximation, regular symbols win over bitcode symbols,
- // but we definitely have a conflict if the regular symbol is not
- // replaceable and neither is the bitcode symbol. We do not
- // replicate the rest of the symbol resolution logic here; symbol
- // resolution will be done accurately after lowering bitcode symbols
- // to regular symbols in addCombinedLTOObject().
- if (cast<DefinedRegular>(this)->isCOMDAT() || RHS->IsReplaceable)
- return 1;
-
- // Fallthrough to the default of a tie otherwise.
- default:
- return 0;
- }
- }
-
- // Either of the object file kind will trump a higher kind.
- if (LK <= LastDefinedCOFFKind)
- return 1;
-
- // The remaining kind pairs are ties amongst defined symbols.
- return 0;
- }
-
- // Now handle the case where the kinds are the same.
- switch (LK) {
- case DefinedRegularKind: {
- auto *LHS = cast<DefinedRegular>(this);
- auto *RHS = cast<DefinedRegular>(Other);
- if (LHS->isCOMDAT() && RHS->isCOMDAT())
- return LHS->getFileIndex() < RHS->getFileIndex() ? 1 : -1;
- return 0;
- }
-
- case DefinedCommonKind: {
- auto *LHS = cast<DefinedCommon>(this);
- auto *RHS = cast<DefinedCommon>(Other);
- if (LHS->getSize() == RHS->getSize())
- return LHS->getFileIndex() < RHS->getFileIndex() ? 1 : -1;
- return LHS->getSize() > RHS->getSize() ? 1 : -1;
- }
-
- case DefinedBitcodeKind: {
- auto *LHS = cast<DefinedBitcode>(this);
- auto *RHS = cast<DefinedBitcode>(Other);
- // If both are non-replaceable, we have a tie.
- if (!LHS->IsReplaceable && !RHS->IsReplaceable)
- return 0;
-
- // Non-replaceable symbols win, but even two replaceable symboles don't
- // tie. If both symbols are replaceable, choice is arbitrary.
- if (RHS->IsReplaceable && LHS->IsReplaceable)
- return uintptr_t(LHS) < uintptr_t(RHS) ? 1 : -1;
- return LHS->IsReplaceable ? -1 : 1;
- }
-
- case LazyKind: {
- // Don't tie, pick the earliest.
- auto *LHS = cast<Lazy>(this);
- auto *RHS = cast<Lazy>(Other);
- return LHS->getFileIndex() < RHS->getFileIndex() ? 1 : -1;
- }
-
- case UndefinedKind: {
- auto *LHS = cast<Undefined>(this);
- auto *RHS = cast<Undefined>(Other);
- // Tie if both undefined symbols have different weak aliases.
- if (LHS->WeakAlias && RHS->WeakAlias) {
- if (LHS->WeakAlias->getName() != RHS->WeakAlias->getName())
- return 0;
- return uintptr_t(LHS) < uintptr_t(RHS) ? 1 : -1;
- }
- return LHS->WeakAlias ? 1 : -1;
- }
-
- case DefinedLocalImportKind:
- case DefinedImportThunkKind:
- case DefinedImportDataKind:
- case DefinedAbsoluteKind:
- case DefinedRelativeKind:
- // These all simply tie.
- return 0;
- }
- llvm_unreachable("unknown symbol kind");
-}
-
-std::string SymbolBody::getDebugName() {
- std::string N = getName().str();
- if (auto *D = dyn_cast<DefinedCOFF>(this)) {
- N += " ";
- N += D->File->getShortName();
- } else if (auto *D = dyn_cast<DefinedBitcode>(this)) {
- N += " ";
- N += D->File->getShortName();
- }
- return N;
+InputFile *SymbolBody::getFile() {
+ if (auto *Sym = dyn_cast<DefinedCOFF>(this))
+ return Sym->File;
+ if (auto *Sym = dyn_cast<DefinedBitcode>(this))
+ return Sym->File;
+ if (auto *Sym = dyn_cast<Lazy>(this))
+ return Sym->File;
+ return nullptr;
}
COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
@@ -174,44 +59,27 @@ DefinedImportThunk::DefinedImportThunk(StringRef Name, DefinedImportData *S,
uint16_t Machine)
: Defined(DefinedImportThunkKind, Name) {
switch (Machine) {
- case AMD64: Data.reset(new ImportThunkChunkX64(S)); return;
- case I386: Data.reset(new ImportThunkChunkX86(S)); return;
- case ARMNT: Data.reset(new ImportThunkChunkARM(S)); return;
+ case AMD64: Data = make<ImportThunkChunkX64>(S); return;
+ case I386: Data = make<ImportThunkChunkX86>(S); return;
+ case ARMNT: Data = make<ImportThunkChunkARM>(S); return;
default: llvm_unreachable("unknown machine type");
}
}
-std::unique_ptr<InputFile> Lazy::getMember() {
- MemoryBufferRef MBRef = File->getMember(&Sym);
-
- // getMember returns an empty buffer if the member was already
- // read from the library.
- if (MBRef.getBuffer().empty())
- return std::unique_ptr<InputFile>(nullptr);
-
- file_magic Magic = identify_magic(MBRef.getBuffer());
- if (Magic == file_magic::coff_import_library)
- return std::unique_ptr<InputFile>(new ImportFile(MBRef));
-
- std::unique_ptr<InputFile> Obj;
- if (Magic == file_magic::coff_object)
- Obj.reset(new ObjectFile(MBRef));
- else if (Magic == file_magic::bitcode)
- Obj.reset(new BitcodeFile(MBRef));
- else
- fatal("unknown file type: " + File->getName());
-
- Obj->setParentName(File->getName());
- return Obj;
-}
-
Defined *Undefined::getWeakAlias() {
// A weak alias may be a weak alias to another symbol, so check recursively.
for (SymbolBody *A = WeakAlias; A; A = cast<Undefined>(A)->WeakAlias)
- if (auto *D = dyn_cast<Defined>(A->repl()))
+ if (auto *D = dyn_cast<Defined>(A))
return D;
return nullptr;
}
+// Returns a symbol name for an error message.
+std::string toString(SymbolBody &B) {
+ if (Optional<std::string> S = demangle(B.getName()))
+ return ("\"" + *S + "\" (" + B.getName() + ")").str();
+ return B.getName();
+}
+
} // namespace coff
} // namespace lld
diff --git a/COFF/Symbols.h b/COFF/Symbols.h
index f96c1fb3cc1d..bc9ad4aa8aff 100644
--- a/COFF/Symbols.h
+++ b/COFF/Symbols.h
@@ -12,6 +12,7 @@
#include "Chunks.h"
#include "Config.h"
+#include "Memory.h"
#include "lld/Core/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Object/Archive.h"
@@ -32,15 +33,8 @@ class ArchiveFile;
class BitcodeFile;
class InputFile;
class ObjectFile;
-class SymbolBody;
-
-// A real symbol object, SymbolBody, is usually accessed indirectly
-// through a Symbol. There's always one Symbol for each symbol name.
-// The resolver updates SymbolBody pointers as it resolves symbols.
-struct Symbol {
- explicit Symbol(SymbolBody *P) : Body(P) {}
- SymbolBody *Body;
-};
+struct Symbol;
+class SymbolTable;
// The base class for real symbol classes.
class SymbolBody {
@@ -75,28 +69,19 @@ public:
// Returns the symbol name.
StringRef getName();
- // A SymbolBody has a backreference to a Symbol. Originally they are
- // doubly-linked. A backreference will never change. But the pointer
- // in the Symbol may be mutated by the resolver. If you have a
- // pointer P to a SymbolBody and are not sure whether the resolver
- // has chosen the object among other objects having the same name,
- // you can access P->Backref->Body to get the resolver's result.
- void setBackref(Symbol *P) { Backref = P; }
- SymbolBody *repl() { return Backref ? Backref->Body : this; }
-
- // Decides which symbol should "win" in the symbol table, this or
- // the Other. Returns 1 if this wins, -1 if the Other wins, or 0 if
- // they are duplicate (conflicting) symbols.
- int compare(SymbolBody *Other);
+ // Returns the file from which this symbol was created.
+ InputFile *getFile();
- // Returns a name of this symbol including source file name.
- // Used only for debugging and logging.
- std::string getDebugName();
+ Symbol *symbol();
+ const Symbol *symbol() const {
+ return const_cast<SymbolBody *>(this)->symbol();
+ }
protected:
+ friend SymbolTable;
explicit SymbolBody(Kind K, StringRef N = "")
: SymbolKind(K), IsExternal(true), IsCOMDAT(false),
- IsReplaceable(false), Name(N) {}
+ IsReplaceable(false), WrittenToSymtab(false), Name(N) {}
const unsigned SymbolKind : 8;
unsigned IsExternal : 1;
@@ -107,8 +92,12 @@ protected:
// This bit is used by the \c DefinedBitcode subclass.
unsigned IsReplaceable : 1;
+public:
+ // This bit is used by Writer::createSymbolAndStringTable().
+ unsigned WrittenToSymtab : 1;
+
+protected:
StringRef Name;
- Symbol *Backref = nullptr;
};
// The base class for any defined symbols, including absolute symbols,
@@ -149,12 +138,13 @@ public:
return S->kind() <= LastDefinedCOFFKind;
}
- int getFileIndex() { return File->Index; }
+ ObjectFile *getFile() { return File; }
COFFSymbolRef getCOFFSymbol();
-protected:
ObjectFile *File;
+
+protected:
const coff_symbol_generic *Sym;
};
@@ -194,7 +184,7 @@ public:
uint64_t getRVA() { return Data->getRVA(); }
private:
- friend SymbolBody;
+ friend SymbolTable;
uint64_t getSize() { return Sym->Value; }
CommonChunk *Data;
};
@@ -253,14 +243,12 @@ public:
static bool classof(const SymbolBody *S) { return S->kind() == LazyKind; }
- // Returns an object file for this symbol, or a nullptr if the file
- // was already returned.
- std::unique_ptr<InputFile> getMember();
+ ArchiveFile *File;
- int getFileIndex() { return File->Index; }
+private:
+ friend SymbolTable;
private:
- ArchiveFile *File;
const Archive::Symbol Sym;
};
@@ -293,26 +281,22 @@ public:
// table in an output. The former has "__imp_" prefix.
class DefinedImportData : public Defined {
public:
- DefinedImportData(StringRef D, StringRef N, StringRef E,
- const coff_import_header *H)
- : Defined(DefinedImportDataKind, N), DLLName(D), ExternalName(E), Hdr(H) {
+ DefinedImportData(StringRef N, ImportFile *F)
+ : Defined(DefinedImportDataKind, N), File(F) {
}
static bool classof(const SymbolBody *S) {
return S->kind() == DefinedImportDataKind;
}
- uint64_t getRVA() { return Location->getRVA(); }
- StringRef getDLLName() { return DLLName; }
- StringRef getExternalName() { return ExternalName; }
- void setLocation(Chunk *AddressTable) { Location = AddressTable; }
- uint16_t getOrdinal() { return Hdr->OrdinalHint; }
+ uint64_t getRVA() { return File->Location->getRVA(); }
+ StringRef getDLLName() { return File->DLLName; }
+ StringRef getExternalName() { return File->ExternalName; }
+ void setLocation(Chunk *AddressTable) { File->Location = AddressTable; }
+ uint16_t getOrdinal() { return File->Hdr->OrdinalHint; }
private:
- StringRef DLLName;
- StringRef ExternalName;
- const coff_import_header *Hdr;
- Chunk *Location = nullptr;
+ ImportFile *File;
};
// This class represents a symbol for a jump table entry which jumps
@@ -329,10 +313,10 @@ public:
}
uint64_t getRVA() { return Data->getRVA(); }
- Chunk *getChunk() { return Data.get(); }
+ Chunk *getChunk() { return Data; }
private:
- std::unique_ptr<Chunk> Data;
+ Chunk *Data;
};
// If you have a symbol "__imp_foo" in your object file, a symbol name
@@ -343,17 +327,17 @@ private:
class DefinedLocalImport : public Defined {
public:
DefinedLocalImport(StringRef N, Defined *S)
- : Defined(DefinedLocalImportKind, N), Data(S) {}
+ : Defined(DefinedLocalImportKind, N), Data(make<LocalImportChunk>(S)) {}
static bool classof(const SymbolBody *S) {
return S->kind() == DefinedLocalImportKind;
}
- uint64_t getRVA() { return Data.getRVA(); }
- Chunk *getChunk() { return &Data; }
+ uint64_t getRVA() { return Data->getRVA(); }
+ Chunk *getChunk() { return Data; }
private:
- LocalImportChunk Data;
+ LocalImportChunk *Data;
};
class DefinedBitcode : public Defined {
@@ -361,6 +345,11 @@ class DefinedBitcode : public Defined {
public:
DefinedBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable)
: Defined(DefinedBitcodeKind, N), File(F) {
+ // IsReplaceable tracks whether the bitcode symbol may be replaced with some
+ // other (defined, common or bitcode) symbol. This is the case for common,
+ // comdat and weak external symbols. We try to replace bitcode symbols with
+ // "real" symbols (see SymbolTable::add{Regular,Bitcode}), and resolve the
+ // result against the real symbol from the combined LTO object.
this->IsReplaceable = IsReplaceable;
}
@@ -368,7 +357,6 @@ public:
return S->kind() == DefinedBitcodeKind;
}
-private:
BitcodeFile *File;
};
@@ -397,6 +385,52 @@ inline uint64_t Defined::getRVA() {
llvm_unreachable("unknown symbol kind");
}
+// A real symbol object, SymbolBody, is usually stored within a Symbol. There's
+// always one Symbol for each symbol name. The resolver updates the SymbolBody
+// stored in the Body field of this object as it resolves symbols. Symbol also
+// holds computed properties of symbol names.
+struct Symbol {
+ // True if this symbol was referenced by a regular (non-bitcode) object.
+ unsigned IsUsedInRegularObj : 1;
+
+ // True if we've seen both a lazy and an undefined symbol with this symbol
+ // name, which means that we have enqueued an archive member load and should
+ // not load any more archive members to resolve the same symbol.
+ unsigned PendingArchiveLoad : 1;
+
+ // This field is used to store the Symbol's SymbolBody. This instantiation of
+ // AlignedCharArrayUnion gives us a struct with a char array field that is
+ // large and aligned enough to store any derived class of SymbolBody.
+ llvm::AlignedCharArrayUnion<DefinedRegular, DefinedCommon, DefinedAbsolute,
+ DefinedRelative, Lazy, Undefined,
+ DefinedImportData, DefinedImportThunk,
+ DefinedLocalImport, DefinedBitcode>
+ Body;
+
+ SymbolBody *body() {
+ return reinterpret_cast<SymbolBody *>(Body.buffer);
+ }
+ const SymbolBody *body() const { return const_cast<Symbol *>(this)->body(); }
+};
+
+template <typename T, typename... ArgT>
+void replaceBody(Symbol *S, ArgT &&... Arg) {
+ static_assert(sizeof(T) <= sizeof(S->Body), "Body too small");
+ static_assert(alignof(T) <= alignof(decltype(S->Body)),
+ "Body not aligned enough");
+ assert(static_cast<SymbolBody *>(static_cast<T *>(nullptr)) == nullptr &&
+ "Not a SymbolBody");
+ new (S->Body.buffer) T(std::forward<ArgT>(Arg)...);
+}
+
+inline Symbol *SymbolBody::symbol() {
+ assert(isExternal());
+ return reinterpret_cast<Symbol *>(reinterpret_cast<char *>(this) -
+ offsetof(Symbol, Body));
+}
+
+std::string toString(SymbolBody &B);
+
} // namespace coff
} // namespace lld
diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp
index d8077df95701..3e69aebbb424 100644
--- a/COFF/Writer.cpp
+++ b/COFF/Writer.cpp
@@ -7,13 +7,15 @@
//
//===----------------------------------------------------------------------===//
+#include "Writer.h"
#include "Config.h"
#include "DLL.h"
#include "Error.h"
#include "InputFiles.h"
+#include "Memory.h"
+#include "PDB.h"
#include "SymbolTable.h"
#include "Symbols.h"
-#include "Writer.h"
#include "lld/Core/Parallel.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
@@ -21,6 +23,7 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/RandomNumberGenerator.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstdio>
@@ -42,6 +45,61 @@ static const int DOSStubSize = 64;
static const int NumberfOfDataDirectory = 16;
namespace {
+
+class DebugDirectoryChunk : public Chunk {
+public:
+ DebugDirectoryChunk(const std::vector<std::unique_ptr<Chunk>> &R)
+ : Records(R) {}
+
+ size_t getSize() const override {
+ return Records.size() * sizeof(debug_directory);
+ }
+
+ void writeTo(uint8_t *B) const override {
+ auto *D = reinterpret_cast<debug_directory *>(B + OutputSectionOff);
+
+ for (const std::unique_ptr<Chunk> &Record : Records) {
+ D->Characteristics = 0;
+ D->TimeDateStamp = 0;
+ D->MajorVersion = 0;
+ D->MinorVersion = 0;
+ D->Type = COFF::IMAGE_DEBUG_TYPE_CODEVIEW;
+ D->SizeOfData = Record->getSize();
+ D->AddressOfRawData = Record->getRVA();
+ // TODO(compnerd) get the file offset
+ D->PointerToRawData = 0;
+
+ ++D;
+ }
+ }
+
+private:
+ const std::vector<std::unique_ptr<Chunk>> &Records;
+};
+
+class CVDebugRecordChunk : public Chunk {
+ size_t getSize() const override {
+ return sizeof(codeview::DebugInfo) + Config->PDBPath.size() + 1;
+ }
+
+ void writeTo(uint8_t *B) const override {
+ // Save off the DebugInfo entry to backfill the file signature (build id)
+ // in Writer::writeBuildId
+ DI = reinterpret_cast<codeview::DebugInfo *>(B + OutputSectionOff);
+
+ DI->Signature.CVSignature = OMF::Signature::PDB70;
+
+ // variable sized field (PDB Path)
+ auto *P = reinterpret_cast<char *>(B + OutputSectionOff + sizeof(*DI));
+ if (!Config->PDBPath.empty())
+ memcpy(P, Config->PDBPath.data(), Config->PDBPath.size());
+ P[Config->PDBPath.size()] = '\0';
+ }
+
+public:
+ mutable codeview::DebugInfo *DI = nullptr;
+};
+
// The writer writes a SymbolTable result to a file.
class Writer {
public:
@@ -62,6 +120,7 @@ private:
void setSectionPermissions();
void writeSections();
void sortExceptionTable();
+ void writeBuildId();
void applyRelocations();
llvm::Optional<coff_symbol16> createSymbol(Defined *D);
@@ -76,9 +135,7 @@ private:
std::map<StringRef, std::vector<DefinedImportData *>> binImports();
SymbolTable *Symtab;
- std::unique_ptr<llvm::FileOutputBuffer> Buffer;
- llvm::SpecificBumpPtrAllocator<OutputSection> CAlloc;
- llvm::SpecificBumpPtrAllocator<BaserelChunk> BAlloc;
+ std::unique_ptr<FileOutputBuffer> Buffer;
std::vector<OutputSection *> OutputSections;
std::vector<char> Strtab;
std::vector<llvm::object::coff_symbol16> OutputSymtab;
@@ -87,6 +144,11 @@ private:
EdataContents Edata;
std::unique_ptr<SEHTableChunk> SEHTable;
+ std::unique_ptr<Chunk> DebugDirectory;
+ std::vector<std::unique_ptr<Chunk>> DebugRecords;
+ CVDebugRecordChunk *BuildId = nullptr;
+ ArrayRef<uint8_t> SectionTable;
+
uint64_t FileSize;
uint32_t PointerToSymbolTable = 0;
uint64_t SizeOfImage;
@@ -239,6 +301,11 @@ void Writer::run() {
fixSafeSEHSymbols();
writeSections();
sortExceptionTable();
+ writeBuildId();
+
+ if (!Config->PDBPath.empty())
+ createPDB(Config->PDBPath, Symtab, SectionTable);
+
if (auto EC = Buffer->commit())
fatal(EC, "failed to write the output file");
}
@@ -274,7 +341,7 @@ void Writer::createSections() {
StringRef Name = getOutputSection(Pair.first);
OutputSection *&Sec = Sections[Name];
if (!Sec) {
- Sec = new (CAlloc.Allocate()) OutputSection(Name);
+ Sec = make<OutputSection>(Name);
OutputSections.push_back(Sec);
}
std::vector<Chunk *> &Chunks = Pair.second;
@@ -286,25 +353,46 @@ void Writer::createSections() {
}
void Writer::createMiscChunks() {
+ OutputSection *RData = createSection(".rdata");
+
// Create thunks for locally-dllimported symbols.
if (!Symtab->LocalImportChunks.empty()) {
- OutputSection *Sec = createSection(".rdata");
for (Chunk *C : Symtab->LocalImportChunks)
- Sec->addChunk(C);
+ RData->addChunk(C);
+ }
+
+ // Create Debug Information Chunks
+ if (Config->Debug) {
+ DebugDirectory = llvm::make_unique<DebugDirectoryChunk>(DebugRecords);
+
+ // TODO(compnerd) create a coffgrp entry if DebugType::CV is not enabled
+ if (Config->DebugTypes & static_cast<unsigned>(coff::DebugType::CV)) {
+ auto Chunk = llvm::make_unique<CVDebugRecordChunk>();
+
+ BuildId = Chunk.get();
+ DebugRecords.push_back(std::move(Chunk));
+ }
+
+ RData->addChunk(DebugDirectory.get());
+ for (const std::unique_ptr<Chunk> &C : DebugRecords)
+ RData->addChunk(C.get());
}
// Create SEH table. x86-only.
if (Config->Machine != I386)
return;
+
std::set<Defined *> Handlers;
+
for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) {
if (!File->SEHCompat)
return;
for (SymbolBody *B : File->SEHandlers)
- Handlers.insert(cast<Defined>(B->repl()));
+ Handlers.insert(cast<Defined>(B));
}
+
SEHTable.reset(new SEHTableChunk(Handlers));
- createSection(".rdata")->addChunk(SEHTable.get());
+ RData->addChunk(SEHTable.get());
}
// Create .idata section for the DLL-imported symbol table.
@@ -340,7 +428,7 @@ void Writer::createImportTables() {
Sec->addChunk(C);
}
if (!DelayIdata.empty()) {
- Defined *Helper = cast<Defined>(Config->DelayLoadHelper->repl());
+ Defined *Helper = cast<Defined>(Config->DelayLoadHelper);
DelayIdata.create(Helper);
OutputSection *Sec = createSection(".didat");
for (Chunk *C : DelayIdata.getChunks())
@@ -383,6 +471,10 @@ size_t Writer::addEntryToStringTable(StringRef Str) {
}
Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
+ // Relative symbols are unrepresentable in a COFF symbol table.
+ if (isa<DefinedRelative>(Def))
+ return None;
+
if (auto *D = dyn_cast<DefinedRegular>(Def))
if (!D->getChunk()->isLive())
return None;
@@ -409,7 +501,6 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
switch (Def->kind()) {
case SymbolBody::DefinedAbsoluteKind:
- case SymbolBody::DefinedRelativeKind:
Sym.Value = Def->getRVA();
Sym.SectionNumber = IMAGE_SYM_ABSOLUTE;
break;
@@ -445,13 +536,11 @@ void Writer::createSymbolAndStringTable() {
for (lld::coff::ObjectFile *File : Symtab->ObjectFiles)
for (SymbolBody *B : File->getSymbols())
if (auto *D = dyn_cast<Defined>(B))
- if (Optional<coff_symbol16> Sym = createSymbol(D))
- OutputSymtab.push_back(*Sym);
-
- for (ImportFile *File : Symtab->ImportFiles)
- for (SymbolBody *B : File->getSymbols())
- if (Optional<coff_symbol16> Sym = createSymbol(cast<Defined>(B)))
- OutputSymtab.push_back(*Sym);
+ if (!D->WrittenToSymtab) {
+ D->WrittenToSymtab = true;
+ if (Optional<coff_symbol16> Sym = createSymbol(D))
+ OutputSymtab.push_back(*Sym);
+ }
OutputSection *LastSection = OutputSections.back();
// We position the symbol table to be adjacent to the end of the last section.
@@ -542,7 +631,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
PE->SizeOfImage = SizeOfImage;
PE->SizeOfHeaders = SizeOfHeaders;
if (!Config->NoEntry) {
- Defined *Entry = cast<Defined>(Config->Entry->repl());
+ Defined *Entry = cast<Defined>(Config->Entry);
PE->AddressOfEntryPoint = Entry->getRVA();
// Pointer to thumb code must have the LSB set, so adjust it.
if (Config->Machine == ARMNT)
@@ -584,33 +673,32 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
Dir[IAT].RelativeVirtualAddress = Idata.getIATRVA();
Dir[IAT].Size = Idata.getIATSize();
}
- if (!DelayIdata.empty()) {
- Dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress =
- DelayIdata.getDirRVA();
- Dir[DELAY_IMPORT_DESCRIPTOR].Size = DelayIdata.getDirSize();
- }
if (OutputSection *Sec = findSection(".rsrc")) {
Dir[RESOURCE_TABLE].RelativeVirtualAddress = Sec->getRVA();
Dir[RESOURCE_TABLE].Size = Sec->getVirtualSize();
}
- if (OutputSection *Sec = findSection(".reloc")) {
- Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = Sec->getRVA();
- Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize();
- }
if (OutputSection *Sec = findSection(".pdata")) {
Dir[EXCEPTION_TABLE].RelativeVirtualAddress = Sec->getRVA();
Dir[EXCEPTION_TABLE].Size = Sec->getVirtualSize();
}
+ if (OutputSection *Sec = findSection(".reloc")) {
+ Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = Sec->getRVA();
+ Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize();
+ }
if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) {
- if (Defined *B = dyn_cast<Defined>(Sym->Body)) {
+ if (Defined *B = dyn_cast<Defined>(Sym->body())) {
Dir[TLS_TABLE].RelativeVirtualAddress = B->getRVA();
Dir[TLS_TABLE].Size = Config->is64()
? sizeof(object::coff_tls_directory64)
: sizeof(object::coff_tls_directory32);
}
}
+ if (Config->Debug) {
+ Dir[DEBUG_DIRECTORY].RelativeVirtualAddress = DebugDirectory->getRVA();
+ Dir[DEBUG_DIRECTORY].Size = DebugDirectory->getSize();
+ }
if (Symbol *Sym = Symtab->findUnderscore("_load_config_used")) {
- if (auto *B = dyn_cast<DefinedRegular>(Sym->Body)) {
+ if (auto *B = dyn_cast<DefinedRegular>(Sym->body())) {
SectionChunk *SC = B->getChunk();
assert(B->getRVA() >= SC->getRVA());
uint64_t OffsetInChunk = B->getRVA() - SC->getRVA();
@@ -626,12 +714,19 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
Dir[LOAD_CONFIG_TABLE].Size = LoadConfigSize;
}
}
+ if (!DelayIdata.empty()) {
+ Dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress =
+ DelayIdata.getDirRVA();
+ Dir[DELAY_IMPORT_DESCRIPTOR].Size = DelayIdata.getDirSize();
+ }
// Write section table
for (OutputSection *Sec : OutputSections) {
Sec->writeHeaderTo(Buf);
Buf += sizeof(coff_section);
}
+ SectionTable = ArrayRef<uint8_t>(
+ Buf - OutputSections.size() * sizeof(coff_section), Buf);
if (OutputSymtab.empty())
return;
@@ -660,8 +755,10 @@ void Writer::openFile(StringRef Path) {
void Writer::fixSafeSEHSymbols() {
if (!SEHTable)
return;
- Config->SEHTable->setRVA(SEHTable->getRVA());
- Config->SEHCount->setVA(SEHTable->getSize() / 4);
+ if (auto *T = dyn_cast<DefinedRelative>(Config->SEHTable->body()))
+ T->setRVA(SEHTable->getRVA());
+ if (auto *C = dyn_cast<DefinedAbsolute>(Config->SEHCount->body()))
+ C->setVA(SEHTable->getSize() / 4);
}
// Handles /section options to allow users to overwrite
@@ -715,6 +812,30 @@ void Writer::sortExceptionTable() {
errs() << "warning: don't know how to handle .pdata.\n";
}
+// Backfill the CVSignature in a PDB70 Debug Record. This backfilling allows us
+// to get reproducible builds.
+void Writer::writeBuildId() {
+ // There is nothing to backfill if BuildId was not setup.
+ if (BuildId == nullptr)
+ return;
+
+ MD5 Hash;
+ MD5::MD5Result Res;
+
+ Hash.update(ArrayRef<uint8_t>{Buffer->getBufferStart(),
+ Buffer->getBufferEnd()});
+ Hash.final(Res);
+
+ assert(BuildId->DI->Signature.CVSignature == OMF::Signature::PDB70 &&
+ "only PDB 7.0 is supported");
+ assert(sizeof(Res) == sizeof(BuildId->DI->PDB70.Signature) &&
+ "signature size mismatch");
+ memcpy(BuildId->DI->PDB70.Signature, Res,
+ sizeof(codeview::PDB70DebugInfo::Signature));
+ // TODO(compnerd) track the Age
+ BuildId->DI->PDB70.Age = 1;
+}
+
OutputSection *Writer::findSection(StringRef Name) {
for (OutputSection *Sec : OutputSections)
if (Sec->getName() == Name)
@@ -744,16 +865,13 @@ OutputSection *Writer::createSection(StringRef Name) {
uint32_t Perms = StringSwitch<uint32_t>(Name)
.Case(".bss", BSS | R | W)
.Case(".data", DATA | R | W)
- .Case(".didat", DATA | R)
- .Case(".edata", DATA | R)
- .Case(".idata", DATA | R)
- .Case(".rdata", DATA | R)
+ .Cases(".didat", ".edata", ".idata", ".rdata", DATA | R)
.Case(".reloc", DATA | DISCARDABLE | R)
.Case(".text", CODE | R | X)
.Default(0);
if (!Perms)
llvm_unreachable("unknown section name");
- auto Sec = new (CAlloc.Allocate()) OutputSection(Name);
+ auto Sec = make<OutputSection>(Name);
Sec->addPermissions(Perms);
OutputSections.push_back(Sec);
return Sec;
@@ -784,13 +902,11 @@ void Writer::addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V) {
uint32_t P = V[J].RVA & Mask;
if (P == Page)
continue;
- BaserelChunk *Buf = BAlloc.Allocate();
- Dest->addChunk(new (Buf) BaserelChunk(Page, &V[I], &V[0] + J));
+ Dest->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J));
I = J;
Page = P;
}
if (I == J)
return;
- BaserelChunk *Buf = BAlloc.Allocate();
- Dest->addChunk(new (Buf) BaserelChunk(Page, &V[I], &V[0] + J));
+ Dest->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J));
}
diff --git a/COFF/Writer.h b/COFF/Writer.h
index 0473315ae50a..0d26090177d8 100644
--- a/COFF/Writer.h
+++ b/COFF/Writer.h
@@ -14,9 +14,7 @@
namespace lld {
namespace coff {
-
-class Chunk;
-class OutputSection;
+class SymbolTable;
void writeResult(SymbolTable *T);
diff --git a/ELF/CMakeLists.txt b/ELF/CMakeLists.txt
index a1b65adc7400..2e9d2b941fd9 100644
--- a/ELF/CMakeLists.txt
+++ b/ELF/CMakeLists.txt
@@ -2,24 +2,30 @@ set(LLVM_TARGET_DEFINITIONS Options.td)
tablegen(LLVM Options.inc -gen-opt-parser-defs)
add_public_tablegen_target(ELFOptionsTableGen)
+if(NOT LLD_BUILT_STANDALONE)
+ set(tablegen_deps intrinsics_gen)
+endif()
+
add_lld_library(lldELF
Driver.cpp
DriverUtils.cpp
EhFrame.cpp
Error.cpp
+ GdbIndex.cpp
ICF.cpp
InputFiles.cpp
InputSection.cpp
LTO.cpp
LinkerScript.cpp
MarkLive.cpp
+ Mips.cpp
OutputSections.cpp
Relocations.cpp
ScriptParser.cpp
Strings.cpp
- SymbolListFile.cpp
SymbolTable.cpp
Symbols.cpp
+ SyntheticSections.cpp
Target.cpp
Thunks.cpp
Writer.cpp
@@ -31,6 +37,8 @@ add_lld_library(lldELF
BitWriter
Codegen
Core
+ DebugInfoDWARF
+ Demangle
IPO
Linker
LTO
@@ -44,7 +52,10 @@ add_lld_library(lldELF
LINK_LIBS
lldConfig
+ lldCore
${PTHREAD_LIB}
- )
-add_dependencies(lldELF intrinsics_gen ELFOptionsTableGen)
+ DEPENDS
+ ELFOptionsTableGen
+ ${tablegen_deps}
+ )
diff --git a/ELF/Config.h b/ELF/Config.h
index 2ccd95e88775..b828cdb25047 100644
--- a/ELF/Config.h
+++ b/ELF/Config.h
@@ -12,6 +12,7 @@
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/Support/ELF.h"
#include <vector>
@@ -30,21 +31,36 @@ enum ELFKind {
ELF64BEKind
};
-enum class BuildIdKind { None, Fnv1, Md5, Sha1, Hexstring };
+// For --build-id.
+enum class BuildIdKind { None, Fast, Md5, Sha1, Hexstring, Uuid };
-enum class UnresolvedPolicy { NoUndef, Error, Warn, Ignore };
+// For --discard-{all,locals,none} and --retain-symbols-file.
+enum class DiscardPolicy { Default, All, Locals, RetainFile, None };
+
+// For --strip-{all,debug}.
+enum class StripPolicy { None, All, Debug };
+
+// For --unresolved-symbols.
+enum class UnresolvedPolicy { NoUndef, ReportError, Warn, Ignore };
+
+// For --sort-section and linkerscript sorting rules.
+enum class SortSectionPolicy { Default, None, Alignment, Name, Priority };
+
+// For --target2
+enum class Target2Policy { Abs, Rel, GotRel };
struct SymbolVersion {
llvm::StringRef Name;
bool IsExternCpp;
+ bool HasWildcard;
};
// This struct contains symbols version definition that
// can be found in version script if it is used for link.
struct VersionDefinition {
- VersionDefinition(llvm::StringRef Name, size_t Id) : Name(Name), Id(Id) {}
+ VersionDefinition(llvm::StringRef Name, uint16_t Id) : Name(Name), Id(Id) {}
llvm::StringRef Name;
- size_t Id;
+ uint16_t Id;
std::vector<SymbolVersion> Globals;
size_t NameOff; // Offset in string table.
};
@@ -54,75 +70,92 @@ struct VersionDefinition {
// and such fields have the same name as the corresponding options.
// Most fields are initialized by the driver.
struct Configuration {
- Symbol *EntrySym = nullptr;
InputFile *FirstElf = nullptr;
+ uint8_t OSABI = 0;
+ llvm::StringMap<uint64_t> SectionStartMap;
llvm::StringRef DynamicLinker;
llvm::StringRef Entry;
llvm::StringRef Emulation;
llvm::StringRef Fini;
llvm::StringRef Init;
- llvm::StringRef LtoAAPipeline;
- llvm::StringRef LtoNewPmPasses;
+ llvm::StringRef LTOAAPipeline;
+ llvm::StringRef LTONewPmPasses;
llvm::StringRef OutputFile;
llvm::StringRef SoName;
llvm::StringRef Sysroot;
+ llvm::StringSet<> RetainSymbolsFile;
std::string RPath;
std::vector<VersionDefinition> VersionDefinitions;
- std::vector<llvm::StringRef> DynamicList;
+ std::vector<llvm::StringRef> AuxiliaryList;
std::vector<llvm::StringRef> SearchPaths;
+ std::vector<llvm::StringRef> SymbolOrderingFile;
std::vector<llvm::StringRef> Undefined;
std::vector<SymbolVersion> VersionScriptGlobals;
+ std::vector<SymbolVersion> VersionScriptLocals;
std::vector<uint8_t> BuildIdVector;
bool AllowMultipleDefinition;
bool AsNeeded = false;
bool Bsymbolic;
bool BsymbolicFunctions;
+ bool ColorDiagnostics = false;
bool Demangle = true;
bool DisableVerify;
- bool DiscardAll;
- bool DiscardLocals;
- bool DiscardNone;
bool EhFrameHdr;
bool EnableNewDtags;
bool ExportDynamic;
bool FatalWarnings;
bool GcSections;
+ bool GdbIndex;
bool GnuHash = false;
bool ICF;
bool Mips64EL = false;
+ bool MipsN32Abi = false;
bool NoGnuUnique;
bool NoUndefinedVersion;
+ bool Nostdlib;
+ bool OFormatBinary;
+ bool OMagic;
bool Pic;
bool Pie;
bool PrintGcSections;
bool Rela;
bool Relocatable;
bool SaveTemps;
+ bool SingleRoRx;
bool Shared;
bool Static = false;
- bool StripAll;
- bool StripDebug;
bool SysvHash = true;
+ bool Target1Rel;
bool Threads;
bool Trace;
bool Verbose;
bool WarnCommon;
+ bool WarnMissingEntry;
bool ZCombreloc;
- bool ZExecStack;
+ bool ZExecstack;
bool ZNodelete;
bool ZNow;
bool ZOrigin;
bool ZRelro;
+ bool ExitEarly;
+ bool ZWxneeded;
+ DiscardPolicy Discard;
+ SortSectionPolicy SortSection;
+ StripPolicy Strip = StripPolicy::None;
UnresolvedPolicy UnresolvedSymbols;
+ Target2Policy Target2 = Target2Policy::GotRel;
BuildIdKind BuildId = BuildIdKind::None;
ELFKind EKind = ELFNoneKind;
uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL;
uint16_t EMachine = llvm::ELF::EM_NONE;
- uint64_t EntryAddr = -1;
+ uint64_t ErrorLimit = 20;
uint64_t ImageBase;
- unsigned LtoJobs;
- unsigned LtoO;
+ uint64_t MaxPageSize;
+ uint64_t ZStackSize;
+ unsigned LTOPartitions;
+ unsigned LTOO;
unsigned Optimize;
+ unsigned ThinLTOJobs;
};
// The only instance of Configuration struct.
diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp
index c6ca2639236f..a11dbc7cc47f 100644
--- a/ELF/Driver.cpp
+++ b/ELF/Driver.cpp
@@ -14,14 +14,17 @@
#include "InputFiles.h"
#include "InputSection.h"
#include "LinkerScript.h"
+#include "Memory.h"
#include "Strings.h"
-#include "SymbolListFile.h"
#include "SymbolTable.h"
#include "Target.h"
+#include "Threads.h"
#include "Writer.h"
+#include "lld/Config/Version.h"
#include "lld/Driver/Driver.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/CommandLine.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdlib>
@@ -38,48 +41,59 @@ using namespace lld::elf;
Configuration *elf::Config;
LinkerDriver *elf::Driver;
-bool elf::link(ArrayRef<const char *> Args, raw_ostream &Error) {
- HasError = false;
+BumpPtrAllocator elf::BAlloc;
+StringSaver elf::Saver{BAlloc};
+std::vector<SpecificAllocBase *> elf::SpecificAllocBase::Instances;
+
+bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
+ raw_ostream &Error) {
+ ErrorCount = 0;
ErrorOS = &Error;
+ Argv0 = Args[0];
- Configuration C;
- LinkerDriver D;
- ScriptConfiguration SC;
- Config = &C;
- Driver = &D;
- ScriptConfig = &SC;
+ Config = make<Configuration>();
+ Driver = make<LinkerDriver>();
+ ScriptConfig = make<ScriptConfiguration>();
- Driver->main(Args);
- return !HasError;
+ Driver->main(Args, CanExitEarly);
+ freeArena();
+ return !ErrorCount;
}
// Parses a linker -m option.
-static std::pair<ELFKind, uint16_t> parseEmulation(StringRef S) {
- if (S.endswith("_fbsd"))
+static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
+ uint8_t OSABI = 0;
+ StringRef S = Emul;
+ if (S.endswith("_fbsd")) {
S = S.drop_back(5);
+ OSABI = ELFOSABI_FREEBSD;
+ }
std::pair<ELFKind, uint16_t> Ret =
StringSwitch<std::pair<ELFKind, uint16_t>>(S)
- .Case("aarch64linux", {ELF64LEKind, EM_AARCH64})
+ .Cases("aarch64elf", "aarch64linux", {ELF64LEKind, EM_AARCH64})
.Case("armelf_linux_eabi", {ELF32LEKind, EM_ARM})
.Case("elf32_x86_64", {ELF32LEKind, EM_X86_64})
.Case("elf32btsmip", {ELF32BEKind, EM_MIPS})
.Case("elf32ltsmip", {ELF32LEKind, EM_MIPS})
+ .Case("elf32btsmipn32", {ELF32BEKind, EM_MIPS})
+ .Case("elf32ltsmipn32", {ELF32LEKind, EM_MIPS})
.Case("elf32ppc", {ELF32BEKind, EM_PPC})
.Case("elf64btsmip", {ELF64BEKind, EM_MIPS})
.Case("elf64ltsmip", {ELF64LEKind, EM_MIPS})
.Case("elf64ppc", {ELF64BEKind, EM_PPC64})
+ .Cases("elf_amd64", "elf_x86_64", {ELF64LEKind, EM_X86_64})
.Case("elf_i386", {ELF32LEKind, EM_386})
- .Case("elf_x86_64", {ELF64LEKind, EM_X86_64})
+ .Case("elf_iamcu", {ELF32LEKind, EM_IAMCU})
.Default({ELFNoneKind, EM_NONE});
if (Ret.first == ELFNoneKind) {
if (S == "i386pe" || S == "i386pep" || S == "thumb2pe")
- error("Windows targets are not supported on the ELF frontend: " + S);
+ error("Windows targets are not supported on the ELF frontend: " + Emul);
else
- error("unknown emulation: " + S);
+ error("unknown emulation: " + Emul);
}
- return Ret;
+ return std::make_tuple(Ret.first, Ret.second, OSABI);
}
// Returns slices of MB by parsing MB as an archive file.
@@ -87,25 +101,28 @@ static std::pair<ELFKind, uint16_t> parseEmulation(StringRef S) {
std::vector<MemoryBufferRef>
LinkerDriver::getArchiveMembers(MemoryBufferRef MB) {
std::unique_ptr<Archive> File =
- check(Archive::create(MB), "failed to parse archive");
+ check(Archive::create(MB),
+ MB.getBufferIdentifier() + ": failed to parse archive");
std::vector<MemoryBufferRef> V;
- Error Err;
+ Error Err = Error::success();
for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
- Archive::Child C = check(COrErr, "could not get the child of the archive " +
- File->getFileName());
+ Archive::Child C =
+ check(COrErr, MB.getBufferIdentifier() +
+ ": could not get the child of the archive");
MemoryBufferRef MBRef =
check(C.getMemoryBufferRef(),
- "could not get the buffer for a child of the archive " +
- File->getFileName());
+ MB.getBufferIdentifier() +
+ ": could not get the buffer for a child of the archive");
V.push_back(MBRef);
}
if (Err)
- Error(Err);
+ fatal(MB.getBufferIdentifier() + ": Archive::children failed: " +
+ toString(std::move(Err)));
// Take ownership of memory buffers created for members of thin archives.
for (std::unique_ptr<MemoryBuffer> &MB : File->takeThinBuffers())
- OwningMBs.push_back(std::move(MB));
+ make<std::unique_ptr<MemoryBuffer>>(std::move(MB));
return V;
}
@@ -114,25 +131,28 @@ LinkerDriver::getArchiveMembers(MemoryBufferRef MB) {
// Newly created memory buffers are owned by this driver.
void LinkerDriver::addFile(StringRef Path) {
using namespace sys::fs;
- if (Config->Verbose)
- outs() << Path << "\n";
Optional<MemoryBufferRef> Buffer = readFile(Path);
if (!Buffer.hasValue())
return;
MemoryBufferRef MBRef = *Buffer;
+ if (InBinary) {
+ Files.push_back(make<BinaryFile>(MBRef));
+ return;
+ }
+
switch (identify_magic(MBRef.getBuffer())) {
case file_magic::unknown:
readLinkerScript(MBRef);
return;
case file_magic::archive:
- if (WholeArchive) {
+ if (InWholeArchive) {
for (MemoryBufferRef MB : getArchiveMembers(MBRef))
Files.push_back(createObjectFile(MB, Path));
return;
}
- Files.push_back(make_unique<ArchiveFile>(MBRef));
+ Files.push_back(make<ArchiveFile>(MBRef));
return;
case file_magic::elf_shared_object:
if (Config->Relocatable) {
@@ -143,13 +163,16 @@ void LinkerDriver::addFile(StringRef Path) {
return;
default:
if (InLib)
- Files.push_back(make_unique<LazyObjectFile>(MBRef));
+ Files.push_back(make<LazyObjectFile>(MBRef));
else
Files.push_back(createObjectFile(MBRef));
}
}
Optional<MemoryBufferRef> LinkerDriver::readFile(StringRef Path) {
+ if (Config->Verbose)
+ outs() << Path << "\n";
+
auto MBOrErr = MemoryBuffer::getFile(Path);
if (auto EC = MBOrErr.getError()) {
error(EC, "cannot open " + Path);
@@ -157,7 +180,7 @@ Optional<MemoryBufferRef> LinkerDriver::readFile(StringRef Path) {
}
std::unique_ptr<MemoryBuffer> &MB = *MBOrErr;
MemoryBufferRef MBRef = MB->getMemBufferRef();
- OwningMBs.push_back(std::move(MB)); // take MB ownership
+ make<std::unique_ptr<MemoryBuffer>>(std::move(MB)); // take MB ownership
if (Cpio)
Cpio->append(relativeToRoot(Path), MBRef.getBuffer());
@@ -167,11 +190,10 @@ Optional<MemoryBufferRef> LinkerDriver::readFile(StringRef Path) {
// Add a given library by searching it from input search paths.
void LinkerDriver::addLibrary(StringRef Name) {
- std::string Path = searchLibrary(Name);
- if (Path.empty())
- error("unable to find library -l" + Name);
+ if (Optional<std::string> Path = searchLibrary(Name))
+ addFile(*Path);
else
- addFile(Path);
+ error("unable to find library -l" + Name);
}
// This function is called on startup. We need this for LTO since
@@ -184,12 +206,6 @@ static void initLLVM(opt::InputArgList &Args) {
InitializeAllAsmPrinters();
InitializeAllAsmParsers();
- // This is a flag to discard all but GlobalValue names.
- // We want to enable it by default because it saves memory.
- // Disable it only when a developer option (-save-temps) is given.
- Driver->Context.setDiscardValueNames(!Config->SaveTemps);
- Driver->Context.enableDebugTypeODRUniquing();
-
// Parse and evaluate -mllvm options.
std::vector<const char *> V;
V.push_back("lld (LLVM option parsing)");
@@ -206,9 +222,6 @@ static void checkOptions(opt::InputArgList &Args) {
if (Config->EMachine == EM_MIPS && Config->GnuHash)
error("the .gnu.hash section is not compatible with the MIPS target.");
- if (Config->EMachine == EM_AMDGPU && !Config->Entry.empty())
- error("-e option is not valid for AMDGPU.");
-
if (Config->Pie && Config->Shared)
error("-shared and -pie may not be used together");
@@ -224,8 +237,8 @@ static void checkOptions(opt::InputArgList &Args) {
}
}
-static StringRef
-getString(opt::InputArgList &Args, unsigned Key, StringRef Default = "") {
+static StringRef getString(opt::InputArgList &Args, unsigned Key,
+ StringRef Default = "") {
if (auto *Arg = Args.getLastArg(Key))
return Arg->getValue();
return Default;
@@ -254,33 +267,64 @@ static bool hasZOption(opt::InputArgList &Args, StringRef Key) {
return false;
}
-void LinkerDriver::main(ArrayRef<const char *> ArgsArr) {
+static uint64_t getZOptionValue(opt::InputArgList &Args, StringRef Key,
+ uint64_t Default) {
+ for (auto *Arg : Args.filtered(OPT_z)) {
+ StringRef Value = Arg->getValue();
+ size_t Pos = Value.find("=");
+ if (Pos != StringRef::npos && Key == Value.substr(0, Pos)) {
+ Value = Value.substr(Pos + 1);
+ uint64_t Result;
+ if (Value.getAsInteger(0, Result))
+ error("invalid " + Key + ": " + Value);
+ return Result;
+ }
+ }
+ return Default;
+}
+
+void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
ELFOptTable Parser;
opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
+
+ // Interpret this flag early because error() depends on them.
+ Config->ErrorLimit = getInteger(Args, OPT_error_limit, 20);
+
+ // Handle -help
if (Args.hasArg(OPT_help)) {
printHelp(ArgsArr[0]);
return;
}
- if (Args.hasArg(OPT_version)) {
- outs() << getVersionString();
+
+ // GNU linkers disagree here. Though both -version and -v are mentioned
+ // in help to print the version information, GNU ld just normally exits,
+ // while gold can continue linking. We are compatible with ld.bfd here.
+ if (Args.hasArg(OPT_version) || Args.hasArg(OPT_v))
+ outs() << getLLDVersion() << "\n";
+ if (Args.hasArg(OPT_version))
return;
- }
+
+ Config->ExitEarly = CanExitEarly && !Args.hasArg(OPT_full_shutdown);
if (const char *Path = getReproduceOption(Args)) {
// Note that --reproduce is a debug option so you can ignore it
// if you are trying to understand the whole picture of the code.
- Cpio.reset(CpioFile::create(Path));
- if (Cpio) {
+ ErrorOr<CpioFile *> F = CpioFile::create(Path);
+ if (F) {
+ Cpio.reset(*F);
Cpio->append("response.txt", createResponseFile(Args));
- Cpio->append("version.txt", getVersionString());
- }
+ Cpio->append("version.txt", getLLDVersion() + "\n");
+ } else
+ error(F.getError(),
+ Twine("--reproduce: failed to open ") + Path + ".cpio");
}
readConfigs(Args);
initLLVM(Args);
createFiles(Args);
+ inferMachineType();
checkOptions(Args);
- if (HasError)
+ if (ErrorCount)
return;
switch (Config->EKind) {
@@ -297,7 +341,7 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr) {
link<ELF64BE>(Args);
return;
default:
- error("-m or at least a .o file required");
+ llvm_unreachable("unknown Config->EKind");
}
}
@@ -314,10 +358,115 @@ static UnresolvedPolicy getUnresolvedSymbolOption(opt::InputArgList &Args) {
if (S == "ignore-all" || S == "ignore-in-object-files")
return UnresolvedPolicy::Ignore;
if (S == "ignore-in-shared-libs" || S == "report-all")
- return UnresolvedPolicy::Error;
+ return UnresolvedPolicy::ReportError;
error("unknown --unresolved-symbols value: " + S);
}
- return UnresolvedPolicy::Error;
+ return UnresolvedPolicy::ReportError;
+}
+
+static Target2Policy getTarget2Option(opt::InputArgList &Args) {
+ if (auto *Arg = Args.getLastArg(OPT_target2)) {
+ StringRef S = Arg->getValue();
+ if (S == "rel")
+ return Target2Policy::Rel;
+ if (S == "abs")
+ return Target2Policy::Abs;
+ if (S == "got-rel")
+ return Target2Policy::GotRel;
+ error("unknown --target2 option: " + S);
+ }
+ return Target2Policy::GotRel;
+}
+
+static bool isOutputFormatBinary(opt::InputArgList &Args) {
+ if (auto *Arg = Args.getLastArg(OPT_oformat)) {
+ StringRef S = Arg->getValue();
+ if (S == "binary")
+ return true;
+ error("unknown --oformat value: " + S);
+ }
+ return false;
+}
+
+static bool getArg(opt::InputArgList &Args, unsigned K1, unsigned K2,
+ bool Default) {
+ if (auto *Arg = Args.getLastArg(K1, K2))
+ return Arg->getOption().getID() == K1;
+ return Default;
+}
+
+static DiscardPolicy getDiscardOption(opt::InputArgList &Args) {
+ if (Config->Relocatable)
+ return DiscardPolicy::None;
+ auto *Arg =
+ Args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none);
+ if (!Arg)
+ return DiscardPolicy::Default;
+ if (Arg->getOption().getID() == OPT_discard_all)
+ return DiscardPolicy::All;
+ if (Arg->getOption().getID() == OPT_discard_locals)
+ return DiscardPolicy::Locals;
+ return DiscardPolicy::None;
+}
+
+static StripPolicy getStripOption(opt::InputArgList &Args) {
+ if (auto *Arg = Args.getLastArg(OPT_strip_all, OPT_strip_debug)) {
+ if (Arg->getOption().getID() == OPT_strip_all)
+ return StripPolicy::All;
+ return StripPolicy::Debug;
+ }
+ return StripPolicy::None;
+}
+
+static uint64_t parseSectionAddress(StringRef S, opt::Arg *Arg) {
+ uint64_t VA = 0;
+ if (S.startswith("0x"))
+ S = S.drop_front(2);
+ if (S.getAsInteger(16, VA))
+ error("invalid argument: " + stringize(Arg));
+ return VA;
+}
+
+static StringMap<uint64_t> getSectionStartMap(opt::InputArgList &Args) {
+ StringMap<uint64_t> Ret;
+ for (auto *Arg : Args.filtered(OPT_section_start)) {
+ StringRef Name;
+ StringRef Addr;
+ std::tie(Name, Addr) = StringRef(Arg->getValue()).split('=');
+ Ret[Name] = parseSectionAddress(Addr, Arg);
+ }
+
+ if (auto *Arg = Args.getLastArg(OPT_Ttext))
+ Ret[".text"] = parseSectionAddress(Arg->getValue(), Arg);
+ if (auto *Arg = Args.getLastArg(OPT_Tdata))
+ Ret[".data"] = parseSectionAddress(Arg->getValue(), Arg);
+ if (auto *Arg = Args.getLastArg(OPT_Tbss))
+ Ret[".bss"] = parseSectionAddress(Arg->getValue(), Arg);
+ return Ret;
+}
+
+static SortSectionPolicy getSortKind(opt::InputArgList &Args) {
+ StringRef S = getString(Args, OPT_sort_section);
+ if (S == "alignment")
+ return SortSectionPolicy::Alignment;
+ if (S == "name")
+ return SortSectionPolicy::Name;
+ if (!S.empty())
+ error("unknown --sort-section rule: " + S);
+ return SortSectionPolicy::Default;
+}
+
+static std::vector<StringRef> getLines(MemoryBufferRef MB) {
+ SmallVector<StringRef, 0> Arr;
+ MB.getBuffer().split(Arr, '\n');
+
+ std::vector<StringRef> Ret;
+ for (StringRef S : Arr) {
+ S = S.trim();
+ if (!S.empty())
+ Ret.push_back(S);
+ }
+ return Ret;
}
// Initializes Config members by the command line options.
@@ -334,34 +483,37 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
if (auto *Arg = Args.getLastArg(OPT_m)) {
// Parse ELF{32,64}{LE,BE} and CPU type.
StringRef S = Arg->getValue();
- std::tie(Config->EKind, Config->EMachine) = parseEmulation(S);
+ std::tie(Config->EKind, Config->EMachine, Config->OSABI) =
+ parseEmulation(S);
+ Config->MipsN32Abi = (S == "elf32btsmipn32" || S == "elf32ltsmipn32");
Config->Emulation = S;
}
Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition);
Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic);
Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions);
- Config->Demangle = !Args.hasArg(OPT_no_demangle);
+ Config->Demangle = getArg(Args, OPT_demangle, OPT_no_demangle, true);
Config->DisableVerify = Args.hasArg(OPT_disable_verify);
- Config->DiscardAll = Args.hasArg(OPT_discard_all);
- Config->DiscardLocals = Args.hasArg(OPT_discard_locals);
- Config->DiscardNone = Args.hasArg(OPT_discard_none);
Config->EhFrameHdr = Args.hasArg(OPT_eh_frame_hdr);
Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags);
Config->ExportDynamic = Args.hasArg(OPT_export_dynamic);
Config->FatalWarnings = Args.hasArg(OPT_fatal_warnings);
- Config->GcSections = Args.hasArg(OPT_gc_sections);
+ Config->GcSections = getArg(Args, OPT_gc_sections, OPT_no_gc_sections, false);
+ Config->GdbIndex = Args.hasArg(OPT_gdb_index);
Config->ICF = Args.hasArg(OPT_icf);
Config->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique);
Config->NoUndefinedVersion = Args.hasArg(OPT_no_undefined_version);
- Config->Pie = Args.hasArg(OPT_pie);
+ Config->Nostdlib = Args.hasArg(OPT_nostdlib);
+ Config->OMagic = Args.hasArg(OPT_omagic);
+ Config->Pie = getArg(Args, OPT_pie, OPT_nopie, false);
Config->PrintGcSections = Args.hasArg(OPT_print_gc_sections);
Config->Relocatable = Args.hasArg(OPT_relocatable);
+ Config->Discard = getDiscardOption(Args);
Config->SaveTemps = Args.hasArg(OPT_save_temps);
+ Config->SingleRoRx = Args.hasArg(OPT_no_rosegment);
Config->Shared = Args.hasArg(OPT_shared);
- Config->StripAll = Args.hasArg(OPT_strip_all);
- Config->StripDebug = Args.hasArg(OPT_strip_debug);
- Config->Threads = Args.hasArg(OPT_threads);
+ Config->Target1Rel = getArg(Args, OPT_target1_rel, OPT_target1_abs, false);
+ Config->Threads = getArg(Args, OPT_threads, OPT_no_threads, true);
Config->Trace = Args.hasArg(OPT_trace);
Config->Verbose = Args.hasArg(OPT_verbose);
Config->WarnCommon = Args.hasArg(OPT_warn_common);
@@ -370,33 +522,47 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
Config->Entry = getString(Args, OPT_entry);
Config->Fini = getString(Args, OPT_fini, "_fini");
Config->Init = getString(Args, OPT_init, "_init");
- Config->LtoAAPipeline = getString(Args, OPT_lto_aa_pipeline);
- Config->LtoNewPmPasses = getString(Args, OPT_lto_newpm_passes);
+ Config->LTOAAPipeline = getString(Args, OPT_lto_aa_pipeline);
+ Config->LTONewPmPasses = getString(Args, OPT_lto_newpm_passes);
Config->OutputFile = getString(Args, OPT_o);
Config->SoName = getString(Args, OPT_soname);
Config->Sysroot = getString(Args, OPT_sysroot);
Config->Optimize = getInteger(Args, OPT_O, 1);
- Config->LtoO = getInteger(Args, OPT_lto_O, 2);
- if (Config->LtoO > 3)
+ Config->LTOO = getInteger(Args, OPT_lto_O, 2);
+ if (Config->LTOO > 3)
error("invalid optimization level for LTO: " + getString(Args, OPT_lto_O));
- Config->LtoJobs = getInteger(Args, OPT_lto_jobs, 1);
- if (Config->LtoJobs == 0)
- error("number of threads must be > 0");
+ Config->LTOPartitions = getInteger(Args, OPT_lto_partitions, 1);
+ if (Config->LTOPartitions == 0)
+ error("--lto-partitions: number of threads must be > 0");
+ Config->ThinLTOJobs = getInteger(Args, OPT_thinlto_jobs, -1u);
+ if (Config->ThinLTOJobs == 0)
+ error("--thinlto-jobs: number of threads must be > 0");
Config->ZCombreloc = !hasZOption(Args, "nocombreloc");
- Config->ZExecStack = hasZOption(Args, "execstack");
+ Config->ZExecstack = hasZOption(Args, "execstack");
Config->ZNodelete = hasZOption(Args, "nodelete");
Config->ZNow = hasZOption(Args, "now");
Config->ZOrigin = hasZOption(Args, "origin");
Config->ZRelro = !hasZOption(Args, "norelro");
+ Config->ZStackSize = getZOptionValue(Args, "stack-size", -1);
+ Config->ZWxneeded = hasZOption(Args, "wxneeded");
- if (Config->Relocatable)
- Config->StripAll = false;
+ Config->OFormatBinary = isOutputFormatBinary(Args);
+ Config->SectionStartMap = getSectionStartMap(Args);
+ Config->SortSection = getSortKind(Args);
+ Config->Target2 = getTarget2Option(Args);
+ Config->UnresolvedSymbols = getUnresolvedSymbolOption(Args);
+
+ // --omagic is an option to create old-fashioned executables in which
+ // .text segments are writable. Today, the option is still in use to
+ // create special-purpose programs such as boot loaders. It doesn't
+ // make sense to create PT_GNU_RELRO for such executables.
+ if (Config->OMagic)
+ Config->ZRelro = false;
- // --strip-all implies --strip-debug.
- if (Config->StripAll)
- Config->StripDebug = true;
+ if (!Config->Relocatable)
+ Config->Strip = getStripOption(Args);
// Config->Pic is true if we are generating position-independent code.
Config->Pic = Config->Pie || Config->Shared;
@@ -414,13 +580,15 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
// Parse --build-id or --build-id=<style>.
if (Args.hasArg(OPT_build_id))
- Config->BuildId = BuildIdKind::Fnv1;
+ Config->BuildId = BuildIdKind::Fast;
if (auto *Arg = Args.getLastArg(OPT_build_id_eq)) {
StringRef S = Arg->getValue();
if (S == "md5") {
Config->BuildId = BuildIdKind::Md5;
- } else if (S == "sha1") {
+ } else if (S == "sha1" || S == "tree") {
Config->BuildId = BuildIdKind::Sha1;
+ } else if (S == "uuid") {
+ Config->BuildId = BuildIdKind::Uuid;
} else if (S == "none") {
Config->BuildId = BuildIdKind::None;
} else if (S.startswith("0x")) {
@@ -431,21 +599,58 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
}
}
+ for (auto *Arg : Args.filtered(OPT_auxiliary))
+ Config->AuxiliaryList.push_back(Arg->getValue());
+ if (!Config->Shared && !Config->AuxiliaryList.empty())
+ error("-f may not be used without -shared");
+
for (auto *Arg : Args.filtered(OPT_undefined))
Config->Undefined.push_back(Arg->getValue());
- Config->UnresolvedSymbols = getUnresolvedSymbolOption(Args);
-
if (auto *Arg = Args.getLastArg(OPT_dynamic_list))
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
- parseDynamicList(*Buffer);
+ readDynamicList(*Buffer);
+
+ if (auto *Arg = Args.getLastArg(OPT_symbol_ordering_file))
+ if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
+ Config->SymbolOrderingFile = getLines(*Buffer);
+
+ // If --retain-symbol-file is used, we'll retail only the symbols listed in
+ // the file and discard all others.
+ if (auto *Arg = Args.getLastArg(OPT_retain_symbols_file)) {
+ Config->Discard = DiscardPolicy::RetainFile;
+ if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
+ for (StringRef S : getLines(*Buffer))
+ Config->RetainSymbolsFile.insert(S);
+ }
for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol))
- Config->DynamicList.push_back(Arg->getValue());
+ Config->VersionScriptGlobals.push_back(
+ {Arg->getValue(), /*IsExternCpp*/ false, /*HasWildcard*/ false});
+
+ // Dynamic lists are a simplified linker script that doesn't need the
+ // "global:" and implicitly ends with a "local:*". Set the variables needed to
+ // simulate that.
+ if (Args.hasArg(OPT_dynamic_list) || Args.hasArg(OPT_export_dynamic_symbol)) {
+ Config->ExportDynamic = true;
+ if (!Config->Shared)
+ Config->DefaultSymbolVersion = VER_NDX_LOCAL;
+ }
if (auto *Arg = Args.getLastArg(OPT_version_script))
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
- parseVersionScript(*Buffer);
+ readVersionScript(*Buffer);
+}
+
+// Returns a value of "-format" option.
+static bool getBinaryOption(StringRef S) {
+ if (S == "binary")
+ return true;
+ if (S == "elf" || S == "default")
+ return false;
+ error("unknown -format value: " + S +
+ " (supported formats: elf, default, binary)");
+ return false;
}
void LinkerDriver::createFiles(opt::InputArgList &Args) {
@@ -454,14 +659,20 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
case OPT_l:
addLibrary(Arg->getValue());
break;
- case OPT_alias_script_T:
case OPT_INPUT:
- case OPT_script:
addFile(Arg->getValue());
break;
+ case OPT_alias_script_T:
+ case OPT_script:
+ if (Optional<MemoryBufferRef> MB = readFile(Arg->getValue()))
+ readLinkerScript(*MB);
+ break;
case OPT_as_needed:
Config->AsNeeded = true;
break;
+ case OPT_format:
+ InBinary = getBinaryOption(Arg->getValue());
+ break;
case OPT_no_as_needed:
Config->AsNeeded = false;
break;
@@ -472,10 +683,10 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
Config->Static = false;
break;
case OPT_whole_archive:
- WholeArchive = true;
+ InWholeArchive = true;
break;
case OPT_no_whole_archive:
- WholeArchive = false;
+ InWholeArchive = false;
break;
case OPT_start_lib:
InLib = true;
@@ -486,19 +697,55 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
}
}
- if (Files.empty() && !HasError)
- error("no input files.");
+ if (Files.empty() && ErrorCount == 0)
+ error("no input files");
+}
- // If -m <machine_type> was not given, infer it from object files.
- if (Config->EKind == ELFNoneKind) {
- for (std::unique_ptr<InputFile> &F : Files) {
- if (F->EKind == ELFNoneKind)
- continue;
- Config->EKind = F->EKind;
- Config->EMachine = F->EMachine;
- break;
- }
+// If -m <machine_type> was not given, infer it from object files.
+void LinkerDriver::inferMachineType() {
+ if (Config->EKind != ELFNoneKind)
+ return;
+
+ for (InputFile *F : Files) {
+ if (F->EKind == ELFNoneKind)
+ continue;
+ Config->EKind = F->EKind;
+ Config->EMachine = F->EMachine;
+ Config->OSABI = F->OSABI;
+ Config->MipsN32Abi = Config->EMachine == EM_MIPS && isMipsN32Abi(F);
+ return;
}
+ error("target emulation unknown: -m or at least one .o file required");
+}
+
+// Parse -z max-page-size=<value>. The default value is defined by
+// each target.
+static uint64_t getMaxPageSize(opt::InputArgList &Args) {
+ uint64_t Val =
+ getZOptionValue(Args, "max-page-size", Target->DefaultMaxPageSize);
+ if (!isPowerOf2_64(Val))
+ error("max-page-size: value isn't a power of 2");
+ return Val;
+}
+
+// Parses -image-base option.
+static uint64_t getImageBase(opt::InputArgList &Args) {
+ // Use default if no -image-base option is given.
+ // Because we are using "Target" here, this function
+ // has to be called after the variable is initialized.
+ auto *Arg = Args.getLastArg(OPT_image_base);
+ if (!Arg)
+ return Config->Pic ? 0 : Target->DefaultImageBase;
+
+ StringRef S = Arg->getValue();
+ uint64_t V;
+ if (S.getAsInteger(0, V)) {
+ error("-image-base: number expected, but got " + S);
+ return 0;
+ }
+ if ((V % Config->MaxPageSize) != 0)
+ warn("-image-base: address isn't multiple of page size: " + S);
+ return V;
}
// Do actual linking. Note that when this function is called,
@@ -506,66 +753,70 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
SymbolTable<ELFT> Symtab;
elf::Symtab<ELFT>::X = &Symtab;
+ Target = createTarget();
+ ScriptBase = Script<ELFT>::X = make<LinkerScript<ELFT>>();
- std::unique_ptr<TargetInfo> TI(createTarget());
- Target = TI.get();
- LinkerScript<ELFT> LS;
- Script<ELFT>::X = &LS;
-
- Config->Rela = ELFT::Is64Bits || Config->EMachine == EM_X86_64;
+ Config->Rela =
+ ELFT::Is64Bits || Config->EMachine == EM_X86_64 || Config->MipsN32Abi;
Config->Mips64EL =
(Config->EMachine == EM_MIPS && Config->EKind == ELF64LEKind);
-
- // Add entry symbol. Note that AMDGPU binaries have no entry points.
- if (Config->Entry.empty() && !Config->Shared && !Config->Relocatable &&
- Config->EMachine != EM_AMDGPU)
- Config->Entry = (Config->EMachine == EM_MIPS) ? "__start" : "_start";
+ Config->MaxPageSize = getMaxPageSize(Args);
+ Config->ImageBase = getImageBase(Args);
// Default output filename is "a.out" by the Unix tradition.
if (Config->OutputFile.empty())
Config->OutputFile = "a.out";
+ // Use default entry point name if no name was given via the command
+ // line nor linker scripts. For some reason, MIPS entry point name is
+ // different from others.
+ Config->WarnMissingEntry =
+ (!Config->Entry.empty() || (!Config->Shared && !Config->Relocatable));
+ if (Config->Entry.empty() && !Config->Relocatable)
+ Config->Entry = (Config->EMachine == EM_MIPS) ? "__start" : "_start";
+
// Handle --trace-symbol.
for (auto *Arg : Args.filtered(OPT_trace_symbol))
Symtab.trace(Arg->getValue());
- // Set either EntryAddr (if S is a number) or EntrySym (otherwise).
- if (!Config->Entry.empty()) {
- StringRef S = Config->Entry;
- if (S.getAsInteger(0, Config->EntryAddr))
- Config->EntrySym = Symtab.addUndefined(S);
- }
+ // Add all files to the symbol table. This will add almost all
+ // symbols that we need to the symbol table.
+ for (InputFile *F : Files)
+ Symtab.addFile(F);
- // Initialize Config->ImageBase.
- if (auto *Arg = Args.getLastArg(OPT_image_base)) {
- StringRef S = Arg->getValue();
- if (S.getAsInteger(0, Config->ImageBase))
- error(Arg->getSpelling() + ": number expected, but got " + S);
- else if ((Config->ImageBase % Target->PageSize) != 0)
- warning(Arg->getSpelling() + ": address isn't multiple of page size");
- } else {
- Config->ImageBase = Config->Pic ? 0 : Target->DefaultImageBase;
- }
+ // If an entry symbol is in a static archive, pull out that file now
+ // to complete the symbol table. After this, no new names except a
+ // few linker-synthesized ones will be added to the symbol table.
+ if (Symtab.find(Config->Entry))
+ Symtab.addUndefined(Config->Entry);
- for (std::unique_ptr<InputFile> &F : Files)
- Symtab.addFile(std::move(F));
- if (HasError)
- return; // There were duplicate symbols or incompatible files
+ // Return if there were name resolution errors.
+ if (ErrorCount)
+ return;
Symtab.scanUndefinedFlags();
Symtab.scanShlibUndefined();
- Symtab.scanDynamicList();
Symtab.scanVersionScript();
- Symtab.scanSymbolVersions();
- Symtab.addCombinedLtoObject();
- if (HasError)
+ Symtab.addCombinedLTOObject();
+ if (ErrorCount)
return;
for (auto *Arg : Args.filtered(OPT_wrap))
Symtab.wrap(Arg->getValue());
- // Write the result to the file.
+ // Now that we have a complete list of input files.
+ // Beyond this point, no new files are added.
+ // Aggregate all input sections into one place.
+ for (elf::ObjectFile<ELFT> *F : Symtab.getObjectFiles())
+ for (InputSectionBase<ELFT> *S : F->getSections())
+ if (S && S != &InputSection<ELFT>::Discarded)
+ Symtab.Sections.push_back(S);
+ for (BinaryFile *F : Symtab.getBinaryFiles())
+ for (InputSectionData *S : F->getSections())
+ Symtab.Sections.push_back(cast<InputSection<ELFT>>(S));
+
+ // Do size optimizations: garbage collection and identical code folding.
if (Config->GcSections)
markLive<ELFT>();
if (Config->ICF)
@@ -573,16 +824,16 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// MergeInputSection::splitIntoPieces needs to be called before
// any call of MergeInputSection::getOffset. Do that.
- for (const std::unique_ptr<elf::ObjectFile<ELFT>> &F :
- Symtab.getObjectFiles())
- for (InputSectionBase<ELFT> *S : F->getSections()) {
- if (!S || S == &InputSection<ELFT>::Discarded || !S->Live)
- continue;
- if (S->Compressed)
- S->uncompress();
- if (auto *MS = dyn_cast<MergeInputSection<ELFT>>(S))
- MS->splitIntoPieces();
- }
+ forEach(Symtab.Sections.begin(), Symtab.Sections.end(),
+ [](InputSectionBase<ELFT> *S) {
+ if (!S->Live)
+ return;
+ if (S->isCompressed())
+ S->uncompress();
+ if (auto *MS = dyn_cast<MergeInputSection<ELFT>>(S))
+ MS->splitIntoPieces();
+ });
- writeResult<ELFT>(&Symtab);
+ // Write the result to the file.
+ writeResult<ELFT>();
}
diff --git a/ELF/Driver.h b/ELF/Driver.h
index 6b9b9bb208e5..cba1eb164fdd 100644
--- a/ELF/Driver.h
+++ b/ELF/Driver.h
@@ -12,6 +12,7 @@
#include "SymbolTable.h"
#include "lld/Core/LLVM.h"
+#include "lld/Core/Reproduce.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
@@ -23,14 +24,11 @@ namespace elf {
extern class LinkerDriver *Driver;
-class CpioFile;
-
class LinkerDriver {
public:
- void main(ArrayRef<const char *> Args);
+ void main(ArrayRef<const char *> Args, bool CanExitEarly);
void addFile(StringRef Path);
void addLibrary(StringRef Name);
- llvm::LLVMContext Context; // to parse bitcode ifles
std::unique_ptr<CpioFile> Cpio; // for reproduce
private:
@@ -38,17 +36,19 @@ private:
llvm::Optional<MemoryBufferRef> readFile(StringRef Path);
void readConfigs(llvm::opt::InputArgList &Args);
void createFiles(llvm::opt::InputArgList &Args);
+ void inferMachineType();
template <class ELFT> void link(llvm::opt::InputArgList &Args);
// True if we are in --whole-archive and --no-whole-archive.
- bool WholeArchive = false;
+ bool InWholeArchive = false;
// True if we are in --start-lib and --end-lib.
bool InLib = false;
- llvm::BumpPtrAllocator Alloc;
- std::vector<std::unique_ptr<InputFile>> Files;
- std::vector<std::unique_ptr<MemoryBuffer>> OwningMBs;
+ // True if we are in -format=binary and -format=elf.
+ bool InBinary = false;
+
+ std::vector<InputFile *> Files;
};
// Parses command line options.
@@ -56,9 +56,6 @@ class ELFOptTable : public llvm::opt::OptTable {
public:
ELFOptTable();
llvm::opt::InputArgList parse(ArrayRef<const char *> Argv);
-
-private:
- llvm::BumpPtrAllocator Alloc;
};
// Create enum with OPT_xxx values for each option in Options.td
@@ -69,41 +66,13 @@ enum {
#undef OPTION
};
-// This is the class to create a .cpio file for --reproduce.
-//
-// If "--reproduce foo" is given, we create a file "foo.cpio" and
-// copy all input files to the archive, along with a response file
-// to re-run the same command with the same inputs.
-// It is useful for reporting issues to LLD developers.
-//
-// Cpio as a file format is a deliberate choice. It's standardized in
-// POSIX and very easy to create. cpio command is available virtually
-// on all Unix systems. See
-// http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_07
-// for the format details.
-class CpioFile {
-public:
- static CpioFile *create(StringRef OutputPath);
- void append(StringRef Path, StringRef Data);
-
-private:
- CpioFile(std::unique_ptr<llvm::raw_fd_ostream> OS, StringRef Basename);
-
- std::unique_ptr<llvm::raw_fd_ostream> OS;
- llvm::StringSet<> Seen;
- std::string Basename;
-};
-
void printHelp(const char *Argv0);
-std::string getVersionString();
std::vector<uint8_t> parseHexstring(StringRef S);
std::string createResponseFile(const llvm::opt::InputArgList &Args);
-std::string relativeToRoot(StringRef Path);
-std::string findFromSearchPaths(StringRef Path);
-std::string searchLibrary(StringRef Path);
-std::string buildSysrootedPath(llvm::StringRef Dir, llvm::StringRef File);
+llvm::Optional<std::string> findFromSearchPaths(StringRef Path);
+llvm::Optional<std::string> searchLibrary(StringRef Path);
} // namespace elf
} // namespace lld
diff --git a/ELF/DriverUtils.cpp b/ELF/DriverUtils.cpp
index 3f18259b4ae7..a81b133f674b 100644
--- a/ELF/DriverUtils.cpp
+++ b/ELF/DriverUtils.cpp
@@ -15,14 +15,18 @@
#include "Driver.h"
#include "Error.h"
+#include "Memory.h"
+#include "ScriptParser.h"
#include "lld/Config/Version.h"
+#include "lld/Core/Reproduce.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/StringSaver.h"
+#include "llvm/Support/Process.h"
using namespace llvm;
using namespace llvm::sys;
@@ -40,16 +44,37 @@ using namespace lld::elf;
// Create table mapping all options defined in Options.td
static const opt::OptTable::Info OptInfo[] = {
#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X6, X7, X8, X9, X10) \
- { \
- X1, X2, X9, X10, OPT_##ID, opt::Option::KIND##Class, X8, X7, OPT_##GROUP, \
- OPT_##ALIAS, X6 \
- },
+ {X1, X2, X9, X10, OPT_##ID, opt::Option::KIND##Class, \
+ X8, X7, OPT_##GROUP, OPT_##ALIAS, X6},
#include "Options.inc"
#undef OPTION
};
ELFOptTable::ELFOptTable() : OptTable(OptInfo) {}
+// Parse -color-diagnostics={auto,always,never} or -no-color-diagnostics.
+static bool getColorDiagnostics(opt::InputArgList &Args) {
+ bool Default = (ErrorOS == &errs() && Process::StandardErrHasColors());
+
+ auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
+ OPT_no_color_diagnostics);
+ if (!Arg)
+ return Default;
+ if (Arg->getOption().getID() == OPT_color_diagnostics)
+ return true;
+ if (Arg->getOption().getID() == OPT_no_color_diagnostics)
+ return false;
+
+ StringRef S = Arg->getValue();
+ if (S == "auto")
+ return Default;
+ if (S == "always")
+ return true;
+ if (S != "never")
+ error("unknown option: -color-diagnostics=" + S);
+ return false;
+}
+
static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) {
if (auto *Arg = Args.getLastArg(OPT_rsp_quoting)) {
StringRef S = Arg->getValue();
@@ -76,16 +101,16 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
// --rsp-quoting.
opt::InputArgList Args = this->ParseArgs(Vec, MissingIndex, MissingCount);
- // Expand response files. '@<filename>' is replaced by the file's contents.
- StringSaver Saver(Alloc);
+ // Expand response files (arguments in the form of @<filename>)
+ // and then parse the argument again.
cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec);
-
- // Parse options and then do error checking.
Args = this->ParseArgs(Vec, MissingIndex, MissingCount);
+
+ // Interpret -color-diagnostics early so that error messages
+ // for unknown flags are colored.
+ Config->ColorDiagnostics = getColorDiagnostics(Args);
if (MissingCount)
- error(Twine("missing arg value for \"") + Args.getArgString(MissingIndex) +
- "\", expected " + Twine(MissingCount) +
- (MissingCount == 1 ? " argument.\n" : " arguments"));
+ error(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
for (auto *Arg : Args.filtered(OPT_UNKNOWN))
error("unknown argument: " + Arg->getSpelling());
@@ -97,115 +122,6 @@ void elf::printHelp(const char *Argv0) {
Table.PrintHelp(outs(), Argv0, "lld", false);
}
-std::string elf::getVersionString() {
- std::string Version = getLLDVersion();
- std::string Repo = getLLDRepositoryVersion();
- if (Repo.empty())
- return "LLD " + Version + "\n";
- return "LLD " + Version + " " + Repo + "\n";
-}
-
-// Makes a given pathname an absolute path first, and then remove
-// beginning /. For example, "../foo.o" is converted to "home/john/foo.o",
-// assuming that the current directory is "/home/john/bar".
-std::string elf::relativeToRoot(StringRef Path) {
- SmallString<128> Abs = Path;
- if (std::error_code EC = fs::make_absolute(Abs))
- fatal("make_absolute failed: " + EC.message());
- path::remove_dots(Abs, /*remove_dot_dot=*/true);
-
- // This is Windows specific. root_name() returns a drive letter
- // (e.g. "c:") or a UNC name (//net). We want to keep it as part
- // of the result.
- SmallString<128> Res;
- StringRef Root = path::root_name(Abs);
- if (Root.endswith(":"))
- Res = Root.drop_back();
- else if (Root.startswith("//"))
- Res = Root.substr(2);
-
- path::append(Res, path::relative_path(Abs));
- return Res.str();
-}
-
-CpioFile::CpioFile(std::unique_ptr<raw_fd_ostream> OS, StringRef S)
- : OS(std::move(OS)), Basename(S) {}
-
-CpioFile *CpioFile::create(StringRef OutputPath) {
- std::string Path = (OutputPath + ".cpio").str();
- std::error_code EC;
- auto OS = llvm::make_unique<raw_fd_ostream>(Path, EC, fs::F_None);
- if (EC) {
- error(EC, "--reproduce: failed to open " + Path);
- return nullptr;
- }
- return new CpioFile(std::move(OS), path::filename(OutputPath));
-}
-
-static void writeMember(raw_fd_ostream &OS, StringRef Path, StringRef Data) {
- // The c_dev/c_ino pair should be unique according to the spec,
- // but no one seems to care.
- OS << "070707"; // c_magic
- OS << "000000"; // c_dev
- OS << "000000"; // c_ino
- OS << "100664"; // c_mode: C_ISREG | rw-rw-r--
- OS << "000000"; // c_uid
- OS << "000000"; // c_gid
- OS << "000001"; // c_nlink
- OS << "000000"; // c_rdev
- OS << "00000000000"; // c_mtime
- OS << format("%06o", Path.size() + 1); // c_namesize
- OS << format("%011o", Data.size()); // c_filesize
- OS << Path << '\0'; // c_name
- OS << Data; // c_filedata
-}
-
-void CpioFile::append(StringRef Path, StringRef Data) {
- if (!Seen.insert(Path).second)
- return;
-
- // Construct an in-archive filename so that /home/foo/bar is stored
- // as baz/home/foo/bar where baz is the basename of the output file.
- // (i.e. in that case we are creating baz.cpio.)
- SmallString<128> Fullpath;
- path::append(Fullpath, Basename, Path);
-
- // Use unix path separators so the cpio can be extracted on both unix and
- // windows.
- std::replace(Fullpath.begin(), Fullpath.end(), '\\', '/');
-
- writeMember(*OS, Fullpath, Data);
-
- // Print the trailer and seek back.
- // This way we have a valid archive if we crash.
- uint64_t Pos = OS->tell();
- writeMember(*OS, "TRAILER!!!", "");
- OS->seek(Pos);
-}
-
-// Quote a given string if it contains a space character.
-static std::string quote(StringRef S) {
- if (S.find(' ') == StringRef::npos)
- return S;
- return ("\"" + S + "\"").str();
-}
-
-static std::string rewritePath(StringRef S) {
- if (fs::exists(S))
- return relativeToRoot(S);
- return S;
-}
-
-static std::string stringize(opt::Arg *Arg) {
- std::string K = Arg->getSpelling();
- if (Arg->getNumValues() == 0)
- return K;
- std::string V = quote(Arg->getValue());
- if (Arg->getOption().getRenderStyle() == opt::Option::RenderJoinedStyle)
- return K + V;
- return K + " " + V;
-}
-
// Reconstructs command line arguments so that so that you can re-run
// the same command with the same inputs. This is for --reproduce.
std::string elf::createResponseFile(const opt::InputArgList &Args) {
@@ -226,8 +142,8 @@ std::string elf::createResponseFile(const opt::InputArgList &Args) {
case OPT_alias_script_T:
case OPT_script:
case OPT_version_script:
- OS << Arg->getSpelling() << " "
- << quote(rewritePath(Arg->getValue())) << "\n";
+ OS << Arg->getSpelling() << " " << quote(rewritePath(Arg->getValue()))
+ << "\n";
break;
default:
OS << stringize(Arg) << "\n";
@@ -236,41 +152,39 @@ std::string elf::createResponseFile(const opt::InputArgList &Args) {
return Data.str();
}
-std::string elf::findFromSearchPaths(StringRef Path) {
- for (StringRef Dir : Config->SearchPaths) {
- std::string FullPath = buildSysrootedPath(Dir, Path);
- if (fs::exists(FullPath))
- return FullPath;
- }
- return "";
+// Find a file by concatenating given paths. If a resulting path
+// starts with "=", the character is replaced with a --sysroot value.
+static Optional<std::string> findFile(StringRef Path1, const Twine &Path2) {
+ SmallString<128> S;
+ if (Path1.startswith("="))
+ path::append(S, Config->Sysroot, Path1.substr(1), Path2);
+ else
+ path::append(S, Path1, Path2);
+
+ if (fs::exists(S))
+ return S.str().str();
+ return None;
+}
+
+Optional<std::string> elf::findFromSearchPaths(StringRef Path) {
+ for (StringRef Dir : Config->SearchPaths)
+ if (Optional<std::string> S = findFile(Dir, Path))
+ return S;
+ return None;
}
-// Searches a given library from input search paths, which are filled
-// from -L command line switches. Returns a path to an existent library file.
-std::string elf::searchLibrary(StringRef Path) {
- if (Path.startswith(":"))
- return findFromSearchPaths(Path.substr(1));
+// This is for -lfoo. We'll look for libfoo.so or libfoo.a from
+// search paths.
+Optional<std::string> elf::searchLibrary(StringRef Name) {
+ if (Name.startswith(":"))
+ return findFromSearchPaths(Name.substr(1));
+
for (StringRef Dir : Config->SearchPaths) {
- if (!Config->Static) {
- std::string S = buildSysrootedPath(Dir, ("lib" + Path + ".so").str());
- if (fs::exists(S))
+ if (!Config->Static)
+ if (Optional<std::string> S = findFile(Dir, "lib" + Name + ".so"))
return S;
- }
- std::string S = buildSysrootedPath(Dir, ("lib" + Path + ".a").str());
- if (fs::exists(S))
+ if (Optional<std::string> S = findFile(Dir, "lib" + Name + ".a"))
return S;
}
- return "";
-}
-
-// Makes a path by concatenating Dir and File.
-// If Dir starts with '=' the result will be preceded by Sysroot,
-// which can be set with --sysroot command line switch.
-std::string elf::buildSysrootedPath(StringRef Dir, StringRef File) {
- SmallString<128> Path;
- if (Dir.startswith("="))
- path::append(Path, Config->Sysroot, Dir.substr(1), File);
- else
- path::append(Path, Dir, File);
- return Path.str();
+ return None;
}
diff --git a/ELF/EhFrame.cpp b/ELF/EhFrame.cpp
index b130ac1ca22d..2428473d9012 100644
--- a/ELF/EhFrame.cpp
+++ b/ELF/EhFrame.cpp
@@ -18,6 +18,9 @@
#include "EhFrame.h"
#include "Error.h"
+#include "InputSection.h"
+#include "Relocations.h"
+#include "Strings.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/Dwarf.h"
@@ -29,49 +32,93 @@ using namespace llvm::dwarf;
using namespace llvm::object;
using namespace llvm::support::endian;
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
+namespace {
+template <class ELFT> class EhReader {
+public:
+ EhReader(InputSectionBase<ELFT> *S, ArrayRef<uint8_t> D) : IS(S), D(D) {}
+ size_t readEhRecordSize();
+ uint8_t getFdeEncoding();
+
+private:
+ template <class P> void failOn(const P *Loc, const Twine &Msg) {
+ fatal(IS->getLocation((const uint8_t *)Loc - IS->Data.data()) + ": " + Msg);
+ }
+
+ uint8_t readByte();
+ void skipBytes(size_t Count);
+ StringRef readString();
+ void skipLeb128();
+ void skipAugP();
+
+ InputSectionBase<ELFT> *IS;
+ ArrayRef<uint8_t> D;
+};
+}
+
+template <class ELFT>
+size_t elf::readEhRecordSize(InputSectionBase<ELFT> *S, size_t Off) {
+ return EhReader<ELFT>(S, S->Data.slice(Off)).readEhRecordSize();
+}
// .eh_frame section is a sequence of records. Each record starts with
// a 4 byte length field. This function reads the length.
-template <class ELFT> size_t readEhRecordSize(ArrayRef<uint8_t> D) {
+template <class ELFT> size_t EhReader<ELFT>::readEhRecordSize() {
const endianness E = ELFT::TargetEndianness;
if (D.size() < 4)
- fatal("CIE/FDE too small");
+ failOn(D.data(), "CIE/FDE too small");
// First 4 bytes of CIE/FDE is the size of the record.
// If it is 0xFFFFFFFF, the next 8 bytes contain the size instead,
// but we do not support that format yet.
uint64_t V = read32<E>(D.data());
if (V == UINT32_MAX)
- fatal("CIE/FDE too large");
+ failOn(D.data(), "CIE/FDE too large");
uint64_t Size = V + 4;
if (Size > D.size())
- fatal("CIE/FIE ends past the end of the section");
+ failOn(D.data(), "CIE/FDE ends past the end of the section");
return Size;
}
// Read a byte and advance D by one byte.
-static uint8_t readByte(ArrayRef<uint8_t> &D) {
+template <class ELFT> uint8_t EhReader<ELFT>::readByte() {
if (D.empty())
- fatal("corrupted or unsupported CIE information");
+ failOn(D.data(), "unexpected end of CIE");
uint8_t B = D.front();
D = D.slice(1);
return B;
}
+template <class ELFT> void EhReader<ELFT>::skipBytes(size_t Count) {
+ if (D.size() < Count)
+ failOn(D.data(), "CIE is too small");
+ D = D.slice(Count);
+}
+
+// Read a null-terminated string.
+template <class ELFT> StringRef EhReader<ELFT>::readString() {
+ const uint8_t *End = std::find(D.begin(), D.end(), '\0');
+ if (End == D.end())
+ failOn(D.data(), "corrupted CIE (failed to read string)");
+ StringRef S = toStringRef(D.slice(0, End - D.begin()));
+ D = D.slice(S.size() + 1);
+ return S;
+}
+
// Skip an integer encoded in the LEB128 format.
// Actual number is not of interest because only the runtime needs it.
// But we need to be at least able to skip it so that we can read
// the field that follows a LEB128 number.
-static void skipLeb128(ArrayRef<uint8_t> &D) {
+template <class ELFT> void EhReader<ELFT>::skipLeb128() {
+ const uint8_t *ErrPos = D.data();
while (!D.empty()) {
uint8_t Val = D.front();
D = D.slice(1);
if ((Val & 0x80) == 0)
return;
}
- fatal("corrupted or unsupported CIE information");
+ failOn(ErrPos, "corrupted CIE (failed to read LEB128)");
}
template <class ELFT> static size_t getAugPSize(unsigned Enc) {
@@ -89,79 +136,79 @@ template <class ELFT> static size_t getAugPSize(unsigned Enc) {
case DW_EH_PE_sdata8:
return 8;
}
- fatal("unknown FDE encoding");
+ return 0;
}
-template <class ELFT> static void skipAugP(ArrayRef<uint8_t> &D) {
- uint8_t Enc = readByte(D);
+template <class ELFT> void EhReader<ELFT>::skipAugP() {
+ uint8_t Enc = readByte();
if ((Enc & 0xf0) == DW_EH_PE_aligned)
- fatal("DW_EH_PE_aligned encoding is not supported");
+ failOn(D.data() - 1, "DW_EH_PE_aligned encoding is not supported");
size_t Size = getAugPSize<ELFT>(Enc);
+ if (Size == 0)
+ failOn(D.data() - 1, "unknown FDE encoding");
if (Size >= D.size())
- fatal("corrupted CIE");
+ failOn(D.data() - 1, "corrupted CIE");
D = D.slice(Size);
}
-template <class ELFT> uint8_t getFdeEncoding(ArrayRef<uint8_t> D) {
- if (D.size() < 8)
- fatal("CIE too small");
- D = D.slice(8);
+template <class ELFT> uint8_t elf::getFdeEncoding(EhSectionPiece *P) {
+ auto *IS = static_cast<InputSectionBase<ELFT> *>(P->ID);
+ return EhReader<ELFT>(IS, P->data()).getFdeEncoding();
+}
- uint8_t Version = readByte(D);
+template <class ELFT> uint8_t EhReader<ELFT>::getFdeEncoding() {
+ skipBytes(8);
+ int Version = readByte();
if (Version != 1 && Version != 3)
- fatal("FDE version 1 or 3 expected, but got " + Twine((unsigned)Version));
+ failOn(D.data() - 1,
+ "FDE version 1 or 3 expected, but got " + Twine(Version));
- const unsigned char *AugEnd = std::find(D.begin(), D.end(), '\0');
- if (AugEnd == D.end())
- fatal("corrupted CIE");
- StringRef Aug(reinterpret_cast<const char *>(D.begin()), AugEnd - D.begin());
- D = D.slice(Aug.size() + 1);
+ StringRef Aug = readString();
- // Code alignment factor should always be 1 for .eh_frame.
- if (readByte(D) != 1)
- fatal("CIE code alignment must be 1");
-
- // Skip data alignment factor.
- skipLeb128(D);
+ // Skip code and data alignment factors.
+ skipLeb128();
+ skipLeb128();
// Skip the return address register. In CIE version 1 this is a single
// byte. In CIE version 3 this is an unsigned LEB128.
if (Version == 1)
- readByte(D);
+ readByte();
else
- skipLeb128(D);
+ skipLeb128();
// We only care about an 'R' value, but other records may precede an 'R'
// record. Unfortunately records are not in TLV (type-length-value) format,
// so we need to teach the linker how to skip records for each type.
for (char C : Aug) {
if (C == 'R')
- return readByte(D);
+ return readByte();
if (C == 'z') {
- skipLeb128(D);
+ skipLeb128();
continue;
}
if (C == 'P') {
- skipAugP<ELFT>(D);
+ skipAugP();
continue;
}
if (C == 'L') {
- readByte(D);
+ readByte();
continue;
}
- fatal("unknown .eh_frame augmentation string: " + Aug);
+ failOn(Aug.data(), "unknown .eh_frame augmentation string: " + Aug);
}
return DW_EH_PE_absptr;
}
-template size_t readEhRecordSize<ELF32LE>(ArrayRef<uint8_t>);
-template size_t readEhRecordSize<ELF32BE>(ArrayRef<uint8_t>);
-template size_t readEhRecordSize<ELF64LE>(ArrayRef<uint8_t>);
-template size_t readEhRecordSize<ELF64BE>(ArrayRef<uint8_t>);
-
-template uint8_t getFdeEncoding<ELF32LE>(ArrayRef<uint8_t>);
-template uint8_t getFdeEncoding<ELF32BE>(ArrayRef<uint8_t>);
-template uint8_t getFdeEncoding<ELF64LE>(ArrayRef<uint8_t>);
-template uint8_t getFdeEncoding<ELF64BE>(ArrayRef<uint8_t>);
-}
-}
+template size_t elf::readEhRecordSize<ELF32LE>(InputSectionBase<ELF32LE> *S,
+ size_t Off);
+template size_t elf::readEhRecordSize<ELF32BE>(InputSectionBase<ELF32BE> *S,
+ size_t Off);
+template size_t elf::readEhRecordSize<ELF64LE>(InputSectionBase<ELF64LE> *S,
+ size_t Off);
+template size_t elf::readEhRecordSize<ELF64BE>(InputSectionBase<ELF64BE> *S,
+ size_t Off);
+
+template uint8_t elf::getFdeEncoding<ELF32LE>(EhSectionPiece *P);
+template uint8_t elf::getFdeEncoding<ELF32BE>(EhSectionPiece *P);
+template uint8_t elf::getFdeEncoding<ELF64LE>(EhSectionPiece *P);
+template uint8_t elf::getFdeEncoding<ELF64BE>(EhSectionPiece *P);
diff --git a/ELF/EhFrame.h b/ELF/EhFrame.h
index 0d5a2ff2f417..cadc93d3a2e4 100644
--- a/ELF/EhFrame.h
+++ b/ELF/EhFrame.h
@@ -14,8 +14,12 @@
namespace lld {
namespace elf {
-template <class ELFT> size_t readEhRecordSize(ArrayRef<uint8_t> Data);
-template <class ELFT> uint8_t getFdeEncoding(ArrayRef<uint8_t> Data);
+template <class ELFT> class InputSectionBase;
+struct EhSectionPiece;
+
+template <class ELFT>
+size_t readEhRecordSize(InputSectionBase<ELFT> *S, size_t Off);
+template <class ELFT> uint8_t getFdeEncoding(EhSectionPiece *P);
}
}
diff --git a/ELF/Error.cpp b/ELF/Error.cpp
index 59a49c17b97c..6e30f08143ed 100644
--- a/ELF/Error.cpp
+++ b/ELF/Error.cpp
@@ -12,54 +12,95 @@
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/raw_ostream.h"
+#include <mutex>
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+#include <unistd.h>
+#endif
+
+using namespace lld::elf;
using namespace llvm;
namespace lld {
-namespace elf {
-bool HasError;
-raw_ostream *ErrorOS;
+uint64_t elf::ErrorCount;
+raw_ostream *elf::ErrorOS;
+StringRef elf::Argv0;
+
+// The functions defined in this file can be called from multiple threads,
+// but outs() or errs() are not thread-safe. We protect them using a mutex.
+static std::mutex Mu;
+
+static void print(StringRef S, raw_ostream::Colors C) {
+ *ErrorOS << Argv0 + ": ";
+ if (Config->ColorDiagnostics) {
+ ErrorOS->changeColor(C, true);
+ *ErrorOS << S;
+ ErrorOS->resetColor();
+ } else {
+ *ErrorOS << S;
+ }
+}
-void log(const Twine &Msg) {
+void elf::log(const Twine &Msg) {
+ std::lock_guard<std::mutex> Lock(Mu);
if (Config->Verbose)
- outs() << Msg << "\n";
+ outs() << Argv0 << ": " << Msg << "\n";
}
-void warning(const Twine &Msg) {
- if (Config->FatalWarnings)
+void elf::warn(const Twine &Msg) {
+ if (Config->FatalWarnings) {
error(Msg);
- else
- *ErrorOS << Msg << "\n";
+ return;
+ }
+ std::lock_guard<std::mutex> Lock(Mu);
+ print("warning: ", raw_ostream::MAGENTA);
+ *ErrorOS << Msg << "\n";
}
-void error(const Twine &Msg) {
- *ErrorOS << Msg << "\n";
- HasError = true;
+void elf::error(const Twine &Msg) {
+ std::lock_guard<std::mutex> Lock(Mu);
+
+ if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) {
+ print("error: ", raw_ostream::RED);
+ *ErrorOS << Msg << "\n";
+ } else if (ErrorCount == Config->ErrorLimit) {
+ print("error: ", raw_ostream::RED);
+ *ErrorOS << "too many errors emitted, stopping now"
+ << " (use -error-limit=0 to see all errors)\n";
+ if (Config->ExitEarly)
+ exitLld(1);
+ }
+
+ ++ErrorCount;
}
-void error(std::error_code EC, const Twine &Prefix) {
+void elf::error(std::error_code EC, const Twine &Prefix) {
error(Prefix + ": " + EC.message());
}
-void fatal(const Twine &Msg) {
- *ErrorOS << Msg << "\n";
- exit(1);
-}
+void elf::exitLld(int Val) {
+ // Dealloc/destroy ManagedStatic variables before calling
+ // _exit(). In a non-LTO build, this is a nop. In an LTO
+ // build allows us to get the output of -time-passes.
+ llvm_shutdown();
-void fatal(const Twine &Msg, const Twine &Prefix) {
- fatal(Prefix + ": " + Msg);
+ outs().flush();
+ errs().flush();
+ _exit(Val);
}
-void check(std::error_code EC) {
- if (EC)
- fatal(EC.message());
+void elf::fatal(const Twine &Msg) {
+ std::lock_guard<std::mutex> Lock(Mu);
+ print("error: ", raw_ostream::RED);
+ *ErrorOS << Msg << "\n";
+ exitLld(1);
}
-void check(Error Err) {
- check(errorToErrorCode(std::move(Err)));
+void elf::fatal(std::error_code EC, const Twine &Prefix) {
+ fatal(Prefix + ": " + EC.message());
}
-} // namespace elf
} // namespace lld
diff --git a/ELF/Error.h b/ELF/Error.h
index 552f50498464..1ec683595cf4 100644
--- a/ELF/Error.h
+++ b/ELF/Error.h
@@ -6,31 +6,47 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
+//
+// In LLD, we have three levels of errors: fatal, error or warn.
+//
+// Fatal makes the program exit immediately with an error message.
+// You shouldn't use it except for reporting a corrupted input file.
+//
+// Error prints out an error message and increment a global variable
+// ErrorCount to record the fact that we met an error condition. It does
+// not exit, so it is safe for a lld-as-a-library use case. It is generally
+// useful because it can report more than one errors in a single run.
+//
+// Warn doesn't do anything but printing out a given message.
+//
+//===----------------------------------------------------------------------===//
-#ifndef LLD_COFF_ERROR_H
-#define LLD_COFF_ERROR_H
+#ifndef LLD_ELF_ERROR_H
+#define LLD_ELF_ERROR_H
#include "lld/Core/LLVM.h"
+#include "llvm/Support/Error.h"
+
namespace lld {
namespace elf {
-extern bool HasError;
+extern uint64_t ErrorCount;
extern llvm::raw_ostream *ErrorOS;
+extern llvm::StringRef Argv0;
void log(const Twine &Msg);
-void warning(const Twine &Msg);
+void warn(const Twine &Msg);
void error(const Twine &Msg);
void error(std::error_code EC, const Twine &Prefix);
-template <typename T> void error(const ErrorOr<T> &V, const Twine &Prefix) {
- error(V.getError(), Prefix);
-}
-
+LLVM_ATTRIBUTE_NORETURN void exitLld(int Val);
LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg);
-LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg, const Twine &Prefix);
+LLVM_ATTRIBUTE_NORETURN void fatal(std::error_code EC, const Twine &Prefix);
+// check() functions are convenient functions to strip errors
+// from error-or-value objects.
template <class T> T check(ErrorOr<T> E) {
if (auto EC = E.getError())
fatal(EC.message());
@@ -39,19 +55,23 @@ template <class T> T check(ErrorOr<T> E) {
template <class T> T check(Expected<T> E) {
if (!E)
- fatal(errorToErrorCode(E.takeError()).message());
+ handleAllErrors(std::move(E.takeError()),
+ [](llvm::ErrorInfoBase &EIB) -> Error {
+ fatal(EIB.message());
+ return Error::success();
+ });
return std::move(*E);
}
template <class T> T check(ErrorOr<T> E, const Twine &Prefix) {
if (auto EC = E.getError())
- fatal(EC.message(), Prefix);
+ fatal(Prefix + ": " + EC.message());
return std::move(*E);
}
template <class T> T check(Expected<T> E, const Twine &Prefix) {
if (!E)
- fatal(errorToErrorCode(E.takeError()).message(), Prefix);
+ fatal(Prefix + ": " + errorToErrorCode(E.takeError()).message());
return std::move(*E);
}
diff --git a/ELF/GdbIndex.cpp b/ELF/GdbIndex.cpp
new file mode 100644
index 000000000000..762144dd0a96
--- /dev/null
+++ b/ELF/GdbIndex.cpp
@@ -0,0 +1,205 @@
+//===- GdbIndex.cpp -------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// File contains classes for implementation of --gdb-index command line option.
+//
+// If that option is used, linker should emit a .gdb_index section that allows
+// debugger to locate and read .dwo files, containing neccessary debug
+// information.
+// More information about implementation can be found in DWARF specification,
+// latest version is available at http://dwarfstd.org.
+//
+// .gdb_index section format:
+// (Information is based on/taken from
+// https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html (*))
+//
+// A mapped index consists of several areas, laid out in order:
+// 1) The file header.
+// 2) "The CU (compilation unit) list. This is a sequence of pairs of 64-bit
+// little-endian values, sorted by the CU offset. The first element in each
+// pair is the offset of a CU in the .debug_info section. The second element
+// in each pair is the length of that CU. References to a CU elsewhere in the
+// map are done using a CU index, which is just the 0-based index into this
+// table. Note that if there are type CUs, then conceptually CUs and type CUs
+// form a single list for the purposes of CU indices."(*)
+// 3) The types CU list. Depricated as .debug_types does not appear in the DWARF
+// v5 specification.
+// 4) The address area. The address area is a sequence of address
+// entries, where each entrie contains low address, high address and CU
+// index.
+// 5) "The symbol table. This is an open-addressed hash table. The size of the
+// hash table is always a power of 2. Each slot in the hash table consists of
+// a pair of offset_type values. The first value is the offset of the
+// symbol's name in the constant pool. The second value is the offset of the
+// CU vector in the constant pool."(*)
+// 6) "The constant pool. This is simply a bunch of bytes. It is organized so
+// that alignment is correct: CU vectors are stored first, followed by
+// strings." (*)
+//
+// For constructing the .gdb_index section following steps should be performed:
+// 1) For file header nothing special should be done. It contains the offsets to
+// the areas below.
+// 2) Scan the compilation unit headers of the .debug_info sections to build a
+// list of compilation units.
+// 3) CU Types are no longer needed as DWARF skeleton type units never made it
+// into the standard. lld does nothing to support parsing of .debug_types
+// and generates empty types CU area in .gdb_index section.
+// 4) Address area entries are extracted from DW_TAG_compile_unit DIEs of
+// .debug_info sections.
+// 5) For building the symbol table linker extracts the public names from the
+// .debug_gnu_pubnames and .debug_gnu_pubtypes sections. Then it builds the
+// hashtable in according to .gdb_index format specification.
+// 6) Constant pool is populated at the same time as symbol table.
+//===----------------------------------------------------------------------===//
+
+#include "GdbIndex.h"
+#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h"
+#include "llvm/Object/ELFObjectFile.h"
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace lld::elf;
+
+template <class ELFT>
+GdbIndexBuilder<ELFT>::GdbIndexBuilder(InputSection<ELFT> *DebugInfoSec)
+ : DebugInfoSec(DebugInfoSec) {
+ if (Expected<std::unique_ptr<object::ObjectFile>> Obj =
+ object::ObjectFile::createObjectFile(DebugInfoSec->getFile()->MB))
+ Dwarf.reset(new DWARFContextInMemory(*Obj.get(), this));
+ else
+ error(toString(DebugInfoSec->getFile()) + ": error creating DWARF context");
+}
+
+template <class ELFT>
+std::vector<std::pair<typename ELFT::uint, typename ELFT::uint>>
+GdbIndexBuilder<ELFT>::readCUList() {
+ std::vector<std::pair<uintX_t, uintX_t>> Ret;
+ for (std::unique_ptr<DWARFCompileUnit> &CU : Dwarf->compile_units())
+ Ret.push_back(
+ {DebugInfoSec->OutSecOff + CU->getOffset(), CU->getLength() + 4});
+ return Ret;
+}
+
+template <class ELFT>
+std::vector<std::pair<StringRef, uint8_t>>
+GdbIndexBuilder<ELFT>::readPubNamesAndTypes() {
+ const bool IsLE = ELFT::TargetEndianness == llvm::support::little;
+ StringRef Data[] = {Dwarf->getGnuPubNamesSection(),
+ Dwarf->getGnuPubTypesSection()};
+
+ std::vector<std::pair<StringRef, uint8_t>> Ret;
+ for (StringRef D : Data) {
+ DWARFDebugPubTable PubTable(D, IsLE, true);
+ for (const DWARFDebugPubTable::Set &S : PubTable.getData())
+ for (const DWARFDebugPubTable::Entry &E : S.Entries)
+ Ret.push_back({E.Name, E.Descriptor.toBits()});
+ }
+ return Ret;
+}
+
+std::pair<bool, GdbSymbol *> GdbHashTab::add(uint32_t Hash, size_t Offset) {
+ if (Size * 4 / 3 >= Table.size())
+ expand();
+
+ GdbSymbol **Slot = findSlot(Hash, Offset);
+ bool New = false;
+ if (*Slot == nullptr) {
+ ++Size;
+ *Slot = new (Alloc) GdbSymbol(Hash, Offset);
+ New = true;
+ }
+ return {New, *Slot};
+}
+
+void GdbHashTab::expand() {
+ if (Table.empty()) {
+ Table.resize(InitialSize);
+ return;
+ }
+ std::vector<GdbSymbol *> NewTable(Table.size() * 2);
+ NewTable.swap(Table);
+
+ for (GdbSymbol *Sym : NewTable) {
+ if (!Sym)
+ continue;
+ GdbSymbol **Slot = findSlot(Sym->NameHash, Sym->NameOffset);
+ *Slot = Sym;
+ }
+}
+
+// Methods finds a slot for symbol with given hash. The step size used to find
+// the next candidate slot when handling a hash collision is specified in
+// .gdb_index section format. The hash value for a table entry is computed by
+// applying an iterative hash function to the symbol's name.
+GdbSymbol **GdbHashTab::findSlot(uint32_t Hash, size_t Offset) {
+ uint32_t Index = Hash & (Table.size() - 1);
+ uint32_t Step = ((Hash * 17) & (Table.size() - 1)) | 1;
+
+ for (;;) {
+ GdbSymbol *S = Table[Index];
+ if (!S || ((S->NameOffset == Offset) && (S->NameHash == Hash)))
+ return &Table[Index];
+ Index = (Index + Step) & (Table.size() - 1);
+ }
+}
+
+template <class ELFT>
+static InputSectionBase<ELFT> *
+findSection(ArrayRef<InputSectionBase<ELFT> *> Arr, uint64_t Offset) {
+ for (InputSectionBase<ELFT> *S : Arr)
+ if (S && S != &InputSection<ELFT>::Discarded)
+ if (Offset >= S->Offset && Offset < S->Offset + S->getSize())
+ return S;
+ return nullptr;
+}
+
+template <class ELFT>
+std::vector<AddressEntry<ELFT>>
+GdbIndexBuilder<ELFT>::readAddressArea(size_t CurrentCU) {
+ std::vector<AddressEntry<ELFT>> Ret;
+ for (const auto &CU : Dwarf->compile_units()) {
+ DWARFAddressRangesVector Ranges;
+ CU->collectAddressRanges(Ranges);
+
+ ArrayRef<InputSectionBase<ELFT> *> Sections =
+ DebugInfoSec->getFile()->getSections();
+
+ for (std::pair<uint64_t, uint64_t> &R : Ranges)
+ if (InputSectionBase<ELFT> *S = findSection(Sections, R.first))
+ Ret.push_back(
+ {S, R.first - S->Offset, R.second - S->Offset, CurrentCU});
+ ++CurrentCU;
+ }
+ return Ret;
+}
+
+// We return file offset as load address for allocatable sections. That is
+// currently used for collecting address ranges in readAddressArea(). We are
+// able then to find section index that range belongs to.
+template <class ELFT>
+uint64_t GdbIndexBuilder<ELFT>::getSectionLoadAddress(
+ const object::SectionRef &Sec) const {
+ if (static_cast<const ELFSectionRef &>(Sec).getFlags() & ELF::SHF_ALLOC)
+ return static_cast<const ELFSectionRef &>(Sec).getOffset();
+ return 0;
+}
+
+template <class ELFT>
+std::unique_ptr<LoadedObjectInfo> GdbIndexBuilder<ELFT>::clone() const {
+ return {};
+}
+
+namespace lld {
+namespace elf {
+template class GdbIndexBuilder<ELF32LE>;
+template class GdbIndexBuilder<ELF32BE>;
+template class GdbIndexBuilder<ELF64LE>;
+template class GdbIndexBuilder<ELF64BE>;
+}
+}
diff --git a/ELF/GdbIndex.h b/ELF/GdbIndex.h
new file mode 100644
index 000000000000..c761ea173a8d
--- /dev/null
+++ b/ELF/GdbIndex.h
@@ -0,0 +1,99 @@
+//===- GdbIndex.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===-------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_GDB_INDEX_H
+#define LLD_ELF_GDB_INDEX_H
+
+#include "InputFiles.h"
+#include "llvm/Object/ELF.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+
+namespace lld {
+namespace elf {
+
+template <class ELFT> class InputSection;
+
+// Struct represents single entry of address area of gdb index.
+template <class ELFT> struct AddressEntry {
+ InputSectionBase<ELFT> *Section;
+ uint64_t LowAddress;
+ uint64_t HighAddress;
+ size_t CuIndex;
+};
+
+// GdbIndexBuilder is a helper class used for extracting data required
+// for building .gdb_index section from objects.
+template <class ELFT> class GdbIndexBuilder : public llvm::LoadedObjectInfo {
+ typedef typename ELFT::uint uintX_t;
+
+ InputSection<ELFT> *DebugInfoSec;
+
+ std::unique_ptr<llvm::DWARFContext> Dwarf;
+
+public:
+ GdbIndexBuilder(InputSection<ELFT> *DebugInfoSec);
+
+ // Extracts the compilation units. Each first element of pair is a offset of a
+ // CU in the .debug_info section and second is the length of that CU.
+ std::vector<std::pair<uintX_t, uintX_t>> readCUList();
+
+ // Extracts the vector of address area entries. Accepts global index of last
+ // parsed CU.
+ std::vector<AddressEntry<ELFT>> readAddressArea(size_t CurrentCU);
+
+ // Method extracts public names and types. It returns list of name and
+ // gnu_pub* kind pairs.
+ std::vector<std::pair<StringRef, uint8_t>> readPubNamesAndTypes();
+
+private:
+ // Method returns section file offset as a load addres for DWARF parser. That
+ // allows to find the target section index for address ranges.
+ uint64_t
+ getSectionLoadAddress(const llvm::object::SectionRef &Sec) const override;
+ std::unique_ptr<llvm::LoadedObjectInfo> clone() const override;
+};
+
+// Element of GdbHashTab hash table.
+struct GdbSymbol {
+ GdbSymbol(uint32_t Hash, size_t Offset)
+ : NameHash(Hash), NameOffset(Offset) {}
+ uint32_t NameHash;
+ size_t NameOffset;
+ size_t CuVectorIndex;
+};
+
+// This class manages the hashed symbol table for the .gdb_index section.
+// The hash value for a table entry is computed by applying an iterative hash
+// function to the symbol's name.
+class GdbHashTab final {
+public:
+ std::pair<bool, GdbSymbol *> add(uint32_t Hash, size_t Offset);
+
+ size_t getCapacity() { return Table.size(); }
+ GdbSymbol *getSymbol(size_t I) { return Table[I]; }
+
+private:
+ void expand();
+
+ GdbSymbol **findSlot(uint32_t Hash, size_t Offset);
+
+ llvm::BumpPtrAllocator Alloc;
+ std::vector<GdbSymbol *> Table;
+
+ // Size keeps the amount of filled entries in Table.
+ size_t Size = 0;
+
+ // Initial size must be a power of 2.
+ static const int32_t InitialSize = 1024;
+};
+
+} // namespace elf
+} // namespace lld
+
+#endif
diff --git a/ELF/ICF.cpp b/ELF/ICF.cpp
index 10a2603b3b3e..32cd0f8a185c 100644
--- a/ELF/ICF.cpp
+++ b/ELF/ICF.cpp
@@ -7,63 +7,82 @@
//
//===----------------------------------------------------------------------===//
//
-// Identical Code Folding is a feature to merge sections not by name (which
-// is regular comdat handling) but by contents. If two non-writable sections
-// have the same data, relocations, attributes, etc., then the two
-// are considered identical and merged by the linker. This optimization
-// makes outputs smaller.
+// ICF is short for Identical Code Folding. This is a size optimization to
+// identify and merge two or more read-only sections (typically functions)
+// that happened to have the same contents. It usually reduces output size
+// by a few percent.
//
-// ICF is theoretically a problem of reducing graphs by merging as many
-// identical subgraphs as possible if we consider sections as vertices and
-// relocations as edges. It may sound simple, but it is a bit more
-// complicated than you might think. The order of processing sections
-// matters because merging two sections can make other sections, whose
-// relocations now point to the same section, mergeable. Graphs may contain
-// cycles. We need a sophisticated algorithm to do this properly and
-// efficiently.
+// In ICF, two sections are considered identical if they have the same
+// section flags, section data, and relocations. Relocations are tricky,
+// because two relocations are considered the same if they have the same
+// relocation types, values, and if they point to the same sections *in
+// terms of ICF*.
//
-// What we do in this file is this. We split sections into groups. Sections
-// in the same group are considered identical.
+// Here is an example. If foo and bar defined below are compiled to the
+// same machine instructions, ICF can and should merge the two, although
+// their relocations point to each other.
//
-// We begin by optimistically putting all sections into a single equivalence
-// class. Then we apply a series of checks that split this initial
-// equivalence class into more and more refined equivalence classes based on
-// the properties by which a section can be distinguished.
+// void foo() { bar(); }
+// void bar() { foo(); }
//
-// We begin by checking that the section contents and flags are the
-// same. This only needs to be done once since these properties don't depend
-// on the current equivalence class assignment.
+// If you merge the two, their relocations point to the same section and
+// thus you know they are mergeable, but how do you know they are
+// mergeable in the first place? This is not an easy problem to solve.
//
-// Then we split the equivalence classes based on checking that their
-// relocations are the same, where relocation targets are compared by their
-// equivalence class, not the concrete section. This may need to be done
-// multiple times because as the equivalence classes are refined, two
-// sections that had a relocation target in the same equivalence class may
-// now target different equivalence classes, and hence these two sections
-// must be put in different equivalence classes (whereas in the previous
-// iteration they were not since the relocation target was the same.)
+// What we are doing in LLD is to partition sections into equivalence
+// classes. Sections in the same equivalence class when the algorithm
+// terminates are considered identical. Here are details:
//
-// Our algorithm is smart enough to merge the following mutually-recursive
-// functions.
+// 1. First, we partition sections using their hash values as keys. Hash
+// values contain section types, section contents and numbers of
+// relocations. During this step, relocation targets are not taken into
+// account. We just put sections that apparently differ into different
+// equivalence classes.
//
-// void foo() { bar(); }
-// void bar() { foo(); }
+// 2. Next, for each equivalence class, we visit sections to compare
+// relocation targets. Relocation targets are considered equivalent if
+// their targets are in the same equivalence class. Sections with
+// different relocation targets are put into different equivalence
+// clases.
+//
+// 3. If we split an equivalence class in step 2, two relocations
+// previously target the same equivalence class may now target
+// different equivalence classes. Therefore, we repeat step 2 until a
+// convergence is obtained.
+//
+// 4. For each equivalence class C, pick an arbitrary section in C, and
+// merge all the other sections in C with it.
+//
+// For small programs, this algorithm needs 3-5 iterations. For large
+// programs such as Chromium, it takes more than 20 iterations.
+//
+// This algorithm was mentioned as an "optimistic algorithm" in [1],
+// though gold implements a different algorithm than this.
+//
+// We parallelize each step so that multiple threads can work on different
+// equivalence classes concurrently. That gave us a large performance
+// boost when applying ICF on large programs. For example, MSVC link.exe
+// or GNU gold takes 10-20 seconds to apply ICF on Chromium, whose output
+// size is about 1.5 GB, but LLD can finish it in less than 2 seconds on a
+// 2.8 GHz 40 core machine. Even without threading, LLD's ICF is still
+// faster than MSVC or gold though.
//
-// This algorithm is so-called "optimistic" algorithm described in
-// http://research.google.com/pubs/pub36912.html. (Note that what GNU
-// gold implemented is different from the optimistic algorithm.)
+// [1] Safe ICF: Pointer Safe and Unwinding aware Identical Code Folding
+// in the Gold Linker
+// http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36912.pdf
//
//===----------------------------------------------------------------------===//
#include "ICF.h"
#include "Config.h"
-#include "OutputSections.h"
#include "SymbolTable.h"
+#include "Threads.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/ELF.h"
-#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <atomic>
using namespace lld;
using namespace lld::elf;
@@ -71,143 +90,132 @@ using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
-namespace lld {
-namespace elf {
+namespace {
template <class ELFT> class ICF {
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::Sym Elf_Sym;
- typedef typename ELFT::uint uintX_t;
- typedef Elf_Rel_Impl<ELFT, false> Elf_Rel;
-
- using Comparator = std::function<bool(const InputSection<ELFT> *,
- const InputSection<ELFT> *)>;
-
public:
void run();
private:
- uint64_t NextId = 1;
-
- static void setLive(SymbolTable<ELFT> *S);
- static uint64_t relSize(InputSection<ELFT> *S);
- static uint64_t getHash(InputSection<ELFT> *S);
- static bool isEligible(InputSectionBase<ELFT> *Sec);
- static std::vector<InputSection<ELFT> *> getSections();
-
- void segregate(InputSection<ELFT> **Begin, InputSection<ELFT> **End,
- Comparator Eq);
-
- void forEachGroup(std::vector<InputSection<ELFT> *> &V, Comparator Eq);
+ void segregate(size_t Begin, size_t End, bool Constant);
template <class RelTy>
- static bool relocationEq(ArrayRef<RelTy> RA, ArrayRef<RelTy> RB);
+ bool constantEq(ArrayRef<RelTy> RelsA, ArrayRef<RelTy> RelsB);
template <class RelTy>
- static bool variableEq(const InputSection<ELFT> *A,
- const InputSection<ELFT> *B, ArrayRef<RelTy> RA,
- ArrayRef<RelTy> RB);
-
- static bool equalsConstant(const InputSection<ELFT> *A,
- const InputSection<ELFT> *B);
-
- static bool equalsVariable(const InputSection<ELFT> *A,
- const InputSection<ELFT> *B);
+ bool variableEq(const InputSection<ELFT> *A, ArrayRef<RelTy> RelsA,
+ const InputSection<ELFT> *B, ArrayRef<RelTy> RelsB);
+
+ bool equalsConstant(const InputSection<ELFT> *A, const InputSection<ELFT> *B);
+ bool equalsVariable(const InputSection<ELFT> *A, const InputSection<ELFT> *B);
+
+ size_t findBoundary(size_t Begin, size_t End);
+
+ void forEachClassRange(size_t Begin, size_t End,
+ std::function<void(size_t, size_t)> Fn);
+
+ void forEachClass(std::function<void(size_t, size_t)> Fn);
+
+ std::vector<InputSection<ELFT> *> Sections;
+
+ // We repeat the main loop while `Repeat` is true.
+ std::atomic<bool> Repeat;
+
+ // The main loop counter.
+ int Cnt = 0;
+
+ // We have two locations for equivalence classes. On the first iteration
+ // of the main loop, Class[0] has a valid value, and Class[1] contains
+ // garbage. We read equivalence classes from slot 0 and write to slot 1.
+ // So, Class[0] represents the current class, and Class[1] represents
+ // the next class. On each iteration, we switch their roles and use them
+ // alternately.
+ //
+ // Why are we doing this? Recall that other threads may be working on
+ // other equivalence classes in parallel. They may read sections that we
+ // are updating. We cannot update equivalence classes in place because
+ // it breaks the invariance that all possibly-identical sections must be
+ // in the same equivalence class at any moment. In other words, the for
+ // loop to update equivalence classes is not atomic, and that is
+ // observable from other threads. By writing new classes to other
+ // places, we can keep the invariance.
+ //
+ // Below, `Current` has the index of the current class, and `Next` has
+ // the index of the next class. If threading is enabled, they are either
+ // (0, 1) or (1, 0).
+ //
+ // Note on single-thread: if that's the case, they are always (0, 0)
+ // because we can safely read the next class without worrying about race
+ // conditions. Using the same location makes this algorithm converge
+ // faster because it uses results of the same iteration earlier.
+ int Current = 0;
+ int Next = 0;
};
}
-}
// Returns a hash value for S. Note that the information about
// relocation targets is not included in the hash value.
-template <class ELFT> uint64_t ICF<ELFT>::getHash(InputSection<ELFT> *S) {
- uint64_t Flags = S->getSectionHdr()->sh_flags;
- uint64_t H = hash_combine(Flags, S->getSize());
- for (const Elf_Shdr *Rel : S->RelocSections)
- H = hash_combine(H, (uint64_t)Rel->sh_size);
- return H;
+template <class ELFT> static uint32_t getHash(InputSection<ELFT> *S) {
+ return hash_combine(S->Flags, S->getSize(), S->NumRelocations);
}
-// Returns true if Sec is subject of ICF.
-template <class ELFT> bool ICF<ELFT>::isEligible(InputSectionBase<ELFT> *Sec) {
- if (!Sec || Sec == &InputSection<ELFT>::Discarded || !Sec->Live)
- return false;
- auto *S = dyn_cast<InputSection<ELFT>>(Sec);
- if (!S)
- return false;
-
+// Returns true if section S is subject of ICF.
+template <class ELFT> static bool isEligible(InputSection<ELFT> *S) {
// .init and .fini contains instructions that must be executed to
// initialize and finalize the process. They cannot and should not
// be merged.
- StringRef Name = S->getSectionName();
- if (Name == ".init" || Name == ".fini")
- return false;
-
- const Elf_Shdr &H = *S->getSectionHdr();
- return (H.sh_flags & SHF_ALLOC) && (~H.sh_flags & SHF_WRITE);
-}
-
-template <class ELFT>
-std::vector<InputSection<ELFT> *> ICF<ELFT>::getSections() {
- std::vector<InputSection<ELFT> *> V;
- for (const std::unique_ptr<ObjectFile<ELFT>> &F :
- Symtab<ELFT>::X->getObjectFiles())
- for (InputSectionBase<ELFT> *S : F->getSections())
- if (isEligible(S))
- V.push_back(cast<InputSection<ELFT>>(S));
- return V;
+ return S->Live && (S->Flags & SHF_ALLOC) && !(S->Flags & SHF_WRITE) &&
+ S->Name != ".init" && S->Name != ".fini";
}
-// All sections between Begin and End must have the same group ID before
-// you call this function. This function compare sections between Begin
-// and End using Eq and assign new group IDs for new groups.
+// Split an equivalence class into smaller classes.
template <class ELFT>
-void ICF<ELFT>::segregate(InputSection<ELFT> **Begin, InputSection<ELFT> **End,
- Comparator Eq) {
- // This loop rearranges [Begin, End) so that all sections that are
- // equal in terms of Eq are contiguous. The algorithm is quadratic in
- // the worst case, but that is not an issue in practice because the
- // number of distinct sections in [Begin, End) is usually very small.
- InputSection<ELFT> **I = Begin;
- for (;;) {
- InputSection<ELFT> *Head = *I;
+void ICF<ELFT>::segregate(size_t Begin, size_t End, bool Constant) {
+ // This loop rearranges sections in [Begin, End) so that all sections
+ // that are equal in terms of equals{Constant,Variable} are contiguous
+ // in [Begin, End).
+ //
+ // The algorithm is quadratic in the worst case, but that is not an
+ // issue in practice because the number of the distinct sections in
+ // each range is usually very small.
+
+ while (Begin < End) {
+ // Divide [Begin, End) into two. Let Mid be the start index of the
+ // second group.
auto Bound = std::stable_partition(
- I + 1, End, [&](InputSection<ELFT> *S) { return Eq(Head, S); });
- if (Bound == End)
- return;
- uint64_t Id = NextId++;
- for (; I != Bound; ++I)
- (*I)->GroupId = Id;
- }
-}
-
-template <class ELFT>
-void ICF<ELFT>::forEachGroup(std::vector<InputSection<ELFT> *> &V,
- Comparator Eq) {
- for (InputSection<ELFT> **I = V.data(), **E = I + V.size(); I != E;) {
- InputSection<ELFT> *Head = *I;
- auto Bound = std::find_if(I + 1, E, [&](InputSection<ELFT> *S) {
- return S->GroupId != Head->GroupId;
- });
- segregate(I, Bound, Eq);
- I = Bound;
+ Sections.begin() + Begin + 1, Sections.begin() + End,
+ [&](InputSection<ELFT> *S) {
+ if (Constant)
+ return equalsConstant(Sections[Begin], S);
+ return equalsVariable(Sections[Begin], S);
+ });
+ size_t Mid = Bound - Sections.begin();
+
+ // Now we split [Begin, End) into [Begin, Mid) and [Mid, End) by
+ // updating the sections in [Begin, End). We use Mid as an equivalence
+ // class ID because every group ends with a unique index.
+ for (size_t I = Begin; I < Mid; ++I)
+ Sections[I]->Class[Next] = Mid;
+
+ // If we created a group, we need to iterate the main loop again.
+ if (Mid != End)
+ Repeat = true;
+
+ Begin = Mid;
}
}
// Compare two lists of relocations.
template <class ELFT>
template <class RelTy>
-bool ICF<ELFT>::relocationEq(ArrayRef<RelTy> RelsA, ArrayRef<RelTy> RelsB) {
- const RelTy *IA = RelsA.begin();
- const RelTy *EA = RelsA.end();
- const RelTy *IB = RelsB.begin();
- const RelTy *EB = RelsB.end();
- if (EA - IA != EB - IB)
- return false;
- for (; IA != EA; ++IA, ++IB)
- if (IA->r_offset != IB->r_offset ||
- IA->getType(Config->Mips64EL) != IB->getType(Config->Mips64EL) ||
- getAddend<ELFT>(*IA) != getAddend<ELFT>(*IB))
- return false;
- return true;
+bool ICF<ELFT>::constantEq(ArrayRef<RelTy> RelsA, ArrayRef<RelTy> RelsB) {
+ auto Eq = [](const RelTy &A, const RelTy &B) {
+ return A.r_offset == B.r_offset &&
+ A.getType(Config->Mips64EL) == B.getType(Config->Mips64EL) &&
+ getAddend<ELFT>(A) == getAddend<ELFT>(B);
+ };
+
+ return RelsA.size() == RelsB.size() &&
+ std::equal(RelsA.begin(), RelsA.end(), RelsB.begin(), Eq);
}
// Compare "non-moving" part of two InputSections, namely everything
@@ -215,125 +223,155 @@ bool ICF<ELFT>::relocationEq(ArrayRef<RelTy> RelsA, ArrayRef<RelTy> RelsB) {
template <class ELFT>
bool ICF<ELFT>::equalsConstant(const InputSection<ELFT> *A,
const InputSection<ELFT> *B) {
- if (A->RelocSections.size() != B->RelocSections.size())
+ if (A->NumRelocations != B->NumRelocations || A->Flags != B->Flags ||
+ A->getSize() != B->getSize() || A->Data != B->Data)
return false;
- for (size_t I = 0, E = A->RelocSections.size(); I != E; ++I) {
- const Elf_Shdr *RA = A->RelocSections[I];
- const Elf_Shdr *RB = B->RelocSections[I];
- ELFFile<ELFT> &FileA = A->File->getObj();
- ELFFile<ELFT> &FileB = B->File->getObj();
- if (RA->sh_type == SHT_RELA) {
- if (!relocationEq(FileA.relas(RA), FileB.relas(RB)))
- return false;
- } else {
- if (!relocationEq(FileA.rels(RA), FileB.rels(RB)))
- return false;
- }
- }
-
- return A->getSectionHdr()->sh_flags == B->getSectionHdr()->sh_flags &&
- A->getSize() == B->getSize() &&
- A->getSectionData() == B->getSectionData();
+ if (A->AreRelocsRela)
+ return constantEq(A->relas(), B->relas());
+ return constantEq(A->rels(), B->rels());
}
+// Compare two lists of relocations. Returns true if all pairs of
+// relocations point to the same section in terms of ICF.
template <class ELFT>
template <class RelTy>
-bool ICF<ELFT>::variableEq(const InputSection<ELFT> *A,
- const InputSection<ELFT> *B, ArrayRef<RelTy> RelsA,
- ArrayRef<RelTy> RelsB) {
- const RelTy *IA = RelsA.begin();
- const RelTy *EA = RelsA.end();
- const RelTy *IB = RelsB.begin();
- for (; IA != EA; ++IA, ++IB) {
- SymbolBody &SA = A->File->getRelocTargetSym(*IA);
- SymbolBody &SB = B->File->getRelocTargetSym(*IB);
+bool ICF<ELFT>::variableEq(const InputSection<ELFT> *A, ArrayRef<RelTy> RelsA,
+ const InputSection<ELFT> *B, ArrayRef<RelTy> RelsB) {
+ auto Eq = [&](const RelTy &RA, const RelTy &RB) {
+ // The two sections must be identical.
+ SymbolBody &SA = A->getFile()->getRelocTargetSym(RA);
+ SymbolBody &SB = B->getFile()->getRelocTargetSym(RB);
if (&SA == &SB)
- continue;
+ return true;
- // Or, the symbols should be pointing to the same section
- // in terms of the group ID.
+ // Or, the two sections must be in the same equivalence class.
auto *DA = dyn_cast<DefinedRegular<ELFT>>(&SA);
auto *DB = dyn_cast<DefinedRegular<ELFT>>(&SB);
if (!DA || !DB)
return false;
if (DA->Value != DB->Value)
return false;
- InputSection<ELFT> *X = dyn_cast<InputSection<ELFT>>(DA->Section);
- InputSection<ELFT> *Y = dyn_cast<InputSection<ELFT>>(DB->Section);
- if (X && Y && X->GroupId && X->GroupId == Y->GroupId)
- continue;
- return false;
- }
- return true;
+
+ auto *X = dyn_cast<InputSection<ELFT>>(DA->Section);
+ auto *Y = dyn_cast<InputSection<ELFT>>(DB->Section);
+ if (!X || !Y)
+ return false;
+
+ // Ineligible sections are in the special equivalence class 0.
+ // They can never be the same in terms of the equivalence class.
+ if (X->Class[Current] == 0)
+ return false;
+
+ return X->Class[Current] == Y->Class[Current];
+ };
+
+ return std::equal(RelsA.begin(), RelsA.end(), RelsB.begin(), Eq);
}
// Compare "moving" part of two InputSections, namely relocation targets.
template <class ELFT>
bool ICF<ELFT>::equalsVariable(const InputSection<ELFT> *A,
const InputSection<ELFT> *B) {
- for (size_t I = 0, E = A->RelocSections.size(); I != E; ++I) {
- const Elf_Shdr *RA = A->RelocSections[I];
- const Elf_Shdr *RB = B->RelocSections[I];
- ELFFile<ELFT> &FileA = A->File->getObj();
- ELFFile<ELFT> &FileB = B->File->getObj();
- if (RA->sh_type == SHT_RELA) {
- if (!variableEq(A, B, FileA.relas(RA), FileB.relas(RB)))
- return false;
- } else {
- if (!variableEq(A, B, FileA.rels(RA), FileB.rels(RB)))
- return false;
- }
+ if (A->AreRelocsRela)
+ return variableEq(A, A->relas(), B, B->relas());
+ return variableEq(A, A->rels(), B, B->rels());
+}
+
+template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t Begin, size_t End) {
+ uint32_t Class = Sections[Begin]->Class[Current];
+ for (size_t I = Begin + 1; I < End; ++I)
+ if (Class != Sections[I]->Class[Current])
+ return I;
+ return End;
+}
+
+// Sections in the same equivalence class are contiguous in Sections
+// vector. Therefore, Sections vector can be considered as contiguous
+// groups of sections, grouped by the class.
+//
+// This function calls Fn on every group that starts within [Begin, End).
+// Note that a group must starts in that range but doesn't necessarily
+// have to end before End.
+template <class ELFT>
+void ICF<ELFT>::forEachClassRange(size_t Begin, size_t End,
+ std::function<void(size_t, size_t)> Fn) {
+ if (Begin > 0)
+ Begin = findBoundary(Begin - 1, End);
+
+ while (Begin < End) {
+ size_t Mid = findBoundary(Begin, Sections.size());
+ Fn(Begin, Mid);
+ Begin = Mid;
}
- return true;
+}
+
+// Call Fn on each equivalence class.
+template <class ELFT>
+void ICF<ELFT>::forEachClass(std::function<void(size_t, size_t)> Fn) {
+ // If threading is disabled or the number of sections are
+ // too small to use threading, call Fn sequentially.
+ if (!Config->Threads || Sections.size() < 1024) {
+ forEachClassRange(0, Sections.size(), Fn);
+ ++Cnt;
+ return;
+ }
+
+ Current = Cnt % 2;
+ Next = (Cnt + 1) % 2;
+
+ // Split sections into 256 shards and call Fn in parallel.
+ size_t NumShards = 256;
+ size_t Step = Sections.size() / NumShards;
+ forLoop(0, NumShards,
+ [&](size_t I) { forEachClassRange(I * Step, (I + 1) * Step, Fn); });
+ forEachClassRange(Step * NumShards, Sections.size(), Fn);
+ ++Cnt;
}
// The main function of ICF.
template <class ELFT> void ICF<ELFT>::run() {
- // Initially, we use hash values as section group IDs. Therefore,
- // if two sections have the same ID, they are likely (but not
- // guaranteed) to have the same static contents in terms of ICF.
- std::vector<InputSection<ELFT> *> V = getSections();
- for (InputSection<ELFT> *S : V)
- // Set MSB on to avoid collisions with serial group IDs
- S->GroupId = getHash(S) | (uint64_t(1) << 63);
-
- // From now on, sections in V are ordered so that sections in
- // the same group are consecutive in the vector.
- std::stable_sort(V.begin(), V.end(),
+ // Collect sections to merge.
+ for (InputSectionBase<ELFT> *Sec : Symtab<ELFT>::X->Sections)
+ if (auto *S = dyn_cast<InputSection<ELFT>>(Sec))
+ if (isEligible(S))
+ Sections.push_back(S);
+
+ // Initially, we use hash values to partition sections.
+ for (InputSection<ELFT> *S : Sections)
+ // Set MSB to 1 to avoid collisions with non-hash IDs.
+ S->Class[0] = getHash(S) | (1 << 31);
+
+ // From now on, sections in Sections vector are ordered so that sections
+ // in the same equivalence class are consecutive in the vector.
+ std::stable_sort(Sections.begin(), Sections.end(),
[](InputSection<ELFT> *A, InputSection<ELFT> *B) {
- return A->GroupId < B->GroupId;
+ return A->Class[0] < B->Class[0];
});
// Compare static contents and assign unique IDs for each static content.
- forEachGroup(V, equalsConstant);
+ forEachClass([&](size_t Begin, size_t End) { segregate(Begin, End, true); });
- // Split groups by comparing relocations until we get a convergence.
- int Cnt = 1;
- for (;;) {
- ++Cnt;
- uint64_t Id = NextId;
- forEachGroup(V, equalsVariable);
- if (Id == NextId)
- break;
- }
- log("ICF needed " + Twine(Cnt) + " iterations.");
-
- // Merge sections in the same group.
- for (auto I = V.begin(), E = V.end(); I != E;) {
- InputSection<ELFT> *Head = *I++;
- auto Bound = std::find_if(I, E, [&](InputSection<ELFT> *S) {
- return Head->GroupId != S->GroupId;
- });
- if (I == Bound)
- continue;
- log("selected " + Head->getSectionName());
- while (I != Bound) {
- InputSection<ELFT> *S = *I++;
- log(" removed " + S->getSectionName());
- Head->replace(S);
+ // Split groups by comparing relocations until convergence is obtained.
+ do {
+ Repeat = false;
+ forEachClass(
+ [&](size_t Begin, size_t End) { segregate(Begin, End, false); });
+ } while (Repeat);
+
+ log("ICF needed " + Twine(Cnt) + " iterations");
+
+ // Merge sections by the equivalence class.
+ forEachClass([&](size_t Begin, size_t End) {
+ if (End - Begin == 1)
+ return;
+
+ log("selected " + Sections[Begin]->Name);
+ for (size_t I = Begin + 1; I < End; ++I) {
+ log(" removed " + Sections[I]->Name);
+ Sections[Begin]->replace(Sections[I]);
}
- }
+ });
}
// ICF entry point function.
diff --git a/ELF/InputFiles.cpp b/ELF/InputFiles.cpp
index 426d9c39715d..2a8659921463 100644
--- a/ELF/InputFiles.cpp
+++ b/ELF/InputFiles.cpp
@@ -11,13 +11,20 @@
#include "Driver.h"
#include "Error.h"
#include "InputSection.h"
+#include "LinkerScript.h"
+#include "Memory.h"
#include "SymbolTable.h"
#include "Symbols.h"
+#include "SyntheticSections.h"
#include "llvm/ADT/STLExtras.h"
-#include "llvm/Bitcode/ReaderWriter.h"
+#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/CodeGen/Analysis.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
+#include "llvm/LTO/LTO.h"
+#include "llvm/MC/StringTableBuilder.h"
+#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
@@ -29,8 +36,68 @@ using namespace llvm::sys::fs;
using namespace lld;
using namespace lld::elf;
+namespace {
+// In ELF object file all section addresses are zero. If we have multiple
+// .text sections (when using -ffunction-section or comdat group) then
+// LLVM DWARF parser will not be able to parse .debug_line correctly, unless
+// we assign each section some unique address. This callback method assigns
+// each section an address equal to its offset in ELF object file.
+class ObjectInfo : public LoadedObjectInfo {
+public:
+ uint64_t getSectionLoadAddress(const object::SectionRef &Sec) const override {
+ return static_cast<const ELFSectionRef &>(Sec).getOffset();
+ }
+ std::unique_ptr<LoadedObjectInfo> clone() const override {
+ return std::unique_ptr<LoadedObjectInfo>();
+ }
+};
+}
+
+template <class ELFT> void elf::ObjectFile<ELFT>::initializeDwarfLine() {
+ std::unique_ptr<object::ObjectFile> Obj =
+ check(object::ObjectFile::createObjectFile(this->MB),
+ "createObjectFile failed");
+
+ ObjectInfo ObjInfo;
+ DWARFContextInMemory Dwarf(*Obj, &ObjInfo);
+ DwarfLine.reset(new DWARFDebugLine(&Dwarf.getLineSection().Relocs));
+ DataExtractor LineData(Dwarf.getLineSection().Data,
+ ELFT::TargetEndianness == support::little,
+ ELFT::Is64Bits ? 8 : 4);
+
+ // The second parameter is offset in .debug_line section
+ // for compilation unit (CU) of interest. We have only one
+ // CU (object file), so offset is always 0.
+ DwarfLine->getOrParseLineTable(LineData, 0);
+}
+
+// Returns source line information for a given offset
+// using DWARF debug info.
+template <class ELFT>
+std::string elf::ObjectFile<ELFT>::getLineInfo(InputSectionBase<ELFT> *S,
+ uintX_t Offset) {
+ if (!DwarfLine)
+ initializeDwarfLine();
+
+ // The offset to CU is 0.
+ const DWARFDebugLine::LineTable *Tbl = DwarfLine->getLineTable(0);
+ if (!Tbl)
+ return "";
+
+ // Use fake address calcuated by adding section file offset and offset in
+ // section. See comments for ObjectInfo class.
+ DILineInfo Info;
+ Tbl->getFileLineInfoForAddress(
+ S->Offset + Offset, nullptr,
+ DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info);
+ if (Info.Line == 0)
+ return "";
+ return convertToUnixPathSeparator(Info.FileName) + ":" +
+ std::to_string(Info.Line);
+}
+
// Returns "(internal)", "foo.a(bar.o)" or "baz.o".
-std::string elf::getFilename(const InputFile *F) {
+std::string elf::toString(const InputFile *F) {
if (!F)
return "(internal)";
if (!F->ArchiveName.empty())
@@ -38,15 +105,6 @@ std::string elf::getFilename(const InputFile *F) {
return F->getName();
}
-template <class ELFT>
-static ELFFile<ELFT> createELFObj(MemoryBufferRef MB) {
- std::error_code EC;
- ELFFile<ELFT> F(MB.getBuffer(), EC);
- if (EC)
- error(EC, "failed to read " + MB.getBufferIdentifier());
- return F;
-}
-
template <class ELFT> static ELFKind getELFKind() {
if (ELFT::TargetEndianness == support::little)
return ELFT::Is64Bits ? ELF64LEKind : ELF32LEKind;
@@ -54,41 +112,31 @@ template <class ELFT> static ELFKind getELFKind() {
}
template <class ELFT>
-ELFFileBase<ELFT>::ELFFileBase(Kind K, MemoryBufferRef MB)
- : InputFile(K, MB), ELFObj(createELFObj<ELFT>(MB)) {
+ELFFileBase<ELFT>::ELFFileBase(Kind K, MemoryBufferRef MB) : InputFile(K, MB) {
EKind = getELFKind<ELFT>();
- EMachine = ELFObj.getHeader()->e_machine;
+ EMachine = getObj().getHeader()->e_machine;
+ OSABI = getObj().getHeader()->e_ident[llvm::ELF::EI_OSABI];
}
template <class ELFT>
-typename ELFT::SymRange ELFFileBase<ELFT>::getElfSymbols(bool OnlyGlobals) {
- if (!Symtab)
- return Elf_Sym_Range(nullptr, nullptr);
- Elf_Sym_Range Syms = ELFObj.symbols(Symtab);
- uint32_t NumSymbols = std::distance(Syms.begin(), Syms.end());
- uint32_t FirstNonLocal = Symtab->sh_info;
- if (FirstNonLocal > NumSymbols)
- fatal(getFilename(this) + ": invalid sh_info in symbol table");
-
- if (OnlyGlobals)
- return makeArrayRef(Syms.begin() + FirstNonLocal, Syms.end());
- return makeArrayRef(Syms.begin(), Syms.end());
+typename ELFT::SymRange ELFFileBase<ELFT>::getGlobalSymbols() {
+ return makeArrayRef(Symbols.begin() + FirstNonLocal, Symbols.end());
}
template <class ELFT>
uint32_t ELFFileBase<ELFT>::getSectionIndex(const Elf_Sym &Sym) const {
- uint32_t I = Sym.st_shndx;
- if (I == ELF::SHN_XINDEX)
- return ELFObj.getExtendedSymbolTableIndex(&Sym, Symtab, SymtabSHNDX);
- if (I >= ELF::SHN_LORESERVE)
- return 0;
- return I;
+ return check(getObj().getSectionIndex(&Sym, Symbols, SymtabSHNDX));
}
-template <class ELFT> void ELFFileBase<ELFT>::initStringTable() {
- if (!Symtab)
- return;
- StringTable = check(ELFObj.getStringTableForSymtab(*Symtab));
+template <class ELFT>
+void ELFFileBase<ELFT>::initSymtab(ArrayRef<Elf_Shdr> Sections,
+ const Elf_Shdr *Symtab) {
+ FirstNonLocal = Symtab->sh_info;
+ Symbols = check(getObj().symbols(Symtab));
+ if (FirstNonLocal == 0 || FirstNonLocal > Symbols.size())
+ fatal(toString(this) + ": invalid sh_info in symbol table");
+
+ StringTable = check(getObj().getStringTableForSymtab(*Symtab, Sections));
}
template <class ELFT>
@@ -97,37 +145,25 @@ elf::ObjectFile<ELFT>::ObjectFile(MemoryBufferRef M)
template <class ELFT>
ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getNonLocalSymbols() {
- if (!this->Symtab)
- return this->SymbolBodies;
- uint32_t FirstNonLocal = this->Symtab->sh_info;
- return makeArrayRef(this->SymbolBodies).slice(FirstNonLocal);
+ return makeArrayRef(this->SymbolBodies).slice(this->FirstNonLocal);
}
template <class ELFT>
ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getLocalSymbols() {
- if (!this->Symtab)
+ if (this->SymbolBodies.empty())
return this->SymbolBodies;
- uint32_t FirstNonLocal = this->Symtab->sh_info;
- return makeArrayRef(this->SymbolBodies).slice(1, FirstNonLocal - 1);
+ return makeArrayRef(this->SymbolBodies).slice(1, this->FirstNonLocal - 1);
}
template <class ELFT>
ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getSymbols() {
- if (!this->Symtab)
+ if (this->SymbolBodies.empty())
return this->SymbolBodies;
return makeArrayRef(this->SymbolBodies).slice(1);
}
-template <class ELFT> uint32_t elf::ObjectFile<ELFT>::getMipsGp0() const {
- if (ELFT::Is64Bits && MipsOptions && MipsOptions->Reginfo)
- return MipsOptions->Reginfo->ri_gp_value;
- if (!ELFT::Is64Bits && MipsReginfo && MipsReginfo->Reginfo)
- return MipsReginfo->Reginfo->ri_gp_value;
- return 0;
-}
-
template <class ELFT>
-void elf::ObjectFile<ELFT>::parse(DenseSet<StringRef> &ComdatGroups) {
+void elf::ObjectFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
// Read section and symbol tables.
initializeSections(ComdatGroups);
initializeSymbols();
@@ -137,22 +173,25 @@ void elf::ObjectFile<ELFT>::parse(DenseSet<StringRef> &ComdatGroups) {
// They are identified and deduplicated by group name. This function
// returns a group name.
template <class ELFT>
-StringRef elf::ObjectFile<ELFT>::getShtGroupSignature(const Elf_Shdr &Sec) {
- const ELFFile<ELFT> &Obj = this->ELFObj;
- const Elf_Shdr *Symtab = check(Obj.getSection(Sec.sh_link));
- const Elf_Sym *Sym = Obj.getSymbol(Symtab, Sec.sh_info);
- StringRef Strtab = check(Obj.getStringTableForSymtab(*Symtab));
- return check(Sym->getName(Strtab));
+StringRef
+elf::ObjectFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
+ const Elf_Shdr &Sec) {
+ if (this->Symbols.empty())
+ this->initSymtab(Sections,
+ check(object::getSection<ELFT>(Sections, Sec.sh_link)));
+ const Elf_Sym *Sym =
+ check(object::getSymbol<ELFT>(this->Symbols, Sec.sh_info));
+ return check(Sym->getName(this->StringTable));
}
template <class ELFT>
ArrayRef<typename elf::ObjectFile<ELFT>::Elf_Word>
elf::ObjectFile<ELFT>::getShtGroupEntries(const Elf_Shdr &Sec) {
- const ELFFile<ELFT> &Obj = this->ELFObj;
+ const ELFFile<ELFT> &Obj = this->getObj();
ArrayRef<Elf_Word> Entries =
check(Obj.template getSectionContentsAsArray<Elf_Word>(&Sec));
if (Entries.empty() || Entries[0] != GRP_COMDAT)
- fatal(getFilename(this) + ": unsupported SHT_GROUP format");
+ fatal(toString(this) + ": unsupported SHT_GROUP format");
return Entries.slice(1);
}
@@ -163,15 +202,39 @@ bool elf::ObjectFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
if (Config->Optimize == 0)
return false;
+ // Do not merge sections if generating a relocatable object. It makes
+ // the code simpler because we do not need to update relocation addends
+ // to reflect changes introduced by merging. Instead of that we write
+ // such "merge" sections into separate OutputSections and keep SHF_MERGE
+ // / SHF_STRINGS flags and sh_entsize value to be able to perform merging
+ // later during a final linking.
+ if (Config->Relocatable)
+ return false;
+
+ // A mergeable section with size 0 is useless because they don't have
+ // any data to merge. A mergeable string section with size 0 can be
+ // argued as invalid because it doesn't end with a null character.
+ // We'll avoid a mess by handling them as if they were non-mergeable.
+ if (Sec.sh_size == 0)
+ return false;
+
+ // Check for sh_entsize. The ELF spec is not clear about the zero
+ // sh_entsize. It says that "the member [sh_entsize] contains 0 if
+ // the section does not hold a table of fixed-size entries". We know
+ // that Rust 1.13 produces a string mergeable section with a zero
+ // sh_entsize. Here we just accept it rather than being picky about it.
+ uintX_t EntSize = Sec.sh_entsize;
+ if (EntSize == 0)
+ return false;
+ if (Sec.sh_size % EntSize)
+ fatal(toString(this) +
+ ": SHF_MERGE section size must be a multiple of sh_entsize");
+
uintX_t Flags = Sec.sh_flags;
if (!(Flags & SHF_MERGE))
return false;
if (Flags & SHF_WRITE)
- fatal(getFilename(this) + ": writable SHF_MERGE section is not supported");
- uintX_t EntSize = Sec.sh_entsize;
- if (!EntSize || Sec.sh_size % EntSize)
- fatal(getFilename(this) +
- ": SHF_MERGE section size must be a multiple of sh_entsize");
+ fatal(toString(this) + ": writable SHF_MERGE section is not supported");
// Don't try to merge if the alignment is larger than the sh_entsize and this
// is not SHF_STRINGS.
@@ -187,74 +250,61 @@ bool elf::ObjectFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
template <class ELFT>
void elf::ObjectFile<ELFT>::initializeSections(
- DenseSet<StringRef> &ComdatGroups) {
- uint64_t Size = this->ELFObj.getNumSections();
+ DenseSet<CachedHashStringRef> &ComdatGroups) {
+ ArrayRef<Elf_Shdr> ObjSections = check(this->getObj().sections());
+ const ELFFile<ELFT> &Obj = this->getObj();
+ uint64_t Size = ObjSections.size();
Sections.resize(Size);
unsigned I = -1;
- const ELFFile<ELFT> &Obj = this->ELFObj;
- for (const Elf_Shdr &Sec : Obj.sections()) {
+ StringRef SectionStringTable = check(Obj.getSectionStringTable(ObjSections));
+ for (const Elf_Shdr &Sec : ObjSections) {
++I;
if (Sections[I] == &InputSection<ELFT>::Discarded)
continue;
+ // SHF_EXCLUDE'ed sections are discarded by the linker. However,
+ // if -r is given, we'll let the final link discard such sections.
+ // This is compatible with GNU.
+ if ((Sec.sh_flags & SHF_EXCLUDE) && !Config->Relocatable) {
+ Sections[I] = &InputSection<ELFT>::Discarded;
+ continue;
+ }
+
switch (Sec.sh_type) {
case SHT_GROUP:
Sections[I] = &InputSection<ELFT>::Discarded;
- if (ComdatGroups.insert(getShtGroupSignature(Sec)).second)
+ if (ComdatGroups.insert(CachedHashStringRef(
+ getShtGroupSignature(ObjSections, Sec)))
+ .second)
continue;
for (uint32_t SecIndex : getShtGroupEntries(Sec)) {
if (SecIndex >= Size)
- fatal(getFilename(this) + ": invalid section index in group: " +
+ fatal(toString(this) + ": invalid section index in group: " +
Twine(SecIndex));
Sections[SecIndex] = &InputSection<ELFT>::Discarded;
}
break;
case SHT_SYMTAB:
- this->Symtab = &Sec;
+ this->initSymtab(ObjSections, &Sec);
break;
case SHT_SYMTAB_SHNDX:
- this->SymtabSHNDX = check(Obj.getSHNDXTable(Sec));
+ this->SymtabSHNDX = check(Obj.getSHNDXTable(Sec, ObjSections));
break;
case SHT_STRTAB:
case SHT_NULL:
break;
- case SHT_RELA:
- case SHT_REL: {
- // This section contains relocation information.
- // If -r is given, we do not interpret or apply relocation
- // but just copy relocation sections to output.
- if (Config->Relocatable) {
- Sections[I] = new (IAlloc.Allocate()) InputSection<ELFT>(this, &Sec);
- break;
- }
-
- // Find the relocation target section and associate this
- // section with it.
- InputSectionBase<ELFT> *Target = getRelocTarget(Sec);
- if (!Target)
- break;
- if (auto *S = dyn_cast<InputSection<ELFT>>(Target)) {
- S->RelocSections.push_back(&Sec);
- break;
- }
- if (auto *S = dyn_cast<EhInputSection<ELFT>>(Target)) {
- if (S->RelocSection)
- fatal(
- getFilename(this) +
- ": multiple relocation sections to .eh_frame are not supported");
- S->RelocSection = &Sec;
- break;
- }
- fatal(getFilename(this) +
- ": relocations pointing to SHF_MERGE are not supported");
- }
- case SHT_ARM_ATTRIBUTES:
- // FIXME: ARM meta-data section. At present attributes are ignored,
- // they can be used to reason about object compatibility.
- Sections[I] = &InputSection<ELFT>::Discarded;
- break;
default:
- Sections[I] = createInputSection(Sec);
+ Sections[I] = createInputSection(Sec, SectionStringTable);
+ }
+
+ // .ARM.exidx sections have a reverse dependency on the InputSection they
+ // have a SHF_LINK_ORDER dependency, this is identified by the sh_link.
+ if (Sec.sh_flags & SHF_LINK_ORDER) {
+ if (Sec.sh_link >= Sections.size())
+ fatal(toString(this) + ": invalid sh_link index: " +
+ Twine(Sec.sh_link));
+ auto *IS = cast<InputSection<ELFT>>(Sections[Sec.sh_link]);
+ IS->DependentSection = Sections[I];
}
}
}
@@ -264,8 +314,7 @@ InputSectionBase<ELFT> *
elf::ObjectFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
uint32_t Idx = Sec.sh_info;
if (Idx >= Sections.size())
- fatal(getFilename(this) + ": invalid relocated section index: " +
- Twine(Idx));
+ fatal(toString(this) + ": invalid relocated section index: " + Twine(Idx));
InputSectionBase<ELFT> *Target = Sections[Idx];
// Strictly speaking, a relocation section must be included in the
@@ -275,14 +324,65 @@ elf::ObjectFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
return nullptr;
if (!Target)
- fatal(getFilename(this) + ": unsupported relocation reference");
+ fatal(toString(this) + ": unsupported relocation reference");
return Target;
}
template <class ELFT>
InputSectionBase<ELFT> *
-elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
- StringRef Name = check(this->ELFObj.getSectionName(&Sec));
+elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec,
+ StringRef SectionStringTable) {
+ StringRef Name =
+ check(this->getObj().getSectionName(&Sec, SectionStringTable));
+
+ switch (Sec.sh_type) {
+ case SHT_ARM_ATTRIBUTES:
+ // FIXME: ARM meta-data section. Retain the first attribute section
+ // we see. The eglibc ARM dynamic loaders require the presence of an
+ // attribute section for dlopen to work.
+ // In a full implementation we would merge all attribute sections.
+ if (In<ELFT>::ARMAttributes == nullptr) {
+ In<ELFT>::ARMAttributes = make<InputSection<ELFT>>(this, &Sec, Name);
+ return In<ELFT>::ARMAttributes;
+ }
+ return &InputSection<ELFT>::Discarded;
+ case SHT_RELA:
+ case SHT_REL: {
+ // This section contains relocation information.
+ // If -r is given, we do not interpret or apply relocation
+ // but just copy relocation sections to output.
+ if (Config->Relocatable)
+ return make<InputSection<ELFT>>(this, &Sec, Name);
+
+ // Find the relocation target section and associate this
+ // section with it.
+ InputSectionBase<ELFT> *Target = getRelocTarget(Sec);
+ if (!Target)
+ return nullptr;
+ if (Target->FirstRelocation)
+ fatal(toString(this) +
+ ": multiple relocation sections to one section are not supported");
+ if (!isa<InputSection<ELFT>>(Target) && !isa<EhInputSection<ELFT>>(Target))
+ fatal(toString(this) +
+ ": relocations pointing to SHF_MERGE are not supported");
+
+ size_t NumRelocations;
+ if (Sec.sh_type == SHT_RELA) {
+ ArrayRef<Elf_Rela> Rels = check(this->getObj().relas(&Sec));
+ Target->FirstRelocation = Rels.begin();
+ NumRelocations = Rels.size();
+ Target->AreRelocsRela = true;
+ } else {
+ ArrayRef<Elf_Rel> Rels = check(this->getObj().rels(&Sec));
+ Target->FirstRelocation = Rels.begin();
+ NumRelocations = Rels.size();
+ Target->AreRelocsRela = false;
+ }
+ assert(isUInt<31>(NumRelocations));
+ Target->NumRelocations = NumRelocations;
+ return nullptr;
+ }
+ }
// .note.GNU-stack is a marker section to control the presence of
// PT_GNU_STACK segment in outputs. Since the presence of the segment
@@ -296,39 +396,23 @@ elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
return &InputSection<ELFT>::Discarded;
}
- if (Config->StripDebug && Name.startswith(".debug"))
+ if (Config->Strip != StripPolicy::None && Name.startswith(".debug"))
return &InputSection<ELFT>::Discarded;
- // A MIPS object file has a special sections that contain register
- // usage info, which need to be handled by the linker specially.
- if (Config->EMachine == EM_MIPS) {
- if (Name == ".reginfo") {
- MipsReginfo.reset(new MipsReginfoInputSection<ELFT>(this, &Sec));
- return MipsReginfo.get();
- }
- if (Name == ".MIPS.options") {
- MipsOptions.reset(new MipsOptionsInputSection<ELFT>(this, &Sec));
- return MipsOptions.get();
- }
- }
-
// The linker merges EH (exception handling) frames and creates a
// .eh_frame_hdr section for runtime. So we handle them with a special
// class. For relocatable outputs, they are just passed through.
if (Name == ".eh_frame" && !Config->Relocatable)
- return new (EHAlloc.Allocate()) EhInputSection<ELFT>(this, &Sec);
+ return make<EhInputSection<ELFT>>(this, &Sec, Name);
if (shouldMerge(Sec))
- return new (MAlloc.Allocate()) MergeInputSection<ELFT>(this, &Sec);
- return new (IAlloc.Allocate()) InputSection<ELFT>(this, &Sec);
+ return make<MergeInputSection<ELFT>>(this, &Sec, Name);
+ return make<InputSection<ELFT>>(this, &Sec, Name);
}
template <class ELFT> void elf::ObjectFile<ELFT>::initializeSymbols() {
- this->initStringTable();
- Elf_Sym_Range Syms = this->getElfSymbols(false);
- uint32_t NumSymbols = std::distance(Syms.begin(), Syms.end());
- SymbolBodies.reserve(NumSymbols);
- for (const Elf_Sym &Sym : Syms)
+ SymbolBodies.reserve(this->Symbols.size());
+ for (const Elf_Sym &Sym : this->Symbols)
SymbolBodies.push_back(createSymbolBody(&Sym));
}
@@ -336,12 +420,23 @@ template <class ELFT>
InputSectionBase<ELFT> *
elf::ObjectFile<ELFT>::getSection(const Elf_Sym &Sym) const {
uint32_t Index = this->getSectionIndex(Sym);
- if (Index == 0)
- return nullptr;
- if (Index >= Sections.size() || !Sections[Index])
- fatal(getFilename(this) + ": invalid section index: " + Twine(Index));
+ if (Index >= Sections.size())
+ fatal(toString(this) + ": invalid section index: " + Twine(Index));
InputSectionBase<ELFT> *S = Sections[Index];
- if (S == &InputSectionBase<ELFT>::Discarded)
+
+ // We found that GNU assembler 2.17.50 [FreeBSD] 2007-07-03 could
+ // generate broken objects. STT_SECTION/STT_NOTYPE symbols can be
+ // associated with SHT_REL[A]/SHT_SYMTAB/SHT_STRTAB sections.
+ // In this case it is fine for section to be null here as we do not
+ // allocate sections of these types.
+ if (!S) {
+ if (Index == 0 || Sym.getType() == STT_SECTION ||
+ Sym.getType() == STT_NOTYPE)
+ return nullptr;
+ fatal(toString(this) + ": invalid section index: " + Twine(Index));
+ }
+
+ if (S == &InputSection<ELFT>::Discarded)
return S;
return S->Repl;
}
@@ -350,11 +445,26 @@ template <class ELFT>
SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
int Binding = Sym->getBinding();
InputSectionBase<ELFT> *Sec = getSection(*Sym);
+
+ uint8_t StOther = Sym->st_other;
+ uint8_t Type = Sym->getType();
+ uintX_t Value = Sym->st_value;
+ uintX_t Size = Sym->st_size;
+
if (Binding == STB_LOCAL) {
+ if (Sym->getType() == STT_FILE)
+ SourceFile = check(Sym->getName(this->StringTable));
+
+ if (this->StringTable.size() <= Sym->st_name)
+ fatal(toString(this) + ": invalid symbol name offset");
+
+ StringRefZ Name = this->StringTable.data() + Sym->st_name;
if (Sym->st_shndx == SHN_UNDEF)
- return new (this->Alloc)
- Undefined(Sym->st_name, Sym->st_other, Sym->getType(), this);
- return new (this->Alloc) DefinedRegular<ELFT>(*Sym, Sec);
+ return new (BAlloc)
+ Undefined(Name, /*IsLocal=*/true, StOther, Type, this);
+
+ return new (BAlloc) DefinedRegular<ELFT>(Name, /*IsLocal=*/true, StOther,
+ Type, Value, Size, Sec, this);
}
StringRef Name = check(Sym->getName(this->StringTable));
@@ -362,33 +472,38 @@ SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
switch (Sym->st_shndx) {
case SHN_UNDEF:
return elf::Symtab<ELFT>::X
- ->addUndefined(Name, Binding, Sym->st_other, Sym->getType(),
- /*CanOmitFromDynSym*/ false, this)
+ ->addUndefined(Name, /*IsLocal=*/false, Binding, StOther, Type,
+ /*CanOmitFromDynSym=*/false, this)
->body();
case SHN_COMMON:
+ if (Value == 0 || Value >= UINT32_MAX)
+ fatal(toString(this) + ": common symbol '" + Name +
+ "' has invalid alignment: " + Twine(Value));
return elf::Symtab<ELFT>::X
- ->addCommon(Name, Sym->st_size, Sym->st_value, Binding, Sym->st_other,
- Sym->getType(), this)
+ ->addCommon(Name, Size, Value, Binding, StOther, Type, this)
->body();
}
switch (Binding) {
default:
- fatal(getFilename(this) + ": unexpected binding: " + Twine(Binding));
+ fatal(toString(this) + ": unexpected binding: " + Twine(Binding));
case STB_GLOBAL:
case STB_WEAK:
case STB_GNU_UNIQUE:
if (Sec == &InputSection<ELFT>::Discarded)
return elf::Symtab<ELFT>::X
- ->addUndefined(Name, Binding, Sym->st_other, Sym->getType(),
- /*CanOmitFromDynSym*/ false, this)
+ ->addUndefined(Name, /*IsLocal=*/false, Binding, StOther, Type,
+ /*CanOmitFromDynSym=*/false, this)
->body();
- return elf::Symtab<ELFT>::X->addRegular(Name, *Sym, Sec)->body();
+ return elf::Symtab<ELFT>::X
+ ->addRegular(Name, StOther, Type, Value, Size, Binding, Sec, this)
+ ->body();
}
}
template <class ELFT> void ArchiveFile::parse() {
- File = check(Archive::create(MB), "failed to parse archive");
+ File = check(Archive::create(MB),
+ MB.getBufferIdentifier() + ": failed to parse archive");
// Read the symbol table to construct Lazy objects.
for (const Archive::Symbol &Sym : File->symbols())
@@ -396,13 +511,14 @@ template <class ELFT> void ArchiveFile::parse() {
}
// Returns a buffer pointing to a member file containing a given symbol.
-MemoryBufferRef ArchiveFile::getMember(const Archive::Symbol *Sym) {
+std::pair<MemoryBufferRef, uint64_t>
+ArchiveFile::getMember(const Archive::Symbol *Sym) {
Archive::Child C =
check(Sym->getMember(),
"could not get the member for symbol " + Sym->getName());
if (!Seen.insert(C.getChildOffset()).second)
- return MemoryBufferRef();
+ return {MemoryBufferRef(), 0};
MemoryBufferRef Ret =
check(C.getMemoryBufferRef(),
@@ -412,8 +528,9 @@ MemoryBufferRef ArchiveFile::getMember(const Archive::Symbol *Sym) {
if (C.getParent()->isThin() && Driver->Cpio)
Driver->Cpio->append(relativeToRoot(check(C.getFullName())),
Ret.getBuffer());
-
- return Ret;
+ if (C.getParent()->isThin())
+ return {Ret, 0};
+ return {Ret, C.getChildOffset()};
}
template <class ELFT>
@@ -423,32 +540,29 @@ SharedFile<ELFT>::SharedFile(MemoryBufferRef M)
template <class ELFT>
const typename ELFT::Shdr *
SharedFile<ELFT>::getSection(const Elf_Sym &Sym) const {
- uint32_t Index = this->getSectionIndex(Sym);
- if (Index == 0)
- return nullptr;
- return check(this->ELFObj.getSection(Index));
+ return check(
+ this->getObj().getSection(&Sym, this->Symbols, this->SymtabSHNDX));
}
// Partially parse the shared object file so that we can call
// getSoName on this object.
template <class ELFT> void SharedFile<ELFT>::parseSoName() {
- typedef typename ELFT::Dyn Elf_Dyn;
- typedef typename ELFT::uint uintX_t;
const Elf_Shdr *DynamicSec = nullptr;
- const ELFFile<ELFT> Obj = this->ELFObj;
- for (const Elf_Shdr &Sec : Obj.sections()) {
+ const ELFFile<ELFT> Obj = this->getObj();
+ ArrayRef<Elf_Shdr> Sections = check(Obj.sections());
+ for (const Elf_Shdr &Sec : Sections) {
switch (Sec.sh_type) {
default:
continue;
case SHT_DYNSYM:
- this->Symtab = &Sec;
+ this->initSymtab(Sections, &Sec);
break;
case SHT_DYNAMIC:
DynamicSec = &Sec;
break;
case SHT_SYMTAB_SHNDX:
- this->SymtabSHNDX = check(Obj.getSHNDXTable(Sec));
+ this->SymtabSHNDX = check(Obj.getSHNDXTable(Sec, Sections));
break;
case SHT_GNU_versym:
this->VersymSec = &Sec;
@@ -459,20 +573,25 @@ template <class ELFT> void SharedFile<ELFT>::parseSoName() {
}
}
- this->initStringTable();
+ if (this->VersymSec && this->Symbols.empty())
+ error("SHT_GNU_versym should be associated with symbol table");
+
+ // DSOs are identified by soname, and they usually contain
+ // DT_SONAME tag in their header. But if they are missing,
+ // filenames are used as default sonames.
SoName = sys::path::filename(this->getName());
if (!DynamicSec)
return;
- auto *Begin =
- reinterpret_cast<const Elf_Dyn *>(Obj.base() + DynamicSec->sh_offset);
- const Elf_Dyn *End = Begin + DynamicSec->sh_size / sizeof(Elf_Dyn);
- for (const Elf_Dyn &Dyn : make_range(Begin, End)) {
+ ArrayRef<Elf_Dyn> Arr =
+ check(Obj.template getSectionContentsAsArray<Elf_Dyn>(DynamicSec),
+ toString(this) + ": getSectionContentsAsArray failed");
+ for (const Elf_Dyn &Dyn : Arr) {
if (Dyn.d_tag == DT_SONAME) {
uintX_t Val = Dyn.getVal();
if (Val >= this->StringTable.size())
- fatal(getFilename(this) + ": invalid DT_SONAME entry");
+ fatal(toString(this) + ": invalid DT_SONAME entry");
SoName = StringRef(this->StringTable.data() + Val);
return;
}
@@ -494,9 +613,9 @@ SharedFile<ELFT>::parseVerdefs(const Elf_Versym *&Versym) {
return Verdefs;
// The location of the first global versym entry.
- Versym = reinterpret_cast<const Elf_Versym *>(this->ELFObj.base() +
- VersymSec->sh_offset) +
- this->Symtab->sh_info;
+ const char *Base = this->MB.getBuffer().data();
+ Versym = reinterpret_cast<const Elf_Versym *>(Base + VersymSec->sh_offset) +
+ this->FirstNonLocal;
// We cannot determine the largest verdef identifier without inspecting
// every Elf_Verdef, but both bfd and gold assign verdef identifiers
@@ -507,7 +626,7 @@ SharedFile<ELFT>::parseVerdefs(const Elf_Versym *&Versym) {
// Build the Verdefs array by following the chain of Elf_Verdef objects
// from the start of the .gnu.version_d section.
- const uint8_t *Verdef = this->ELFObj.base() + VerdefSec->sh_offset;
+ const char *Verdef = Base + VerdefSec->sh_offset;
for (unsigned I = 0; I != VerdefCount; ++I) {
auto *CurVerdef = reinterpret_cast<const Elf_Verdef *>(Verdef);
Verdef += CurVerdef->vd_next;
@@ -526,7 +645,7 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() {
const Elf_Versym *Versym = nullptr;
std::vector<const Elf_Verdef *> Verdefs = parseVerdefs(Versym);
- Elf_Sym_Range Syms = this->getElfSymbols(true);
+ Elf_Sym_Range Syms = this->getGlobalSymbols();
for (const Elf_Sym &Sym : Syms) {
unsigned VersymIndex = 0;
if (Versym) {
@@ -552,18 +671,16 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() {
}
}
-static ELFKind getELFKind(MemoryBufferRef MB) {
- std::string TripleStr = getBitcodeTargetTriple(MB, Driver->Context);
- Triple TheTriple(TripleStr);
- bool Is64Bits = TheTriple.isArch64Bit();
- if (TheTriple.isLittleEndian())
- return Is64Bits ? ELF64LEKind : ELF32LEKind;
- return Is64Bits ? ELF64BEKind : ELF32BEKind;
+static ELFKind getBitcodeELFKind(MemoryBufferRef MB) {
+ Triple T(check(getBitcodeTargetTriple(MB)));
+ if (T.isLittleEndian())
+ return T.isArch64Bit() ? ELF64LEKind : ELF32LEKind;
+ return T.isArch64Bit() ? ELF64BEKind : ELF32BEKind;
}
-static uint8_t getMachineKind(MemoryBufferRef MB) {
- std::string TripleStr = getBitcodeTargetTriple(MB, Driver->Context);
- switch (Triple(TripleStr).getArch()) {
+static uint8_t getBitcodeMachineKind(MemoryBufferRef MB) {
+ Triple T(check(getBitcodeTargetTriple(MB)));
+ switch (T.getArch()) {
case Triple::aarch64:
return EM_AARCH64;
case Triple::arm:
@@ -578,23 +695,22 @@ static uint8_t getMachineKind(MemoryBufferRef MB) {
case Triple::ppc64:
return EM_PPC64;
case Triple::x86:
- return EM_386;
+ return T.isOSIAMCU() ? EM_IAMCU : EM_386;
case Triple::x86_64:
return EM_X86_64;
default:
fatal(MB.getBufferIdentifier() +
- ": could not infer e_machine from bitcode target triple " +
- TripleStr);
+ ": could not infer e_machine from bitcode target triple " + T.str());
}
}
BitcodeFile::BitcodeFile(MemoryBufferRef MB) : InputFile(BitcodeKind, MB) {
- EKind = getELFKind(MB);
- EMachine = getMachineKind(MB);
+ EKind = getBitcodeELFKind(MB);
+ EMachine = getBitcodeMachineKind(MB);
}
-static uint8_t getGvVisibility(const GlobalValue *GV) {
- switch (GV->getVisibility()) {
+static uint8_t mapVisibility(GlobalValue::VisibilityTypes GvVisibility) {
+ switch (GvVisibility) {
case GlobalValue::DefaultVisibility:
return STV_DEFAULT;
case GlobalValue::HiddenVisibility:
@@ -606,124 +722,134 @@ static uint8_t getGvVisibility(const GlobalValue *GV) {
}
template <class ELFT>
-Symbol *BitcodeFile::createSymbol(const DenseSet<const Comdat *> &KeptComdats,
- const IRObjectFile &Obj,
- const BasicSymbolRef &Sym) {
- const GlobalValue *GV = Obj.getSymbolGV(Sym.getRawDataRefImpl());
-
- SmallString<64> Name;
- raw_svector_ostream OS(Name);
- Sym.printName(OS);
- StringRef NameRef = Saver.save(StringRef(Name));
-
- uint32_t Flags = Sym.getFlags();
- bool IsWeak = Flags & BasicSymbolRef::SF_Weak;
- uint32_t Binding = IsWeak ? STB_WEAK : STB_GLOBAL;
-
- uint8_t Type = STT_NOTYPE;
- bool CanOmitFromDynSym = false;
- // FIXME: Expose a thread-local flag for module asm symbols.
- if (GV) {
- if (GV->isThreadLocal())
- Type = STT_TLS;
- CanOmitFromDynSym = canBeOmittedFromSymbolTable(GV);
- }
-
- uint8_t Visibility;
- if (GV)
- Visibility = getGvVisibility(GV);
- else
- // FIXME: Set SF_Hidden flag correctly for module asm symbols, and expose
- // protected visibility.
- Visibility = STV_DEFAULT;
-
- if (GV)
- if (const Comdat *C = GV->getComdat())
- if (!KeptComdats.count(C))
- return Symtab<ELFT>::X->addUndefined(NameRef, Binding, Visibility, Type,
- CanOmitFromDynSym, this);
+static Symbol *createBitcodeSymbol(const std::vector<bool> &KeptComdats,
+ const lto::InputFile::Symbol &ObjSym,
+ BitcodeFile *F) {
+ StringRef NameRef = Saver.save(ObjSym.getName());
+ uint32_t Flags = ObjSym.getFlags();
+ uint32_t Binding = (Flags & BasicSymbolRef::SF_Weak) ? STB_WEAK : STB_GLOBAL;
+
+ uint8_t Type = ObjSym.isTLS() ? STT_TLS : STT_NOTYPE;
+ uint8_t Visibility = mapVisibility(ObjSym.getVisibility());
+ bool CanOmitFromDynSym = ObjSym.canBeOmittedFromSymbolTable();
+
+ int C = check(ObjSym.getComdatIndex());
+ if (C != -1 && !KeptComdats[C])
+ return Symtab<ELFT>::X->addUndefined(NameRef, /*IsLocal=*/false, Binding,
+ Visibility, Type, CanOmitFromDynSym,
+ F);
- const Module &M = Obj.getModule();
if (Flags & BasicSymbolRef::SF_Undefined)
- return Symtab<ELFT>::X->addUndefined(NameRef, Binding, Visibility, Type,
- CanOmitFromDynSym, this);
- if (Flags & BasicSymbolRef::SF_Common) {
- // FIXME: Set SF_Common flag correctly for module asm symbols, and expose
- // size and alignment.
- assert(GV);
- const DataLayout &DL = M.getDataLayout();
- uint64_t Size = DL.getTypeAllocSize(GV->getValueType());
- return Symtab<ELFT>::X->addCommon(NameRef, Size, GV->getAlignment(),
- Binding, Visibility, STT_OBJECT, this);
- }
- return Symtab<ELFT>::X->addBitcode(NameRef, IsWeak, Visibility, Type,
- CanOmitFromDynSym, this);
-}
+ return Symtab<ELFT>::X->addUndefined(NameRef, /*IsLocal=*/false, Binding,
+ Visibility, Type, CanOmitFromDynSym,
+ F);
+
+ if (Flags & BasicSymbolRef::SF_Common)
+ return Symtab<ELFT>::X->addCommon(NameRef, ObjSym.getCommonSize(),
+ ObjSym.getCommonAlignment(), Binding,
+ Visibility, STT_OBJECT, F);
-bool BitcodeFile::shouldSkip(uint32_t Flags) {
- return !(Flags & BasicSymbolRef::SF_Global) ||
- (Flags & BasicSymbolRef::SF_FormatSpecific);
+ return Symtab<ELFT>::X->addBitcode(NameRef, Binding, Visibility, Type,
+ CanOmitFromDynSym, F);
}
template <class ELFT>
-void BitcodeFile::parse(DenseSet<StringRef> &ComdatGroups) {
- Obj = check(IRObjectFile::create(MB, Driver->Context));
- const Module &M = Obj->getModule();
-
- DenseSet<const Comdat *> KeptComdats;
- for (const auto &P : M.getComdatSymbolTable()) {
- StringRef N = Saver.save(P.first());
- if (ComdatGroups.insert(N).second)
- KeptComdats.insert(&P.second);
+void BitcodeFile::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
+
+ // Here we pass a new MemoryBufferRef which is identified by ArchiveName
+ // (the fully resolved path of the archive) + member name + offset of the
+ // member in the archive.
+ // ThinLTO uses the MemoryBufferRef identifier to access its internal
+ // data structures and if two archives define two members with the same name,
+ // this causes a collision which result in only one of the objects being
+ // taken into consideration at LTO time (which very likely causes undefined
+ // symbols later in the link stage).
+ Obj = check(lto::InputFile::create(MemoryBufferRef(
+ MB.getBuffer(), Saver.save(ArchiveName + MB.getBufferIdentifier() +
+ utostr(OffsetInArchive)))));
+
+ std::vector<bool> KeptComdats;
+ for (StringRef S : Obj->getComdatTable()) {
+ StringRef N = Saver.save(S);
+ KeptComdats.push_back(ComdatGroups.insert(CachedHashStringRef(N)).second);
}
- for (const BasicSymbolRef &Sym : Obj->symbols())
- if (!shouldSkip(Sym.getFlags()))
- Symbols.push_back(createSymbol<ELFT>(KeptComdats, *Obj, Sym));
+ for (const lto::InputFile::Symbol &ObjSym : Obj->symbols())
+ Symbols.push_back(createBitcodeSymbol<ELFT>(KeptComdats, ObjSym, this));
}
template <template <class> class T>
-static std::unique_ptr<InputFile> createELFFile(MemoryBufferRef MB) {
+static InputFile *createELFFile(MemoryBufferRef MB) {
unsigned char Size;
unsigned char Endian;
std::tie(Size, Endian) = getElfArchType(MB.getBuffer());
if (Endian != ELFDATA2LSB && Endian != ELFDATA2MSB)
- fatal("invalid data encoding: " + MB.getBufferIdentifier());
+ fatal(MB.getBufferIdentifier() + ": invalid data encoding");
+
+ size_t BufSize = MB.getBuffer().size();
+ if ((Size == ELFCLASS32 && BufSize < sizeof(Elf32_Ehdr)) ||
+ (Size == ELFCLASS64 && BufSize < sizeof(Elf64_Ehdr)))
+ fatal(MB.getBufferIdentifier() + ": file is too short");
- std::unique_ptr<InputFile> Obj;
+ InputFile *Obj;
if (Size == ELFCLASS32 && Endian == ELFDATA2LSB)
- Obj.reset(new T<ELF32LE>(MB));
+ Obj = make<T<ELF32LE>>(MB);
else if (Size == ELFCLASS32 && Endian == ELFDATA2MSB)
- Obj.reset(new T<ELF32BE>(MB));
+ Obj = make<T<ELF32BE>>(MB);
else if (Size == ELFCLASS64 && Endian == ELFDATA2LSB)
- Obj.reset(new T<ELF64LE>(MB));
+ Obj = make<T<ELF64LE>>(MB);
else if (Size == ELFCLASS64 && Endian == ELFDATA2MSB)
- Obj.reset(new T<ELF64BE>(MB));
+ Obj = make<T<ELF64BE>>(MB);
else
- fatal("invalid file class: " + MB.getBufferIdentifier());
+ fatal(MB.getBufferIdentifier() + ": invalid file class");
if (!Config->FirstElf)
- Config->FirstElf = Obj.get();
+ Config->FirstElf = Obj;
return Obj;
}
+template <class ELFT> void BinaryFile::parse() {
+ StringRef Buf = MB.getBuffer();
+ ArrayRef<uint8_t> Data =
+ makeArrayRef<uint8_t>((const uint8_t *)Buf.data(), Buf.size());
+
+ std::string Filename = MB.getBufferIdentifier();
+ std::transform(Filename.begin(), Filename.end(), Filename.begin(),
+ [](char C) { return isalnum(C) ? C : '_'; });
+ Filename = "_binary_" + Filename;
+ StringRef StartName = Saver.save(Twine(Filename) + "_start");
+ StringRef EndName = Saver.save(Twine(Filename) + "_end");
+ StringRef SizeName = Saver.save(Twine(Filename) + "_size");
+
+ auto *Section =
+ make<InputSection<ELFT>>(SHF_ALLOC, SHT_PROGBITS, 8, Data, ".data");
+ Sections.push_back(Section);
+
+ elf::Symtab<ELFT>::X->addRegular(StartName, STV_DEFAULT, STT_OBJECT, 0, 0,
+ STB_GLOBAL, Section, nullptr);
+ elf::Symtab<ELFT>::X->addRegular(EndName, STV_DEFAULT, STT_OBJECT,
+ Data.size(), 0, STB_GLOBAL, Section,
+ nullptr);
+ elf::Symtab<ELFT>::X->addRegular(SizeName, STV_DEFAULT, STT_OBJECT,
+ Data.size(), 0, STB_GLOBAL, nullptr,
+ nullptr);
+}
+
static bool isBitcode(MemoryBufferRef MB) {
using namespace sys::fs;
return identify_magic(MB.getBuffer()) == file_magic::bitcode;
}
-std::unique_ptr<InputFile> elf::createObjectFile(MemoryBufferRef MB,
- StringRef ArchiveName) {
- std::unique_ptr<InputFile> F;
- if (isBitcode(MB))
- F.reset(new BitcodeFile(MB));
- else
- F = createELFFile<ObjectFile>(MB);
+InputFile *elf::createObjectFile(MemoryBufferRef MB, StringRef ArchiveName,
+ uint64_t OffsetInArchive) {
+ InputFile *F =
+ isBitcode(MB) ? make<BitcodeFile>(MB) : createELFFile<ObjectFile>(MB);
F->ArchiveName = ArchiveName;
+ F->OffsetInArchive = OffsetInArchive;
return F;
}
-std::unique_ptr<InputFile> elf::createSharedFile(MemoryBufferRef MB) {
+InputFile *elf::createSharedFile(MemoryBufferRef MB) {
return createELFFile<SharedFile>(MB);
}
@@ -734,8 +860,7 @@ MemoryBufferRef LazyObjectFile::getBuffer() {
return MB;
}
-template <class ELFT>
-void LazyObjectFile::parse() {
+template <class ELFT> void LazyObjectFile::parse() {
for (StringRef Sym : getSymbols())
Symtab<ELFT>::X->addLazyObject(Sym, *this);
}
@@ -745,13 +870,14 @@ template <class ELFT> std::vector<StringRef> LazyObjectFile::getElfSymbols() {
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::SymRange Elf_Sym_Range;
- const ELFFile<ELFT> Obj = createELFObj<ELFT>(this->MB);
- for (const Elf_Shdr &Sec : Obj.sections()) {
+ const ELFFile<ELFT> Obj(this->MB.getBuffer());
+ ArrayRef<Elf_Shdr> Sections = check(Obj.sections());
+ for (const Elf_Shdr &Sec : Sections) {
if (Sec.sh_type != SHT_SYMTAB)
continue;
- Elf_Sym_Range Syms = Obj.symbols(&Sec);
+ Elf_Sym_Range Syms = check(Obj.symbols(&Sec));
uint32_t FirstNonLocal = Sec.sh_info;
- StringRef StringTable = check(Obj.getStringTableForSymtab(Sec));
+ StringRef StringTable = check(Obj.getStringTableForSymtab(Sec, Sections));
std::vector<StringRef> V;
for (const Elf_Sym &Sym : Syms.slice(FirstNonLocal))
if (Sym.st_shndx != SHN_UNDEF)
@@ -762,21 +888,11 @@ template <class ELFT> std::vector<StringRef> LazyObjectFile::getElfSymbols() {
}
std::vector<StringRef> LazyObjectFile::getBitcodeSymbols() {
- LLVMContext Context;
- std::unique_ptr<IRObjectFile> Obj =
- check(IRObjectFile::create(this->MB, Context));
+ std::unique_ptr<lto::InputFile> Obj = check(lto::InputFile::create(this->MB));
std::vector<StringRef> V;
- for (const BasicSymbolRef &Sym : Obj->symbols()) {
- uint32_t Flags = Sym.getFlags();
- if (BitcodeFile::shouldSkip(Flags))
- continue;
- if (Flags & BasicSymbolRef::SF_Undefined)
- continue;
- SmallString<64> Name;
- raw_svector_ostream OS(Name);
- Sym.printName(OS);
- V.push_back(Saver.save(StringRef(Name)));
- }
+ for (const lto::InputFile::Symbol &Sym : Obj->symbols())
+ if (!(Sym.getFlags() & BasicSymbolRef::SF_Undefined))
+ V.push_back(Saver.save(Sym.getName()));
return V;
}
@@ -803,10 +919,10 @@ template void ArchiveFile::parse<ELF32BE>();
template void ArchiveFile::parse<ELF64LE>();
template void ArchiveFile::parse<ELF64BE>();
-template void BitcodeFile::parse<ELF32LE>(DenseSet<StringRef> &);
-template void BitcodeFile::parse<ELF32BE>(DenseSet<StringRef> &);
-template void BitcodeFile::parse<ELF64LE>(DenseSet<StringRef> &);
-template void BitcodeFile::parse<ELF64BE>(DenseSet<StringRef> &);
+template void BitcodeFile::parse<ELF32LE>(DenseSet<CachedHashStringRef> &);
+template void BitcodeFile::parse<ELF32BE>(DenseSet<CachedHashStringRef> &);
+template void BitcodeFile::parse<ELF64LE>(DenseSet<CachedHashStringRef> &);
+template void BitcodeFile::parse<ELF64BE>(DenseSet<CachedHashStringRef> &);
template void LazyObjectFile::parse<ELF32LE>();
template void LazyObjectFile::parse<ELF32BE>();
@@ -827,3 +943,8 @@ template class elf::SharedFile<ELF32LE>;
template class elf::SharedFile<ELF32BE>;
template class elf::SharedFile<ELF64LE>;
template class elf::SharedFile<ELF64BE>;
+
+template void BinaryFile::parse<ELF32LE>();
+template void BinaryFile::parse<ELF32BE>();
+template void BinaryFile::parse<ELF64LE>();
+template void BinaryFile::parse<ELF64BE>();
diff --git a/ELF/InputFiles.h b/ELF/InputFiles.h
index 79cb751494b3..aba1d71379b0 100644
--- a/ELF/InputFiles.h
+++ b/ELF/InputFiles.h
@@ -16,16 +16,24 @@
#include "Symbols.h"
#include "lld/Core/LLVM.h"
+#include "lld/Core/Reproduce.h"
+#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/Comdat.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
#include "llvm/Object/IRObjectFile.h"
-#include "llvm/Support/StringSaver.h"
#include <map>
+namespace llvm {
+class DWARFDebugLine;
+namespace lto {
+class InputFile;
+}
+}
+
namespace lld {
namespace elf {
@@ -44,6 +52,7 @@ public:
LazyObjectKind,
ArchiveKind,
BitcodeKind,
+ BinaryKind,
};
Kind kind() const { return FileKind; }
@@ -56,10 +65,17 @@ public:
// string for creating error messages.
StringRef ArchiveName;
+ // If this file is in an archive, the member contains the offset of
+ // the file in the archive. Otherwise, it's just zero. We store this
+ // field so that we can pass it to lib/LTO in order to disambiguate
+ // between objects.
+ uint64_t OffsetInArchive;
+
// If this is an architecture-specific file, the following members
// have ELF type (i.e. ELF{32,64}{LE,BE}) and target machine type.
ELFKind EKind = ELFNoneKind;
uint16_t EMachine = llvm::ELF::EM_NONE;
+ uint8_t OSABI = 0;
protected:
InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
@@ -69,7 +85,7 @@ private:
};
// Returns "(internal)", "foo.a(bar.o)" or "baz.o".
-std::string getFilename(const InputFile *F);
+std::string toString(const InputFile *F);
template <typename ELFT> class ELFFileBase : public InputFile {
public:
@@ -84,37 +100,37 @@ public:
return K == ObjectKind || K == SharedKind;
}
- const llvm::object::ELFFile<ELFT> &getObj() const { return ELFObj; }
- llvm::object::ELFFile<ELFT> &getObj() { return ELFObj; }
-
- uint8_t getOSABI() const {
- return getObj().getHeader()->e_ident[llvm::ELF::EI_OSABI];
+ llvm::object::ELFFile<ELFT> getObj() const {
+ return llvm::object::ELFFile<ELFT>(MB.getBuffer());
}
StringRef getStringTable() const { return StringTable; }
uint32_t getSectionIndex(const Elf_Sym &Sym) const;
- Elf_Sym_Range getElfSymbols(bool OnlyGlobals);
+ Elf_Sym_Range getGlobalSymbols();
protected:
- llvm::object::ELFFile<ELFT> ELFObj;
- const Elf_Shdr *Symtab = nullptr;
+ ArrayRef<Elf_Sym> Symbols;
+ uint32_t FirstNonLocal = 0;
ArrayRef<Elf_Word> SymtabSHNDX;
StringRef StringTable;
- void initStringTable();
+ void initSymtab(ArrayRef<Elf_Shdr> Sections, const Elf_Shdr *Symtab);
};
// .o file.
template <class ELFT> class ObjectFile : public ELFFileBase<ELFT> {
typedef ELFFileBase<ELFT> Base;
+ typedef typename ELFT::Rel Elf_Rel;
+ typedef typename ELFT::Rela Elf_Rela;
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::SymRange Elf_Sym_Range;
typedef typename ELFT::Word Elf_Word;
typedef typename ELFT::uint uintX_t;
- StringRef getShtGroupSignature(const Elf_Shdr &Sec);
+ StringRef getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
+ const Elf_Shdr &Sec);
ArrayRef<Elf_Word> getShtGroupEntries(const Elf_Shdr &Sec);
public:
@@ -127,40 +143,49 @@ public:
ArrayRef<SymbolBody *> getNonLocalSymbols();
explicit ObjectFile(MemoryBufferRef M);
- void parse(llvm::DenseSet<StringRef> &ComdatGroups);
+ void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
ArrayRef<InputSectionBase<ELFT> *> getSections() const { return Sections; }
InputSectionBase<ELFT> *getSection(const Elf_Sym &Sym) const;
SymbolBody &getSymbolBody(uint32_t SymbolIndex) const {
+ if (SymbolIndex >= SymbolBodies.size())
+ fatal(toString(this) + ": invalid symbol index");
return *SymbolBodies[SymbolIndex];
}
- template <typename RelT> SymbolBody &getRelocTargetSym(const RelT &Rel) const {
+ template <typename RelT>
+ SymbolBody &getRelocTargetSym(const RelT &Rel) const {
uint32_t SymIndex = Rel.getSymbol(Config->Mips64EL);
return getSymbolBody(SymIndex);
}
- const Elf_Shdr *getSymbolTable() const { return this->Symtab; };
+ // Returns source line information for a given offset.
+ // If no information is available, returns "".
+ std::string getLineInfo(InputSectionBase<ELFT> *S, uintX_t Offset);
- // Get MIPS GP0 value defined by this file. This value represents the gp value
+ // MIPS GP0 value defined by this file. This value represents the gp value
// used to create the relocatable object and required to support
// R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations.
- uint32_t getMipsGp0() const;
+ uint32_t MipsGp0 = 0;
// The number is the offset in the string table. It will be used as the
// st_name of the symbol.
std::vector<std::pair<const DefinedRegular<ELFT> *, unsigned>> KeptLocalSyms;
- // SymbolBodies and Thunks for sections in this file are allocated
- // using this buffer.
- llvm::BumpPtrAllocator Alloc;
+ // Name of source file obtained from STT_FILE symbol value,
+ // or empty string if there is no such symbol in object file
+ // symbol table.
+ StringRef SourceFile;
private:
- void initializeSections(llvm::DenseSet<StringRef> &ComdatGroups);
+ void
+ initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
void initializeSymbols();
+ void initializeDwarfLine();
InputSectionBase<ELFT> *getRelocTarget(const Elf_Shdr &Sec);
- InputSectionBase<ELFT> *createInputSection(const Elf_Shdr &Sec);
+ InputSectionBase<ELFT> *createInputSection(const Elf_Shdr &Sec,
+ StringRef SectionStringTable);
bool shouldMerge(const Elf_Shdr &Sec);
SymbolBody *createSymbolBody(const Elf_Sym *Sym);
@@ -171,14 +196,11 @@ private:
// List of all symbols referenced or defined by this file.
std::vector<SymbolBody *> SymbolBodies;
- // MIPS .reginfo section defined by this file.
- std::unique_ptr<MipsReginfoInputSection<ELFT>> MipsReginfo;
- // MIPS .MIPS.options section defined by this file.
- std::unique_ptr<MipsOptionsInputSection<ELFT>> MipsOptions;
-
- llvm::SpecificBumpPtrAllocator<InputSection<ELFT>> IAlloc;
- llvm::SpecificBumpPtrAllocator<MergeInputSection<ELFT>> MAlloc;
- llvm::SpecificBumpPtrAllocator<EhInputSection<ELFT>> EHAlloc;
+ // Debugging information to retrieve source file and line for error
+ // reporting. Linker may find reasonable number of errors in a
+ // single object file, so we cache debugging information in order to
+ // parse it only once for each object file we link.
+ std::unique_ptr<llvm::DWARFDebugLine> DwarfLine;
};
// LazyObjectFile is analogous to ArchiveFile in the sense that
@@ -204,8 +226,6 @@ private:
template <class ELFT> std::vector<StringRef> getElfSymbols();
std::vector<StringRef> getBitcodeSymbols();
- llvm::BumpPtrAllocator Alloc;
- llvm::StringSaver Saver{Alloc};
bool Seen = false;
};
@@ -216,10 +236,11 @@ public:
static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
template <class ELFT> void parse();
- // Returns a memory buffer for a given symbol. An empty memory buffer
+ // Returns a memory buffer for a given symbol and the offset in the archive
+ // for the member. An empty memory buffer and an offset of zero
// is returned if we have already returned the same memory buffer.
// (So that we don't instantiate same members more than once.)
- MemoryBufferRef getMember(const Archive::Symbol *Sym);
+ std::pair<MemoryBufferRef, uint64_t> getMember(const Archive::Symbol *Sym);
private:
std::unique_ptr<Archive> File;
@@ -231,30 +252,25 @@ public:
explicit BitcodeFile(MemoryBufferRef M);
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
template <class ELFT>
- void parse(llvm::DenseSet<StringRef> &ComdatGroups);
+ void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
ArrayRef<Symbol *> getSymbols() { return Symbols; }
- static bool shouldSkip(uint32_t Flags);
- std::unique_ptr<llvm::object::IRObjectFile> Obj;
+ std::unique_ptr<llvm::lto::InputFile> Obj;
private:
std::vector<Symbol *> Symbols;
- llvm::BumpPtrAllocator Alloc;
- llvm::StringSaver Saver{Alloc};
- template <class ELFT>
- Symbol *createSymbol(const llvm::DenseSet<const llvm::Comdat *> &KeptComdats,
- const llvm::object::IRObjectFile &Obj,
- const llvm::object::BasicSymbolRef &Sym);
};
// .so file.
template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
typedef ELFFileBase<ELFT> Base;
+ typedef typename ELFT::Dyn Elf_Dyn;
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Sym Elf_Sym;
- typedef typename ELFT::Word Elf_Word;
typedef typename ELFT::SymRange Elf_Sym_Range;
- typedef typename ELFT::Versym Elf_Versym;
typedef typename ELFT::Verdef Elf_Verdef;
+ typedef typename ELFT::Versym Elf_Versym;
+ typedef typename ELFT::Word Elf_Word;
+ typedef typename ELFT::uint uintX_t;
std::vector<StringRef> Undefs;
StringRef SoName;
@@ -294,9 +310,20 @@ public:
bool isNeeded() const { return !AsNeeded || IsUsed; }
};
-std::unique_ptr<InputFile> createObjectFile(MemoryBufferRef MB,
- StringRef ArchiveName = "");
-std::unique_ptr<InputFile> createSharedFile(MemoryBufferRef MB);
+class BinaryFile : public InputFile {
+public:
+ explicit BinaryFile(MemoryBufferRef M) : InputFile(BinaryKind, M) {}
+ static bool classof(const InputFile *F) { return F->kind() == BinaryKind; }
+ template <class ELFT> void parse();
+ ArrayRef<InputSectionData *> getSections() const { return Sections; }
+
+private:
+ std::vector<InputSectionData *> Sections;
+};
+
+InputFile *createObjectFile(MemoryBufferRef MB, StringRef ArchiveName = "",
+ uint64_t OffsetInArchive = 0);
+InputFile *createSharedFile(MemoryBufferRef MB);
} // namespace elf
} // namespace lld
diff --git a/ELF/InputSection.cpp b/ELF/InputSection.cpp
index 6564e7995a89..805e51dab507 100644
--- a/ELF/InputSection.cpp
+++ b/ELF/InputSection.cpp
@@ -13,103 +13,187 @@
#include "Error.h"
#include "InputFiles.h"
#include "LinkerScript.h"
+#include "Memory.h"
#include "OutputSections.h"
+#include "Relocations.h"
+#include "SyntheticSections.h"
#include "Target.h"
#include "Thunks.h"
-
#include "llvm/Support/Compression.h"
#include "llvm/Support/Endian.h"
+#include <mutex>
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
+using namespace llvm::support;
using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
-template <class ELFT> bool elf::isDiscarded(InputSectionBase<ELFT> *S) {
- return !S || S == &InputSection<ELFT>::Discarded || !S->Live ||
- Script<ELFT>::X->isDiscarded(S);
+// Returns a string to construct an error message.
+template <class ELFT>
+std::string elf::toString(const InputSectionBase<ELFT> *Sec) {
+ return (Sec->getFile()->getName() + ":(" + Sec->Name + ")").str();
+}
+
+template <class ELFT>
+static ArrayRef<uint8_t> getSectionContents(elf::ObjectFile<ELFT> *File,
+ const typename ELFT::Shdr *Hdr) {
+ if (!File || Hdr->sh_type == SHT_NOBITS)
+ return makeArrayRef<uint8_t>(nullptr, Hdr->sh_size);
+ return check(File->getObj().getSectionContents(Hdr));
}
template <class ELFT>
InputSectionBase<ELFT>::InputSectionBase(elf::ObjectFile<ELFT> *File,
- const Elf_Shdr *Header,
+ uintX_t Flags, uint32_t Type,
+ uintX_t Entsize, uint32_t Link,
+ uint32_t Info, uintX_t Addralign,
+ ArrayRef<uint8_t> Data, StringRef Name,
Kind SectionKind)
- : Header(Header), File(File), SectionKind(SectionKind), Repl(this),
- Compressed(Header->sh_flags & SHF_COMPRESSED) {
- // The garbage collector sets sections' Live bits.
- // If GC is disabled, all sections are considered live by default.
- Live = !Config->GcSections;
+ : InputSectionData(SectionKind, Name, Data,
+ !Config->GcSections || !(Flags & SHF_ALLOC)),
+ File(File), Flags(Flags), Entsize(Entsize), Type(Type), Link(Link),
+ Info(Info), Repl(this) {
+ NumRelocations = 0;
+ AreRelocsRela = false;
// The ELF spec states that a value of 0 means the section has
// no alignment constraits.
- Alignment = std::max<uintX_t>(Header->sh_addralign, 1);
+ uint64_t V = std::max<uint64_t>(Addralign, 1);
+ if (!isPowerOf2_64(V))
+ fatal(toString(File) + ": section sh_addralign is not a power of 2");
+
+ // We reject object files having insanely large alignments even though
+ // they are allowed by the spec. I think 4GB is a reasonable limitation.
+ // We might want to relax this in the future.
+ if (V > UINT32_MAX)
+ fatal(toString(File) + ": section sh_addralign is too large");
+ Alignment = V;
+
+ // If it is not a mergeable section, overwrite the flag so that the flag
+ // is consistent with the class. This inconsistency could occur when
+ // string merging is disabled using -O0 flag.
+ if (!Config->Relocatable && !isa<MergeInputSection<ELFT>>(this))
+ this->Flags &= ~(SHF_MERGE | SHF_STRINGS);
+}
+
+template <class ELFT>
+InputSectionBase<ELFT>::InputSectionBase(elf::ObjectFile<ELFT> *File,
+ const Elf_Shdr *Hdr, StringRef Name,
+ Kind SectionKind)
+ : InputSectionBase(File, Hdr->sh_flags & ~SHF_INFO_LINK, Hdr->sh_type,
+ Hdr->sh_entsize, Hdr->sh_link, Hdr->sh_info,
+ Hdr->sh_addralign, getSectionContents(File, Hdr), Name,
+ SectionKind) {
+ this->Offset = Hdr->sh_offset;
}
template <class ELFT> size_t InputSectionBase<ELFT>::getSize() const {
+ if (auto *S = dyn_cast<SyntheticSection<ELFT>>(this))
+ return S->getSize();
+
if (auto *D = dyn_cast<InputSection<ELFT>>(this))
if (D->getThunksSize() > 0)
return D->getThunkOff() + D->getThunksSize();
- return Header->sh_size;
-}
-template <class ELFT> StringRef InputSectionBase<ELFT>::getSectionName() const {
- return check(File->getObj().getSectionName(this->Header));
+ return Data.size();
}
-template <class ELFT>
-ArrayRef<uint8_t> InputSectionBase<ELFT>::getSectionData() const {
- if (Compressed)
- return ArrayRef<uint8_t>((const uint8_t *)Uncompressed.data(),
- Uncompressed.size());
- return check(this->File->getObj().getSectionContents(this->Header));
+// Returns a string for an error message.
+template <class SectionT> static std::string getName(SectionT *Sec) {
+ return (Sec->getFile()->getName() + ":(" + Sec->Name + ")").str();
}
template <class ELFT>
typename ELFT::uint InputSectionBase<ELFT>::getOffset(uintX_t Offset) const {
- switch (SectionKind) {
+ switch (kind()) {
case Regular:
return cast<InputSection<ELFT>>(this)->OutSecOff + Offset;
+ case Synthetic:
+ // For synthetic sections we treat offset -1 as the end of the section.
+ // The same approach is used for synthetic symbols (DefinedSynthetic).
+ return cast<InputSection<ELFT>>(this)->OutSecOff +
+ (Offset == uintX_t(-1) ? getSize() : Offset);
case EHFrame:
- return cast<EhInputSection<ELFT>>(this)->getOffset(Offset);
+ // The file crtbeginT.o has relocations pointing to the start of an empty
+ // .eh_frame that is known to be the first in the link. It does that to
+ // identify the start of the output .eh_frame.
+ return Offset;
case Merge:
return cast<MergeInputSection<ELFT>>(this)->getOffset(Offset);
- case MipsReginfo:
- case MipsOptions:
- // MIPS .reginfo and .MIPS.options sections are consumed by the linker,
- // and the linker produces a single output section. It is possible that
- // input files contain section symbol points to the corresponding input
- // section. Redirect it to the produced output section.
- if (Offset != 0)
- fatal("Unsupported reference to the middle of '" + getSectionName() +
- "' section");
- return this->OutSec->getVA();
}
llvm_unreachable("invalid section kind");
}
-template <class ELFT> void InputSectionBase<ELFT>::uncompress() {
- if (!zlib::isAvailable())
- fatal("build lld with zlib to enable compressed sections support");
+template <class ELFT> bool InputSectionBase<ELFT>::isCompressed() const {
+ return (Flags & SHF_COMPRESSED) || Name.startswith(".zdebug");
+}
- // A compressed section consists of a header of Elf_Chdr type
- // followed by compressed data.
- ArrayRef<uint8_t> Data =
- check(this->File->getObj().getSectionContents(this->Header));
+// Returns compressed data and its size when uncompressed.
+template <class ELFT>
+std::pair<ArrayRef<uint8_t>, uint64_t>
+InputSectionBase<ELFT>::getElfCompressedData(ArrayRef<uint8_t> Data) {
+ // Compressed section with Elf_Chdr is the ELF standard.
if (Data.size() < sizeof(Elf_Chdr))
- fatal("corrupt compressed section");
-
+ fatal(toString(this) + ": corrupted compressed section");
auto *Hdr = reinterpret_cast<const Elf_Chdr *>(Data.data());
- Data = Data.slice(sizeof(Elf_Chdr));
-
if (Hdr->ch_type != ELFCOMPRESS_ZLIB)
- fatal("unsupported compression type");
+ fatal(toString(this) + ": unsupported compression type");
+ return {Data.slice(sizeof(*Hdr)), Hdr->ch_size};
+}
- StringRef Buf((const char *)Data.data(), Data.size());
- if (zlib::uncompress(Buf, Uncompressed, Hdr->ch_size) != zlib::StatusOK)
- fatal("error uncompressing section");
+// Returns compressed data and its size when uncompressed.
+template <class ELFT>
+std::pair<ArrayRef<uint8_t>, uint64_t>
+InputSectionBase<ELFT>::getRawCompressedData(ArrayRef<uint8_t> Data) {
+ // Compressed sections without Elf_Chdr header contain this header
+ // instead. This is a GNU extension.
+ struct ZlibHeader {
+ char Magic[4]; // Should be "ZLIB"
+ char Size[8]; // Uncompressed size in big-endian
+ };
+
+ if (Data.size() < sizeof(ZlibHeader))
+ fatal(toString(this) + ": corrupted compressed section");
+ auto *Hdr = reinterpret_cast<const ZlibHeader *>(Data.data());
+ if (memcmp(Hdr->Magic, "ZLIB", 4))
+ fatal(toString(this) + ": broken ZLIB-compressed section");
+ return {Data.slice(sizeof(*Hdr)), read64be(Hdr->Size)};
+}
+
+// Uncompress section contents. Note that this function is called
+// from parallel_for_each, so it must be thread-safe.
+template <class ELFT> void InputSectionBase<ELFT>::uncompress() {
+ if (!zlib::isAvailable())
+ fatal(toString(this) +
+ ": build lld with zlib to enable compressed sections support");
+
+ // This section is compressed. Here we decompress it. Ideally, all
+ // compressed sections have SHF_COMPRESSED bit and their contents
+ // start with headers of Elf_Chdr type. However, sections whose
+ // names start with ".zdebug_" don't have the bit and contains a raw
+ // ZLIB-compressed data (which is a bad thing because section names
+ // shouldn't be significant in ELF.) We need to be able to read both.
+ ArrayRef<uint8_t> Buf; // Compressed data
+ size_t Size; // Uncompressed size
+ if (Flags & SHF_COMPRESSED)
+ std::tie(Buf, Size) = getElfCompressedData(Data);
+ else
+ std::tie(Buf, Size) = getRawCompressedData(Data);
+
+ // Uncompress Buf.
+ char *OutputBuf;
+ {
+ static std::mutex Mu;
+ std::lock_guard<std::mutex> Lock(Mu);
+ OutputBuf = BAlloc.Allocate<char>(Size);
+ }
+ if (zlib::uncompress(toStringRef(Buf), OutputBuf, Size) != zlib::StatusOK)
+ fatal(toString(this) + ": error while uncompressing section");
+ Data = ArrayRef<uint8_t>((uint8_t *)OutputBuf, Size);
}
template <class ELFT>
@@ -119,29 +203,71 @@ InputSectionBase<ELFT>::getOffset(const DefinedRegular<ELFT> &Sym) const {
}
template <class ELFT>
+InputSectionBase<ELFT> *InputSectionBase<ELFT>::getLinkOrderDep() const {
+ if ((Flags & SHF_LINK_ORDER) && Link != 0)
+ return getFile()->getSections()[Link];
+ return nullptr;
+}
+
+// Returns a source location string. Used to construct an error message.
+template <class ELFT>
+std::string InputSectionBase<ELFT>::getLocation(typename ELFT::uint Offset) {
+ // First check if we can get desired values from debugging information.
+ std::string LineInfo = File->getLineInfo(this, Offset);
+ if (!LineInfo.empty())
+ return LineInfo;
+
+ // File->SourceFile contains STT_FILE symbol that contains a
+ // source file name. If it's missing, we use an object file name.
+ std::string SrcFile = File->SourceFile;
+ if (SrcFile.empty())
+ SrcFile = toString(File);
+
+ // Find a function symbol that encloses a given location.
+ for (SymbolBody *B : File->getSymbols())
+ if (auto *D = dyn_cast<DefinedRegular<ELFT>>(B))
+ if (D->Section == this && D->Type == STT_FUNC)
+ if (D->Value <= Offset && Offset < D->Value + D->Size)
+ return SrcFile + ":(function " + toString(*D) + ")";
+
+ // If there's no symbol, print out the offset in the section.
+ return (SrcFile + ":(" + Name + "+0x" + utohexstr(Offset) + ")").str();
+}
+
+template <class ELFT>
+InputSection<ELFT>::InputSection() : InputSectionBase<ELFT>() {}
+
+template <class ELFT>
+InputSection<ELFT>::InputSection(uintX_t Flags, uint32_t Type,
+ uintX_t Addralign, ArrayRef<uint8_t> Data,
+ StringRef Name, Kind K)
+ : InputSectionBase<ELFT>(nullptr, Flags, Type,
+ /*Entsize*/ 0, /*Link*/ 0, /*Info*/ 0, Addralign,
+ Data, Name, K) {}
+
+template <class ELFT>
InputSection<ELFT>::InputSection(elf::ObjectFile<ELFT> *F,
- const Elf_Shdr *Header)
- : InputSectionBase<ELFT>(F, Header, Base::Regular) {}
+ const Elf_Shdr *Header, StringRef Name)
+ : InputSectionBase<ELFT>(F, Header, Name, Base::Regular) {}
template <class ELFT>
-bool InputSection<ELFT>::classof(const InputSectionBase<ELFT> *S) {
- return S->SectionKind == Base::Regular;
+bool InputSection<ELFT>::classof(const InputSectionData *S) {
+ return S->kind() == Base::Regular || S->kind() == Base::Synthetic;
}
template <class ELFT>
InputSectionBase<ELFT> *InputSection<ELFT>::getRelocatedSection() {
- assert(this->Header->sh_type == SHT_RELA || this->Header->sh_type == SHT_REL);
+ assert(this->Type == SHT_RELA || this->Type == SHT_REL);
ArrayRef<InputSectionBase<ELFT> *> Sections = this->File->getSections();
- return Sections[this->Header->sh_info];
+ return Sections[this->Info];
}
-template <class ELFT>
-void InputSection<ELFT>::addThunk(const Thunk<ELFT> *T) {
+template <class ELFT> void InputSection<ELFT>::addThunk(const Thunk<ELFT> *T) {
Thunks.push_back(T);
}
template <class ELFT> uint64_t InputSection<ELFT>::getThunkOff() const {
- return this->Header->sh_size;
+ return this->Data.size();
}
template <class ELFT> uint64_t InputSection<ELFT>::getThunksSize() const {
@@ -163,35 +289,62 @@ void InputSection<ELFT>::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
uint32_t Type = Rel.getType(Config->Mips64EL);
SymbolBody &Body = this->File->getRelocTargetSym(Rel);
- RelTy *P = reinterpret_cast<RelTy *>(Buf);
+ Elf_Rela *P = reinterpret_cast<Elf_Rela *>(Buf);
Buf += sizeof(RelTy);
+ if (Config->Rela)
+ P->r_addend = getAddend<ELFT>(Rel);
P->r_offset = RelocatedSection->getOffset(Rel.r_offset);
P->setSymbolAndType(Body.DynsymIndex, Type, Config->Mips64EL);
}
}
-// Page(Expr) is the page address of the expression Expr, defined
-// as (Expr & ~0xFFF). (This applies even if the machine page size
-// supported by the platform has a different value.)
-static uint64_t getAArch64Page(uint64_t Expr) {
- return Expr & (~static_cast<uint64_t>(0xFFF));
+static uint32_t getARMUndefinedRelativeWeakVA(uint32_t Type, uint32_t A,
+ uint32_t P) {
+ switch (Type) {
+ case R_ARM_THM_JUMP11:
+ return P + 2;
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
+ case R_ARM_PC24:
+ case R_ARM_PLT32:
+ case R_ARM_PREL31:
+ case R_ARM_THM_JUMP19:
+ case R_ARM_THM_JUMP24:
+ return P + 4;
+ case R_ARM_THM_CALL:
+ // We don't want an interworking BLX to ARM
+ return P + 5;
+ default:
+ return A;
+ }
}
-template <class ELFT>
-static typename ELFT::uint getSymVA(uint32_t Type, typename ELFT::uint A,
- typename ELFT::uint P,
- const SymbolBody &Body, RelExpr Expr) {
- typedef typename ELFT::uint uintX_t;
+static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t Type, uint64_t A,
+ uint64_t P) {
+ switch (Type) {
+ case R_AARCH64_CALL26:
+ case R_AARCH64_CONDBR19:
+ case R_AARCH64_JUMP26:
+ case R_AARCH64_TSTBR14:
+ return P + 4;
+ default:
+ return A;
+ }
+}
+template <class ELFT>
+static typename ELFT::uint
+getRelocTargetVA(uint32_t Type, typename ELFT::uint A, typename ELFT::uint P,
+ const SymbolBody &Body, RelExpr Expr) {
switch (Expr) {
case R_HINT:
+ case R_TLSDESC_CALL:
llvm_unreachable("cannot relocate hint relocs");
case R_TLSLD:
- return Out<ELFT>::Got->getTlsIndexOff() + A -
- Out<ELFT>::Got->getNumEntries() * sizeof(uintX_t);
+ return In<ELFT>::Got->getTlsIndexOff() + A - In<ELFT>::Got->getSize();
case R_TLSLD_PC:
- return Out<ELFT>::Got->getTlsIndexVA() + A - P;
+ return In<ELFT>::Got->getTlsIndexVA() + A - P;
case R_THUNK_ABS:
return Body.getThunkVA<ELFT>() + A;
case R_THUNK_PC:
@@ -200,14 +353,14 @@ static typename ELFT::uint getSymVA(uint32_t Type, typename ELFT::uint A,
case R_PPC_TOC:
return getPPC64TocBase() + A;
case R_TLSGD:
- return Out<ELFT>::Got->getGlobalDynOffset(Body) + A -
- Out<ELFT>::Got->getNumEntries() * sizeof(uintX_t);
+ return In<ELFT>::Got->getGlobalDynOffset(Body) + A -
+ In<ELFT>::Got->getSize();
case R_TLSGD_PC:
- return Out<ELFT>::Got->getGlobalDynAddr(Body) + A - P;
+ return In<ELFT>::Got->getGlobalDynAddr(Body) + A - P;
case R_TLSDESC:
- return Out<ELFT>::Got->getGlobalDynAddr(Body) + A;
+ return In<ELFT>::Got->getGlobalDynAddr(Body) + A;
case R_TLSDESC_PAGE:
- return getAArch64Page(Out<ELFT>::Got->getGlobalDynAddr(Body) + A) -
+ return getAArch64Page(In<ELFT>::Got->getGlobalDynAddr(Body) + A) -
getAArch64Page(P);
case R_PLT:
return Body.getPltVA<ELFT>() + A;
@@ -217,11 +370,13 @@ static typename ELFT::uint getSymVA(uint32_t Type, typename ELFT::uint A,
case R_SIZE:
return Body.getSize<ELFT>() + A;
case R_GOTREL:
- return Body.getVA<ELFT>(A) - Out<ELFT>::Got->getVA();
+ return Body.getVA<ELFT>(A) - In<ELFT>::Got->getVA();
+ case R_GOTREL_FROM_END:
+ return Body.getVA<ELFT>(A) - In<ELFT>::Got->getVA() -
+ In<ELFT>::Got->getSize();
case R_RELAX_TLS_GD_TO_IE_END:
case R_GOT_FROM_END:
- return Body.getGotOffset<ELFT>() + A -
- Out<ELFT>::Got->getNumEntries() * sizeof(uintX_t);
+ return Body.getGotOffset<ELFT>() + A - In<ELFT>::Got->getSize();
case R_RELAX_TLS_GD_TO_IE_ABS:
case R_GOT:
return Body.getGotVA<ELFT>() + A;
@@ -232,11 +387,21 @@ static typename ELFT::uint getSymVA(uint32_t Type, typename ELFT::uint A,
case R_GOT_PC:
return Body.getGotVA<ELFT>() + A - P;
case R_GOTONLY_PC:
- return Out<ELFT>::Got->getVA() + A - P;
+ return In<ELFT>::Got->getVA() + A - P;
+ case R_GOTONLY_PC_FROM_END:
+ return In<ELFT>::Got->getVA() + A - P + In<ELFT>::Got->getSize();
case R_RELAX_TLS_LD_TO_LE:
case R_RELAX_TLS_IE_TO_LE:
case R_RELAX_TLS_GD_TO_LE:
case R_TLS:
+ // A weak undefined TLS symbol resolves to the base of the TLS
+ // block, i.e. gets a value of zero. If we pass --gc-sections to
+ // lld and .tbss is not referenced, it gets reclaimed and we don't
+ // create a TLS program header. Therefore, we resolve this
+ // statically to zero.
+ if (Body.isTls() && (Body.isLazy() || Body.isUndefined()) &&
+ Body.symbol()->isWeak())
+ return 0;
if (Target->TcbSize)
return Body.getVA<ELFT>(A) +
alignTo(Target->TcbSize, Out<ELFT>::TlsPhdr->p_align);
@@ -253,18 +418,26 @@ static typename ELFT::uint getSymVA(uint32_t Type, typename ELFT::uint A,
// If relocation against MIPS local symbol requires GOT entry, this entry
// should be initialized by 'page address'. This address is high 16-bits
// of sum the symbol's value and the addend.
- return Out<ELFT>::Got->getMipsLocalPageOffset(Body.getVA<ELFT>(A));
+ return In<ELFT>::MipsGot->getVA() +
+ In<ELFT>::MipsGot->getPageEntryOffset(Body, A) -
+ In<ELFT>::MipsGot->getGp();
case R_MIPS_GOT_OFF:
+ case R_MIPS_GOT_OFF32:
// In case of MIPS if a GOT relocation has non-zero addend this addend
// should be applied to the GOT entry content not to the GOT entry offset.
// That is why we use separate expression type.
- return Out<ELFT>::Got->getMipsGotOffset(Body, A);
+ return In<ELFT>::MipsGot->getVA() +
+ In<ELFT>::MipsGot->getBodyEntryOffset(Body, A) -
+ In<ELFT>::MipsGot->getGp();
+ case R_MIPS_GOTREL:
+ return Body.getVA<ELFT>(A) - In<ELFT>::MipsGot->getGp();
case R_MIPS_TLSGD:
- return Out<ELFT>::Got->getGlobalDynOffset(Body) +
- Out<ELFT>::Got->getMipsTlsOffset() - MipsGPOffset;
+ return In<ELFT>::MipsGot->getVA() + In<ELFT>::MipsGot->getTlsOffset() +
+ In<ELFT>::MipsGot->getGlobalDynOffset(Body) -
+ In<ELFT>::MipsGot->getGp();
case R_MIPS_TLSLD:
- return Out<ELFT>::Got->getTlsIndexOff() +
- Out<ELFT>::Got->getMipsTlsOffset() - MipsGPOffset;
+ return In<ELFT>::MipsGot->getVA() + In<ELFT>::MipsGot->getTlsOffset() +
+ In<ELFT>::MipsGot->getTlsIndexOff() - In<ELFT>::MipsGot->getGp();
case R_PPC_OPD: {
uint64_t SymVA = Body.getVA<ELFT>(A);
// If we have an undefined weak symbol, we might get here with a symbol
@@ -275,8 +448,8 @@ static typename ELFT::uint getSymVA(uint32_t Type, typename ELFT::uint A,
if (Out<ELF64BE>::Opd) {
// If this is a local call, and we currently have the address of a
// function-descriptor, get the underlying code address instead.
- uint64_t OpdStart = Out<ELF64BE>::Opd->getVA();
- uint64_t OpdEnd = OpdStart + Out<ELF64BE>::Opd->getSize();
+ uint64_t OpdStart = Out<ELF64BE>::Opd->Addr;
+ uint64_t OpdEnd = OpdStart + Out<ELF64BE>::Opd->Size;
bool InOpd = OpdStart <= SymVA && SymVA < OpdEnd;
if (InOpd)
SymVA = read64be(&Out<ELF64BE>::OpdBuf[SymVA - OpdStart]);
@@ -284,10 +457,20 @@ static typename ELFT::uint getSymVA(uint32_t Type, typename ELFT::uint A,
return SymVA - P;
}
case R_PC:
+ if (Body.isUndefined() && !Body.isLocal() && Body.symbol()->isWeak()) {
+ // On ARM and AArch64 a branch to an undefined weak resolves to the
+ // next instruction, otherwise the place.
+ if (Config->EMachine == EM_ARM)
+ return getARMUndefinedRelativeWeakVA(Type, A, P);
+ if (Config->EMachine == EM_AARCH64)
+ return getAArch64UndefinedRelativeWeakVA(Type, A, P);
+ }
case R_RELAX_GOT_PC:
return Body.getVA<ELFT>(A) - P;
case R_PLT_PAGE_PC:
case R_PAGE_PC:
+ if (Body.isUndefined() && !Body.isLocal() && Body.symbol()->isWeak())
+ return getAArch64Page(A);
return getAArch64Page(Body.getVA<ELFT>(A)) - getAArch64Page(P);
}
llvm_unreachable("Invalid expression");
@@ -303,7 +486,6 @@ static typename ELFT::uint getSymVA(uint32_t Type, typename ELFT::uint A,
template <class ELFT>
template <class RelTy>
void InputSection<ELFT>::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
- const unsigned Bits = sizeof(uintX_t) * 8;
for (const RelTy &Rel : Rels) {
uint32_t Type = Rel.getType(Config->Mips64EL);
uintX_t Offset = this->getOffset(Rel.r_offset);
@@ -314,13 +496,15 @@ void InputSection<ELFT>::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
SymbolBody &Sym = this->File->getRelocTargetSym(Rel);
if (Target->getRelExpr(Type, Sym) != R_ABS) {
- error(this->getSectionName() + " has non-ABS reloc");
+ error(this->getLocation(Offset) + ": has non-ABS reloc");
return;
}
- uintX_t AddrLoc = this->OutSec->getVA() + Offset;
- uint64_t SymVA =
- SignExtend64<Bits>(getSymVA<ELFT>(Type, Addend, AddrLoc, Sym, R_ABS));
+ uintX_t AddrLoc = this->OutSec->Addr + Offset;
+ uint64_t SymVA = 0;
+ if (!Sym.isTls() || Out<ELFT>::TlsPhdr)
+ SymVA = SignExtend64<sizeof(uintX_t) * 8>(
+ getRelocTargetVA<ELFT>(Type, Addend, AddrLoc, Sym, R_ABS));
Target->relocateOne(BufLoc, Type, SymVA);
}
}
@@ -331,78 +515,80 @@ void InputSectionBase<ELFT>::relocate(uint8_t *Buf, uint8_t *BufEnd) {
// vector only for SHF_ALLOC'ed sections. For other sections,
// we handle relocations directly here.
auto *IS = dyn_cast<InputSection<ELFT>>(this);
- if (IS && !(IS->Header->sh_flags & SHF_ALLOC)) {
- for (const Elf_Shdr *RelSec : IS->RelocSections) {
- if (RelSec->sh_type == SHT_RELA)
- IS->relocateNonAlloc(Buf, IS->File->getObj().relas(RelSec));
- else
- IS->relocateNonAlloc(Buf, IS->File->getObj().rels(RelSec));
- }
+ if (IS && !(IS->Flags & SHF_ALLOC)) {
+ if (IS->AreRelocsRela)
+ IS->relocateNonAlloc(Buf, IS->relas());
+ else
+ IS->relocateNonAlloc(Buf, IS->rels());
return;
}
const unsigned Bits = sizeof(uintX_t) * 8;
- for (const Relocation<ELFT> &Rel : Relocations) {
- uintX_t Offset = Rel.InputSec->getOffset(Rel.Offset);
+ for (const Relocation &Rel : Relocations) {
+ uintX_t Offset = getOffset(Rel.Offset);
uint8_t *BufLoc = Buf + Offset;
uint32_t Type = Rel.Type;
uintX_t A = Rel.Addend;
- uintX_t AddrLoc = OutSec->getVA() + Offset;
+ uintX_t AddrLoc = OutSec->Addr + Offset;
RelExpr Expr = Rel.Expr;
- uint64_t SymVA =
- SignExtend64<Bits>(getSymVA<ELFT>(Type, A, AddrLoc, *Rel.Sym, Expr));
+ uint64_t TargetVA = SignExtend64<Bits>(
+ getRelocTargetVA<ELFT>(Type, A, AddrLoc, *Rel.Sym, Expr));
switch (Expr) {
case R_RELAX_GOT_PC:
case R_RELAX_GOT_PC_NOPIC:
- Target->relaxGot(BufLoc, SymVA);
+ Target->relaxGot(BufLoc, TargetVA);
break;
case R_RELAX_TLS_IE_TO_LE:
- Target->relaxTlsIeToLe(BufLoc, Type, SymVA);
+ Target->relaxTlsIeToLe(BufLoc, Type, TargetVA);
break;
case R_RELAX_TLS_LD_TO_LE:
- Target->relaxTlsLdToLe(BufLoc, Type, SymVA);
+ Target->relaxTlsLdToLe(BufLoc, Type, TargetVA);
break;
case R_RELAX_TLS_GD_TO_LE:
case R_RELAX_TLS_GD_TO_LE_NEG:
- Target->relaxTlsGdToLe(BufLoc, Type, SymVA);
+ Target->relaxTlsGdToLe(BufLoc, Type, TargetVA);
break;
case R_RELAX_TLS_GD_TO_IE:
case R_RELAX_TLS_GD_TO_IE_ABS:
case R_RELAX_TLS_GD_TO_IE_PAGE_PC:
case R_RELAX_TLS_GD_TO_IE_END:
- Target->relaxTlsGdToIe(BufLoc, Type, SymVA);
+ Target->relaxTlsGdToIe(BufLoc, Type, TargetVA);
break;
case R_PPC_PLT_OPD:
// Patch a nop (0x60000000) to a ld.
if (BufLoc + 8 <= BufEnd && read32be(BufLoc + 4) == 0x60000000)
write32be(BufLoc + 4, 0xe8410028); // ld %r2, 40(%r1)
- // fallthrough
+ // fallthrough
default:
- Target->relocateOne(BufLoc, Type, SymVA);
+ Target->relocateOne(BufLoc, Type, TargetVA);
break;
}
}
}
template <class ELFT> void InputSection<ELFT>::writeTo(uint8_t *Buf) {
- if (this->Header->sh_type == SHT_NOBITS)
+ if (this->Type == SHT_NOBITS)
+ return;
+
+ if (auto *S = dyn_cast<SyntheticSection<ELFT>>(this)) {
+ S->writeTo(Buf + OutSecOff);
return;
- ELFFile<ELFT> &EObj = this->File->getObj();
+ }
// If -r is given, then an InputSection may be a relocation section.
- if (this->Header->sh_type == SHT_RELA) {
- copyRelocations(Buf + OutSecOff, EObj.relas(this->Header));
+ if (this->Type == SHT_RELA) {
+ copyRelocations(Buf + OutSecOff, this->template getDataAs<Elf_Rela>());
return;
}
- if (this->Header->sh_type == SHT_REL) {
- copyRelocations(Buf + OutSecOff, EObj.rels(this->Header));
+ if (this->Type == SHT_REL) {
+ copyRelocations(Buf + OutSecOff, this->template getDataAs<Elf_Rel>());
return;
}
// Copy section contents from source object file to output file.
- ArrayRef<uint8_t> Data = this->getSectionData();
+ ArrayRef<uint8_t> Data = this->Data;
memcpy(Buf + OutSecOff, Data.data(), Data.size());
// Iterate over all relocation sections that apply to this section.
@@ -431,15 +617,9 @@ void InputSection<ELFT>::replace(InputSection<ELFT> *Other) {
}
template <class ELFT>
-SplitInputSection<ELFT>::SplitInputSection(
- elf::ObjectFile<ELFT> *File, const Elf_Shdr *Header,
- typename InputSectionBase<ELFT>::Kind SectionKind)
- : InputSectionBase<ELFT>(File, Header, SectionKind) {}
-
-template <class ELFT>
EhInputSection<ELFT>::EhInputSection(elf::ObjectFile<ELFT> *F,
- const Elf_Shdr *Header)
- : SplitInputSection<ELFT>(F, Header, InputSectionBase<ELFT>::EHFrame) {
+ const Elf_Shdr *Header, StringRef Name)
+ : InputSectionBase<ELFT>(F, Header, Name, InputSectionBase<ELFT>::EHFrame) {
// Mark .eh_frame sections as live by default because there are
// usually no relocations that point to .eh_frames. Otherwise,
// the garbage collector would drop all .eh_frame sections.
@@ -447,18 +627,54 @@ EhInputSection<ELFT>::EhInputSection(elf::ObjectFile<ELFT> *F,
}
template <class ELFT>
-bool EhInputSection<ELFT>::classof(const InputSectionBase<ELFT> *S) {
- return S->SectionKind == InputSectionBase<ELFT>::EHFrame;
+bool EhInputSection<ELFT>::classof(const InputSectionData *S) {
+ return S->kind() == InputSectionBase<ELFT>::EHFrame;
+}
+
+// Returns the index of the first relocation that points to a region between
+// Begin and Begin+Size.
+template <class IntTy, class RelTy>
+static unsigned getReloc(IntTy Begin, IntTy Size, const ArrayRef<RelTy> &Rels,
+ unsigned &RelocI) {
+ // Start search from RelocI for fast access. That works because the
+ // relocations are sorted in .eh_frame.
+ for (unsigned N = Rels.size(); RelocI < N; ++RelocI) {
+ const RelTy &Rel = Rels[RelocI];
+ if (Rel.r_offset < Begin)
+ continue;
+
+ if (Rel.r_offset < Begin + Size)
+ return RelocI;
+ return -1;
+ }
+ return -1;
}
// .eh_frame is a sequence of CIE or FDE records.
// This function splits an input section into records and returns them.
+template <class ELFT> void EhInputSection<ELFT>::split() {
+ // Early exit if already split.
+ if (!this->Pieces.empty())
+ return;
+
+ if (this->NumRelocations) {
+ if (this->AreRelocsRela)
+ split(this->relas());
+ else
+ split(this->rels());
+ return;
+ }
+ split(makeArrayRef<typename ELFT::Rela>(nullptr, nullptr));
+}
+
template <class ELFT>
-void EhInputSection<ELFT>::split() {
- ArrayRef<uint8_t> Data = this->getSectionData();
+template <class RelTy>
+void EhInputSection<ELFT>::split(ArrayRef<RelTy> Rels) {
+ ArrayRef<uint8_t> Data = this->Data;
+ unsigned RelI = 0;
for (size_t Off = 0, End = Data.size(); Off != End;) {
- size_t Size = readEhRecordSize<ELFT>(Data.slice(Off));
- this->Pieces.emplace_back(Off, Data.slice(Off, Size));
+ size_t Size = readEhRecordSize<ELFT>(this, Off);
+ this->Pieces.emplace_back(Off, this, Size, getReloc(Off, Size, Rels, RelI));
// The empty record is the end marker.
if (Size == 4)
break;
@@ -466,21 +682,6 @@ void EhInputSection<ELFT>::split() {
}
}
-template <class ELFT>
-typename ELFT::uint EhInputSection<ELFT>::getOffset(uintX_t Offset) const {
- // The file crtbeginT.o has relocations pointing to the start of an empty
- // .eh_frame that is known to be the first in the link. It does that to
- // identify the start of the output .eh_frame. Handle this special case.
- if (this->getSectionHdr()->sh_size == 0)
- return Offset;
- const SectionPiece *Piece = this->getSectionPiece(Offset);
- if (Piece->OutputOff == size_t(-1))
- return -1; // Not in the output
-
- uintX_t Addend = Offset - Piece->InputOff;
- return Piece->OutputOff + Addend;
-}
-
static size_t findNull(ArrayRef<uint8_t> A, size_t EntSize) {
// Optimize the common case.
StringRef S((const char *)A.data(), A.size());
@@ -497,75 +698,96 @@ static size_t findNull(ArrayRef<uint8_t> A, size_t EntSize) {
// Split SHF_STRINGS section. Such section is a sequence of
// null-terminated strings.
-static std::vector<SectionPiece> splitStrings(ArrayRef<uint8_t> Data,
- size_t EntSize) {
- std::vector<SectionPiece> V;
+template <class ELFT>
+void MergeInputSection<ELFT>::splitStrings(ArrayRef<uint8_t> Data,
+ size_t EntSize) {
size_t Off = 0;
+ bool IsAlloc = this->Flags & SHF_ALLOC;
while (!Data.empty()) {
size_t End = findNull(Data, EntSize);
if (End == StringRef::npos)
- fatal("string is not null terminated");
+ fatal(toString(this) + ": string is not null terminated");
size_t Size = End + EntSize;
- V.emplace_back(Off, Data.slice(0, Size));
+ Pieces.emplace_back(Off, !IsAlloc);
+ Hashes.push_back(hash_value(toStringRef(Data.slice(0, Size))));
Data = Data.slice(Size);
Off += Size;
}
- return V;
}
// Split non-SHF_STRINGS section. Such section is a sequence of
// fixed size records.
-static std::vector<SectionPiece> splitNonStrings(ArrayRef<uint8_t> Data,
- size_t EntSize) {
- std::vector<SectionPiece> V;
+template <class ELFT>
+void MergeInputSection<ELFT>::splitNonStrings(ArrayRef<uint8_t> Data,
+ size_t EntSize) {
size_t Size = Data.size();
assert((Size % EntSize) == 0);
- for (unsigned I = 0, N = Size; I != N; I += EntSize)
- V.emplace_back(I, Data.slice(I, EntSize));
- return V;
+ bool IsAlloc = this->Flags & SHF_ALLOC;
+ for (unsigned I = 0, N = Size; I != N; I += EntSize) {
+ Hashes.push_back(hash_value(toStringRef(Data.slice(I, EntSize))));
+ Pieces.emplace_back(I, !IsAlloc);
+ }
}
template <class ELFT>
MergeInputSection<ELFT>::MergeInputSection(elf::ObjectFile<ELFT> *F,
- const Elf_Shdr *Header)
- : SplitInputSection<ELFT>(F, Header, InputSectionBase<ELFT>::Merge) {}
+ const Elf_Shdr *Header,
+ StringRef Name)
+ : InputSectionBase<ELFT>(F, Header, Name, InputSectionBase<ELFT>::Merge) {}
+// This function is called after we obtain a complete list of input sections
+// that need to be linked. This is responsible to split section contents
+// into small chunks for further processing.
+//
+// Note that this function is called from parallel_for_each. This must be
+// thread-safe (i.e. no memory allocation from the pools).
template <class ELFT> void MergeInputSection<ELFT>::splitIntoPieces() {
- ArrayRef<uint8_t> Data = this->getSectionData();
- uintX_t EntSize = this->Header->sh_entsize;
- if (this->Header->sh_flags & SHF_STRINGS)
- this->Pieces = splitStrings(Data, EntSize);
+ ArrayRef<uint8_t> Data = this->Data;
+ uintX_t EntSize = this->Entsize;
+ if (this->Flags & SHF_STRINGS)
+ splitStrings(Data, EntSize);
else
- this->Pieces = splitNonStrings(Data, EntSize);
+ splitNonStrings(Data, EntSize);
- if (Config->GcSections)
+ if (Config->GcSections && (this->Flags & SHF_ALLOC))
for (uintX_t Off : LiveOffsets)
this->getSectionPiece(Off)->Live = true;
}
template <class ELFT>
-bool MergeInputSection<ELFT>::classof(const InputSectionBase<ELFT> *S) {
- return S->SectionKind == InputSectionBase<ELFT>::Merge;
+bool MergeInputSection<ELFT>::classof(const InputSectionData *S) {
+ return S->kind() == InputSectionBase<ELFT>::Merge;
}
// Do binary search to get a section piece at a given input offset.
template <class ELFT>
-SectionPiece *SplitInputSection<ELFT>::getSectionPiece(uintX_t Offset) {
- auto *This = static_cast<const SplitInputSection<ELFT> *>(this);
+SectionPiece *MergeInputSection<ELFT>::getSectionPiece(uintX_t Offset) {
+ auto *This = static_cast<const MergeInputSection<ELFT> *>(this);
return const_cast<SectionPiece *>(This->getSectionPiece(Offset));
}
+template <class It, class T, class Compare>
+static It fastUpperBound(It First, It Last, const T &Value, Compare Comp) {
+ size_t Size = std::distance(First, Last);
+ assert(Size != 0);
+ while (Size != 1) {
+ size_t H = Size / 2;
+ const It MI = First + H;
+ Size -= H;
+ First = Comp(Value, *MI) ? First : First + H;
+ }
+ return Comp(Value, *First) ? First : First + 1;
+}
+
template <class ELFT>
const SectionPiece *
-SplitInputSection<ELFT>::getSectionPiece(uintX_t Offset) const {
- ArrayRef<uint8_t> D = this->getSectionData();
- StringRef Data((const char *)D.data(), D.size());
- uintX_t Size = Data.size();
+MergeInputSection<ELFT>::getSectionPiece(uintX_t Offset) const {
+ uintX_t Size = this->Data.size();
if (Offset >= Size)
- fatal("entry is past the end of the section");
+ fatal(toString(this) + ": entry is past the end of the section");
// Find the element this offset points to.
- auto I = std::upper_bound(
+ auto I = fastUpperBound(
Pieces.begin(), Pieces.end(), Offset,
[](const uintX_t &A, const SectionPiece &B) { return A < B.InputOff; });
--I;
@@ -577,84 +799,31 @@ SplitInputSection<ELFT>::getSectionPiece(uintX_t Offset) const {
// it is not just an addition to a base output offset.
template <class ELFT>
typename ELFT::uint MergeInputSection<ELFT>::getOffset(uintX_t Offset) const {
+ // Initialize OffsetMap lazily.
+ std::call_once(InitOffsetMap, [&] {
+ OffsetMap.reserve(Pieces.size());
+ for (const SectionPiece &Piece : Pieces)
+ OffsetMap[Piece.InputOff] = Piece.OutputOff;
+ });
+
+ // Find a string starting at a given offset.
auto It = OffsetMap.find(Offset);
if (It != OffsetMap.end())
return It->second;
+ if (!this->Live)
+ return 0;
+
// If Offset is not at beginning of a section piece, it is not in the map.
// In that case we need to search from the original section piece vector.
const SectionPiece &Piece = *this->getSectionPiece(Offset);
- assert(Piece.Live);
+ if (!Piece.Live)
+ return 0;
+
uintX_t Addend = Offset - Piece.InputOff;
return Piece.OutputOff + Addend;
}
-// Create a map from input offsets to output offsets for all section pieces.
-// It is called after finalize().
-template <class ELFT> void MergeInputSection<ELFT>::finalizePieces() {
- OffsetMap.grow(this->Pieces.size());
- for (SectionPiece &Piece : this->Pieces) {
- if (!Piece.Live)
- continue;
- if (Piece.OutputOff == size_t(-1)) {
- // Offsets of tail-merged strings are computed lazily.
- auto *OutSec = static_cast<MergeOutputSection<ELFT> *>(this->OutSec);
- ArrayRef<uint8_t> D = Piece.data();
- StringRef S((const char *)D.data(), D.size());
- Piece.OutputOff = OutSec->getOffset(S);
- }
- OffsetMap[Piece.InputOff] = Piece.OutputOff;
- }
-}
-
-template <class ELFT>
-MipsReginfoInputSection<ELFT>::MipsReginfoInputSection(elf::ObjectFile<ELFT> *F,
- const Elf_Shdr *Hdr)
- : InputSectionBase<ELFT>(F, Hdr, InputSectionBase<ELFT>::MipsReginfo) {
- // Initialize this->Reginfo.
- ArrayRef<uint8_t> D = this->getSectionData();
- if (D.size() != sizeof(Elf_Mips_RegInfo<ELFT>)) {
- error("invalid size of .reginfo section");
- return;
- }
- Reginfo = reinterpret_cast<const Elf_Mips_RegInfo<ELFT> *>(D.data());
-}
-
-template <class ELFT>
-bool MipsReginfoInputSection<ELFT>::classof(const InputSectionBase<ELFT> *S) {
- return S->SectionKind == InputSectionBase<ELFT>::MipsReginfo;
-}
-
-template <class ELFT>
-MipsOptionsInputSection<ELFT>::MipsOptionsInputSection(elf::ObjectFile<ELFT> *F,
- const Elf_Shdr *Hdr)
- : InputSectionBase<ELFT>(F, Hdr, InputSectionBase<ELFT>::MipsOptions) {
- // Find ODK_REGINFO option in the section's content.
- ArrayRef<uint8_t> D = this->getSectionData();
- while (!D.empty()) {
- if (D.size() < sizeof(Elf_Mips_Options<ELFT>)) {
- error("invalid size of .MIPS.options section");
- break;
- }
- auto *O = reinterpret_cast<const Elf_Mips_Options<ELFT> *>(D.data());
- if (O->kind == ODK_REGINFO) {
- Reginfo = &O->getRegInfo();
- break;
- }
- D = D.slice(O->size);
- }
-}
-
-template <class ELFT>
-bool MipsOptionsInputSection<ELFT>::classof(const InputSectionBase<ELFT> *S) {
- return S->SectionKind == InputSectionBase<ELFT>::MipsOptions;
-}
-
-template bool elf::isDiscarded<ELF32LE>(InputSectionBase<ELF32LE> *);
-template bool elf::isDiscarded<ELF32BE>(InputSectionBase<ELF32BE> *);
-template bool elf::isDiscarded<ELF64LE>(InputSectionBase<ELF64LE> *);
-template bool elf::isDiscarded<ELF64BE>(InputSectionBase<ELF64BE> *);
-
template class elf::InputSectionBase<ELF32LE>;
template class elf::InputSectionBase<ELF32BE>;
template class elf::InputSectionBase<ELF64LE>;
@@ -665,11 +834,6 @@ template class elf::InputSection<ELF32BE>;
template class elf::InputSection<ELF64LE>;
template class elf::InputSection<ELF64BE>;
-template class elf::SplitInputSection<ELF32LE>;
-template class elf::SplitInputSection<ELF32BE>;
-template class elf::SplitInputSection<ELF64LE>;
-template class elf::SplitInputSection<ELF64BE>;
-
template class elf::EhInputSection<ELF32LE>;
template class elf::EhInputSection<ELF32BE>;
template class elf::EhInputSection<ELF64LE>;
@@ -680,12 +844,7 @@ template class elf::MergeInputSection<ELF32BE>;
template class elf::MergeInputSection<ELF64LE>;
template class elf::MergeInputSection<ELF64BE>;
-template class elf::MipsReginfoInputSection<ELF32LE>;
-template class elf::MipsReginfoInputSection<ELF32BE>;
-template class elf::MipsReginfoInputSection<ELF64LE>;
-template class elf::MipsReginfoInputSection<ELF64BE>;
-
-template class elf::MipsOptionsInputSection<ELF32LE>;
-template class elf::MipsOptionsInputSection<ELF32BE>;
-template class elf::MipsOptionsInputSection<ELF64LE>;
-template class elf::MipsOptionsInputSection<ELF64BE>;
+template std::string elf::toString(const InputSectionBase<ELF32LE> *);
+template std::string elf::toString(const InputSectionBase<ELF32BE> *);
+template std::string elf::toString(const InputSectionBase<ELF64LE> *);
+template std::string elf::toString(const InputSectionBase<ELF64BE> *);
diff --git a/ELF/InputSection.h b/ELF/InputSection.h
index 61a89c540c5d..adbc1e1e3829 100644
--- a/ELF/InputSection.h
+++ b/ELF/InputSection.h
@@ -14,25 +14,64 @@
#include "Relocations.h"
#include "Thunks.h"
#include "lld/Core/LLVM.h"
+#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Object/ELF.h"
+#include <mutex>
namespace lld {
namespace elf {
-template <class ELFT> bool isDiscarded(InputSectionBase<ELFT> *S);
-
+class DefinedCommon;
class SymbolBody;
+struct SectionPiece;
-template <class ELFT> class ICF;
template <class ELFT> class DefinedRegular;
template <class ELFT> class ObjectFile;
template <class ELFT> class OutputSection;
-template <class ELFT> class OutputSectionBase;
+class OutputSectionBase;
+
+// We need non-template input section class to store symbol layout
+// in linker script parser structures, where we do not have ELFT
+// template parameter. For each scripted output section symbol we
+// store pointer to preceding InputSectionData object or nullptr,
+// if symbol should be placed at the very beginning of the output
+// section
+class InputSectionData {
+public:
+ enum Kind { Regular, EHFrame, Merge, Synthetic, };
+
+ // The garbage collector sets sections' Live bits.
+ // If GC is disabled, all sections are considered live by default.
+ InputSectionData(Kind SectionKind, StringRef Name, ArrayRef<uint8_t> Data,
+ bool Live)
+ : SectionKind(SectionKind), Live(Live), Assigned(false), Name(Name),
+ Data(Data) {}
+
+private:
+ unsigned SectionKind : 3;
+
+public:
+ Kind kind() const { return (Kind)SectionKind; }
+
+ unsigned Live : 1; // for garbage collection
+ unsigned Assigned : 1; // for linker script
+ uint32_t Alignment;
+ StringRef Name;
+ ArrayRef<uint8_t> Data;
+
+ template <typename T> llvm::ArrayRef<T> getDataAs() const {
+ size_t S = Data.size();
+ assert(S % sizeof(T) == 0);
+ return llvm::makeArrayRef<T>((const T *)Data.data(), S / sizeof(T));
+ }
+
+ std::vector<Relocation> Relocations;
+};
// This corresponds to a section of an input file.
-template <class ELFT> class InputSectionBase {
+template <class ELFT> class InputSectionBase : public InputSectionData {
protected:
typedef typename ELFT::Chdr Elf_Chdr;
typedef typename ELFT::Rel Elf_Rel;
@@ -40,27 +79,46 @@ protected:
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::uint uintX_t;
- const Elf_Shdr *Header;
// The file this section is from.
ObjectFile<ELFT> *File;
- // If a section is compressed, this vector has uncompressed section data.
- SmallVector<char, 0> Uncompressed;
-
public:
- enum Kind { Regular, EHFrame, Merge, MipsReginfo, MipsOptions };
- Kind SectionKind;
-
- InputSectionBase() : Repl(this) {}
+ // These corresponds to the fields in Elf_Shdr.
+ uintX_t Flags;
+ uintX_t Offset = 0;
+ uintX_t Entsize;
+ uint32_t Type;
+ uint32_t Link;
+ uint32_t Info;
+
+ InputSectionBase()
+ : InputSectionData(Regular, "", ArrayRef<uint8_t>(), false), Repl(this) {
+ NumRelocations = 0;
+ AreRelocsRela = false;
+ }
InputSectionBase(ObjectFile<ELFT> *File, const Elf_Shdr *Header,
+ StringRef Name, Kind SectionKind);
+ InputSectionBase(ObjectFile<ELFT> *File, uintX_t Flags, uint32_t Type,
+ uintX_t Entsize, uint32_t Link, uint32_t Info,
+ uintX_t Addralign, ArrayRef<uint8_t> Data, StringRef Name,
Kind SectionKind);
- OutputSectionBase<ELFT> *OutSec = nullptr;
- uint32_t Alignment;
-
- // Used for garbage collection.
- bool Live;
+ OutputSectionBase *OutSec = nullptr;
+
+ // Relocations that refer to this section.
+ const Elf_Rel *FirstRelocation = nullptr;
+ unsigned NumRelocations : 31;
+ unsigned AreRelocsRela : 1;
+ ArrayRef<Elf_Rel> rels() const {
+ assert(!AreRelocsRela);
+ return llvm::makeArrayRef(FirstRelocation, NumRelocations);
+ }
+ ArrayRef<Elf_Rela> relas() const {
+ assert(AreRelocsRela);
+ return llvm::makeArrayRef(static_cast<const Elf_Rela *>(FirstRelocation),
+ NumRelocations);
+ }
// This pointer points to the "real" instance of this instance.
// Usually Repl == this. However, if ICF merges two sections,
@@ -72,140 +130,163 @@ public:
// Returns the size of this section (even if this is a common or BSS.)
size_t getSize() const;
- static InputSectionBase<ELFT> Discarded;
-
- StringRef getSectionName() const;
- const Elf_Shdr *getSectionHdr() const { return Header; }
ObjectFile<ELFT> *getFile() const { return File; }
+ llvm::object::ELFFile<ELFT> getObj() const { return File->getObj(); }
uintX_t getOffset(const DefinedRegular<ELFT> &Sym) const;
-
+ InputSectionBase *getLinkOrderDep() const;
// Translate an offset in the input section to an offset in the output
// section.
uintX_t getOffset(uintX_t Offset) const;
- ArrayRef<uint8_t> getSectionData() const;
-
+ // ELF supports ZLIB-compressed section.
+ // Returns true if the section is compressed.
+ bool isCompressed() const;
void uncompress();
+ // Returns a source location string. Used to construct an error message.
+ std::string getLocation(uintX_t Offset);
+
void relocate(uint8_t *Buf, uint8_t *BufEnd);
- std::vector<Relocation<ELFT>> Relocations;
- bool Compressed;
-};
+private:
+ std::pair<ArrayRef<uint8_t>, uint64_t>
+ getElfCompressedData(ArrayRef<uint8_t> Data);
-template <class ELFT> InputSectionBase<ELFT> InputSectionBase<ELFT>::Discarded;
+ std::pair<ArrayRef<uint8_t>, uint64_t>
+ getRawCompressedData(ArrayRef<uint8_t> Data);
+};
// SectionPiece represents a piece of splittable section contents.
+// We allocate a lot of these and binary search on them. This means that they
+// have to be as compact as possible, which is why we don't store the size (can
+// be found by looking at the next one) and put the hash in a side table.
struct SectionPiece {
- SectionPiece(size_t Off, ArrayRef<uint8_t> Data)
- : InputOff(Off), Data((const uint8_t *)Data.data()), Size(Data.size()),
- Live(!Config->GcSections) {}
-
- ArrayRef<uint8_t> data() { return {Data, Size}; }
- size_t size() const { return Size; }
+ SectionPiece(size_t Off, bool Live = false)
+ : InputOff(Off), OutputOff(-1), Live(Live || !Config->GcSections) {}
size_t InputOff;
- size_t OutputOff = -1;
-
-private:
- // We use bitfields because SplitInputSection is accessed by
- // std::upper_bound very often.
- // We want to save bits to make it cache friendly.
- const uint8_t *Data;
- uint32_t Size : 31;
-
-public:
- uint32_t Live : 1;
-};
-
-// Usually sections are copied to the output as atomic chunks of data,
-// but some special types of sections are split into small pieces of data
-// and each piece is copied to a different place in the output.
-// This class represents such special sections.
-template <class ELFT> class SplitInputSection : public InputSectionBase<ELFT> {
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::uint uintX_t;
-
-public:
- SplitInputSection(ObjectFile<ELFT> *File, const Elf_Shdr *Header,
- typename InputSectionBase<ELFT>::Kind SectionKind);
-
- // Splittable sections are handled as a sequence of data
- // rather than a single large blob of data.
- std::vector<SectionPiece> Pieces;
-
- // Returns the SectionPiece at a given input section offset.
- SectionPiece *getSectionPiece(uintX_t Offset);
- const SectionPiece *getSectionPiece(uintX_t Offset) const;
+ ssize_t OutputOff : 8 * sizeof(ssize_t) - 1;
+ size_t Live : 1;
};
+static_assert(sizeof(SectionPiece) == 2 * sizeof(size_t),
+ "SectionPiece is too big");
// This corresponds to a SHF_MERGE section of an input file.
-template <class ELFT> class MergeInputSection : public SplitInputSection<ELFT> {
+template <class ELFT> class MergeInputSection : public InputSectionBase<ELFT> {
typedef typename ELFT::uint uintX_t;
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::Shdr Elf_Shdr;
public:
- MergeInputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Header);
- static bool classof(const InputSectionBase<ELFT> *S);
+ MergeInputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Header,
+ StringRef Name);
+ static bool classof(const InputSectionData *S);
void splitIntoPieces();
// Mark the piece at a given offset live. Used by GC.
- void markLiveAt(uintX_t Offset) { LiveOffsets.insert(Offset); }
+ void markLiveAt(uintX_t Offset) {
+ assert(this->Flags & llvm::ELF::SHF_ALLOC);
+ LiveOffsets.insert(Offset);
+ }
// Translate an offset in the input section to an offset
// in the output section.
uintX_t getOffset(uintX_t Offset) const;
- void finalizePieces();
+ // Splittable sections are handled as a sequence of data
+ // rather than a single large blob of data.
+ std::vector<SectionPiece> Pieces;
+
+ // Returns I'th piece's data. This function is very hot when
+ // string merging is enabled, so we want to inline.
+ LLVM_ATTRIBUTE_ALWAYS_INLINE
+ llvm::CachedHashStringRef getData(size_t I) const {
+ size_t Begin = Pieces[I].InputOff;
+ size_t End;
+ if (Pieces.size() - 1 == I)
+ End = this->Data.size();
+ else
+ End = Pieces[I + 1].InputOff;
+
+ StringRef S = {(const char *)(this->Data.data() + Begin), End - Begin};
+ return {S, Hashes[I]};
+ }
+
+ // Returns the SectionPiece at a given input section offset.
+ SectionPiece *getSectionPiece(uintX_t Offset);
+ const SectionPiece *getSectionPiece(uintX_t Offset) const;
private:
- llvm::DenseMap<uintX_t, uintX_t> OffsetMap;
+ void splitStrings(ArrayRef<uint8_t> A, size_t Size);
+ void splitNonStrings(ArrayRef<uint8_t> A, size_t Size);
+
+ std::vector<uint32_t> Hashes;
+
+ mutable llvm::DenseMap<uintX_t, uintX_t> OffsetMap;
+ mutable std::once_flag InitOffsetMap;
+
llvm::DenseSet<uintX_t> LiveOffsets;
};
+struct EhSectionPiece : public SectionPiece {
+ EhSectionPiece(size_t Off, InputSectionData *ID, uint32_t Size,
+ unsigned FirstRelocation)
+ : SectionPiece(Off, false), ID(ID), Size(Size),
+ FirstRelocation(FirstRelocation) {}
+ InputSectionData *ID;
+ uint32_t Size;
+ uint32_t size() const { return Size; }
+
+ ArrayRef<uint8_t> data() { return {ID->Data.data() + this->InputOff, Size}; }
+ unsigned FirstRelocation;
+};
+
// This corresponds to a .eh_frame section of an input file.
-template <class ELFT> class EhInputSection : public SplitInputSection<ELFT> {
+template <class ELFT> class EhInputSection : public InputSectionBase<ELFT> {
public:
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::uint uintX_t;
- EhInputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Header);
- static bool classof(const InputSectionBase<ELFT> *S);
+ EhInputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Header, StringRef Name);
+ static bool classof(const InputSectionData *S);
void split();
+ template <class RelTy> void split(ArrayRef<RelTy> Rels);
- // Translate an offset in the input section to an offset in the output
- // section.
- uintX_t getOffset(uintX_t Offset) const;
-
- // Relocation section that refer to this one.
- const Elf_Shdr *RelocSection = nullptr;
+ // Splittable sections are handled as a sequence of data
+ // rather than a single large blob of data.
+ std::vector<EhSectionPiece> Pieces;
};
// This corresponds to a non SHF_MERGE section of an input file.
template <class ELFT> class InputSection : public InputSectionBase<ELFT> {
- friend ICF<ELFT>;
typedef InputSectionBase<ELFT> Base;
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Rela Elf_Rela;
typedef typename ELFT::Rel Elf_Rel;
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::uint uintX_t;
+ typedef InputSectionData::Kind Kind;
public:
- InputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Header);
+ InputSection();
+ InputSection(uintX_t Flags, uint32_t Type, uintX_t Addralign,
+ ArrayRef<uint8_t> Data, StringRef Name,
+ Kind K = InputSectionData::Regular);
+ InputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Header, StringRef Name);
+
+ static InputSection<ELFT> Discarded;
// Write this section to a mmap'ed file, assuming Buf is pointing to
// beginning of the output section.
void writeTo(uint8_t *Buf);
- // Relocation sections that refer to this one.
- llvm::TinyPtrVector<const Elf_Shdr *> RelocSections;
-
// The offset from beginning of the output sections this section was assigned
// to. The writer sets a value.
uint64_t OutSecOff = 0;
- static bool classof(const InputSectionBase<ELFT> *S);
+ // InputSection that is dependent on us (reverse dependency for GC)
+ InputSectionBase<ELFT> *DependentSection = nullptr;
+
+ static bool classof(const InputSectionData *S);
InputSectionBase<ELFT> *getRelocatedSection();
@@ -223,46 +304,22 @@ public:
template <class RelTy>
void relocateNonAlloc(uint8_t *Buf, llvm::ArrayRef<RelTy> Rels);
-private:
- template <class RelTy>
- void copyRelocations(uint8_t *Buf, llvm::ArrayRef<RelTy> Rels);
+ // Used by ICF.
+ uint32_t Class[2] = {0, 0};
// Called by ICF to merge two input sections.
void replace(InputSection<ELFT> *Other);
- // Used by ICF.
- uint64_t GroupId = 0;
+private:
+ template <class RelTy>
+ void copyRelocations(uint8_t *Buf, llvm::ArrayRef<RelTy> Rels);
llvm::TinyPtrVector<const Thunk<ELFT> *> Thunks;
};
-// MIPS .reginfo section provides information on the registers used by the code
-// in the object file. Linker should collect this information and write a single
-// .reginfo section in the output file. The output section contains a union of
-// used registers masks taken from input .reginfo sections and final value
-// of the `_gp` symbol. For details: Chapter 4 / "Register Information" at
-// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
-template <class ELFT>
-class MipsReginfoInputSection : public InputSectionBase<ELFT> {
- typedef typename ELFT::Shdr Elf_Shdr;
-
-public:
- MipsReginfoInputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Hdr);
- static bool classof(const InputSectionBase<ELFT> *S);
-
- const llvm::object::Elf_Mips_RegInfo<ELFT> *Reginfo = nullptr;
-};
+template <class ELFT> InputSection<ELFT> InputSection<ELFT>::Discarded;
-template <class ELFT>
-class MipsOptionsInputSection : public InputSectionBase<ELFT> {
- typedef typename ELFT::Shdr Elf_Shdr;
-
-public:
- MipsOptionsInputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Hdr);
- static bool classof(const InputSectionBase<ELFT> *S);
-
- const llvm::object::Elf_Mips_RegInfo<ELFT> *Reginfo = nullptr;
-};
+template <class ELFT> std::string toString(const InputSectionBase<ELFT> *);
} // namespace elf
} // namespace lld
diff --git a/ELF/LTO.cpp b/ELF/LTO.cpp
index 0e8006a3b32a..a3d6a141a202 100644
--- a/ELF/LTO.cpp
+++ b/ELF/LTO.cpp
@@ -9,31 +9,30 @@
#include "LTO.h"
#include "Config.h"
-#include "Driver.h"
#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
-#include "llvm/Analysis/AliasAnalysis.h"
-#include "llvm/Analysis/CGSCCPassManager.h"
-#include "llvm/Analysis/LoopPassManager.h"
-#include "llvm/Analysis/TargetLibraryInfo.h"
-#include "llvm/Analysis/TargetTransformInfo.h"
-#include "llvm/Bitcode/ReaderWriter.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
#include "llvm/CodeGen/CommandFlags.h"
-#include "llvm/CodeGen/ParallelCG.h"
-#include "llvm/IR/AutoUpgrade.h"
-#include "llvm/IR/LegacyPassManager.h"
-#include "llvm/IR/PassManager.h"
-#include "llvm/IR/Verifier.h"
-#include "llvm/LTO/legacy/UpdateCompilerUsed.h"
-#include "llvm/Linker/IRMover.h"
-#include "llvm/Passes/PassBuilder.h"
-#include "llvm/Support/StringSaver.h"
-#include "llvm/Support/TargetRegistry.h"
-#include "llvm/Target/TargetMachine.h"
-#include "llvm/Transforms/IPO.h"
-#include "llvm/Transforms/IPO/PassManagerBuilder.h"
-#include "llvm/Transforms/Utils/ModuleUtils.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/LTO/Config.h"
+#include "llvm/LTO/LTO.h"
+#include "llvm/Object/SymbolicFile.h"
+#include "llvm/Support/CodeGen.h"
+#include "llvm/Support/ELF.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <vector>
using namespace llvm;
using namespace llvm::object;
@@ -51,275 +50,110 @@ static void saveBuffer(StringRef Buffer, const Twine &Path) {
OS << Buffer;
}
-// This is for use when debugging LTO.
-static void saveBCFile(Module &M, const Twine &Path) {
- std::error_code EC;
- raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
- if (EC)
- error(EC, "cannot create " + Path);
- WriteBitcodeToFile(&M, OS, /* ShouldPreserveUseListOrder */ true);
+static void diagnosticHandler(const DiagnosticInfo &DI) {
+ SmallString<128> ErrStorage;
+ raw_svector_ostream OS(ErrStorage);
+ DiagnosticPrinterRawOStream DP(OS);
+ DI.print(DP);
+ warn(ErrStorage);
}
-static void runNewCustomLtoPasses(Module &M, TargetMachine &TM) {
- PassBuilder PB(&TM);
-
- AAManager AA;
-
- // Parse a custom AA pipeline if asked to.
- if (!PB.parseAAPipeline(AA, Config->LtoAAPipeline)) {
- error("Unable to parse AA pipeline description: " + Config->LtoAAPipeline);
- return;
- }
-
- LoopAnalysisManager LAM;
- FunctionAnalysisManager FAM;
- CGSCCAnalysisManager CGAM;
- ModuleAnalysisManager MAM;
-
- // Register the AA manager first so that our version is the one used.
- FAM.registerPass([&] { return std::move(AA); });
-
- // Register all the basic analyses with the managers.
- PB.registerModuleAnalyses(MAM);
- PB.registerCGSCCAnalyses(CGAM);
- PB.registerFunctionAnalyses(FAM);
- PB.registerLoopAnalyses(LAM);
- PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
-
- ModulePassManager MPM;
- if (!Config->DisableVerify)
- MPM.addPass(VerifierPass());
+static void checkError(Error E) {
+ handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) -> Error {
+ error(EIB.message());
+ return Error::success();
+ });
+}
- // Now, add all the passes we've been requested to.
- if (!PB.parsePassPipeline(MPM, Config->LtoNewPmPasses)) {
- error("unable to parse pass pipeline description: " +
- Config->LtoNewPmPasses);
- return;
- }
+static std::unique_ptr<lto::LTO> createLTO() {
+ lto::Config Conf;
- if (!Config->DisableVerify)
- MPM.addPass(VerifierPass());
- MPM.run(M, MAM);
-}
+ // LLD supports the new relocations.
+ Conf.Options = InitTargetOptionsFromCodeGenFlags();
+ Conf.Options.RelaxELFRelocations = true;
-static void runOldLtoPasses(Module &M, TargetMachine &TM) {
- // Note that the gold plugin has a similar piece of code, so
- // it is probably better to move this code to a common place.
- legacy::PassManager LtoPasses;
- LtoPasses.add(createTargetTransformInfoWrapperPass(TM.getTargetIRAnalysis()));
- PassManagerBuilder PMB;
- PMB.LibraryInfo = new TargetLibraryInfoImpl(Triple(TM.getTargetTriple()));
- PMB.Inliner = createFunctionInliningPass();
- PMB.VerifyInput = PMB.VerifyOutput = !Config->DisableVerify;
- PMB.LoopVectorize = true;
- PMB.SLPVectorize = true;
- PMB.OptLevel = Config->LtoO;
- PMB.populateLTOPassManager(LtoPasses);
- LtoPasses.run(M);
-}
+ Conf.RelocModel = Config->Pic ? Reloc::PIC_ : Reloc::Static;
+ Conf.DisableVerify = Config->DisableVerify;
+ Conf.DiagHandler = diagnosticHandler;
+ Conf.OptLevel = Config->LTOO;
-static void runLTOPasses(Module &M, TargetMachine &TM) {
- if (!Config->LtoNewPmPasses.empty()) {
- // The user explicitly asked for a set of passes to be run.
- // This needs the new PM to work as there's no clean way to
- // pass a set of passes to run in the legacy PM.
- runNewCustomLtoPasses(M, TM);
- if (HasError)
- return;
- } else {
- // Run the 'default' set of LTO passes. This code still uses
- // the legacy PM as the new one is not the default.
- runOldLtoPasses(M, TM);
- }
+ // Set up a custom pipeline if we've been asked to.
+ Conf.OptPipeline = Config->LTONewPmPasses;
+ Conf.AAPipeline = Config->LTOAAPipeline;
if (Config->SaveTemps)
- saveBCFile(M, Config->OutputFile + ".lto.opt.bc");
+ checkError(Conf.addSaveTemps(std::string(Config->OutputFile) + ".",
+ /*UseInputModulePath*/ true));
+
+ lto::ThinBackend Backend;
+ if (Config->ThinLTOJobs != -1u)
+ Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs);
+ return llvm::make_unique<lto::LTO>(std::move(Conf), Backend,
+ Config->LTOPartitions);
}
-static bool shouldInternalize(const SmallPtrSet<GlobalValue *, 8> &Used,
- Symbol *S, GlobalValue *GV) {
- if (S->IsUsedInRegularObj || Used.count(GV))
- return false;
- return !S->includeInDynsym();
-}
+BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {}
-BitcodeCompiler::BitcodeCompiler()
- : Combined(new Module("ld-temp.o", Driver->Context)) {}
+BitcodeCompiler::~BitcodeCompiler() = default;
static void undefine(Symbol *S) {
- replaceBody<Undefined>(S, S->body()->getName(), STV_DEFAULT, S->body()->Type,
- nullptr);
-}
-
-static void handleUndefinedAsmRefs(const BasicSymbolRef &Sym, GlobalValue *GV,
- StringSet<> &AsmUndefinedRefs) {
- // GV associated => not an assembly symbol, bail out.
- if (GV)
- return;
-
- // This is an undefined reference to a symbol in asm. We put that in
- // compiler.used, so that we can preserve it from being dropped from
- // the output, without necessarily preventing its internalization.
- SmallString<64> Name;
- raw_svector_ostream OS(Name);
- Sym.printName(OS);
- AsmUndefinedRefs.insert(Name.str());
+ replaceBody<Undefined>(S, S->body()->getName(), /*IsLocal=*/false,
+ STV_DEFAULT, S->body()->Type, nullptr);
}
void BitcodeCompiler::add(BitcodeFile &F) {
- std::unique_ptr<IRObjectFile> Obj = std::move(F.Obj);
- std::vector<GlobalValue *> Keep;
- unsigned BodyIndex = 0;
- ArrayRef<Symbol *> Syms = F.getSymbols();
-
- Module &M = Obj->getModule();
- if (M.getDataLayoutStr().empty())
- fatal("invalid bitcode file: " + F.getName() + " has no datalayout");
-
- // Discard non-compatible debug infos if necessary.
- M.materializeMetadata();
- UpgradeDebugInfo(M);
-
- // If a symbol appears in @llvm.used, the linker is required
- // to treat the symbol as there is a reference to the symbol
- // that it cannot see. Therefore, we can't internalize.
- SmallPtrSet<GlobalValue *, 8> Used;
- collectUsedGlobalVariables(M, Used, /* CompilerUsed */ false);
-
- for (const BasicSymbolRef &Sym : Obj->symbols()) {
- uint32_t Flags = Sym.getFlags();
- GlobalValue *GV = Obj->getSymbolGV(Sym.getRawDataRefImpl());
- if (GV && GV->hasAppendingLinkage())
- Keep.push_back(GV);
- if (BitcodeFile::shouldSkip(Flags))
- continue;
- Symbol *S = Syms[BodyIndex++];
- if (Flags & BasicSymbolRef::SF_Undefined) {
- handleUndefinedAsmRefs(Sym, GV, AsmUndefinedRefs);
- continue;
- }
- auto *B = dyn_cast<DefinedBitcode>(S->body());
- if (!B || B->file() != &F)
- continue;
-
- // We collect the set of symbols we want to internalize here
- // and change the linkage after the IRMover executed, i.e. after
- // we imported the symbols and satisfied undefined references
- // to it. We can't just change linkage here because otherwise
- // the IRMover will just rename the symbol.
- if (GV && shouldInternalize(Used, S, GV))
- InternalizedSyms.insert(GV->getName());
-
- // At this point we know that either the combined LTO object will provide a
- // definition of a symbol, or we will internalize it. In either case, we
- // need to undefine the symbol. In the former case, the real definition
- // needs to be able to replace the original definition without conflicting.
- // In the latter case, we need to allow the combined LTO object to provide a
- // definition with the same name, for example when doing parallel codegen.
- undefine(S);
-
- if (!GV)
- // Module asm symbol.
- continue;
-
- switch (GV->getLinkage()) {
- default:
- break;
- case GlobalValue::LinkOnceAnyLinkage:
- GV->setLinkage(GlobalValue::WeakAnyLinkage);
- break;
- case GlobalValue::LinkOnceODRLinkage:
- GV->setLinkage(GlobalValue::WeakODRLinkage);
- break;
- }
-
- Keep.push_back(GV);
- }
-
- IRMover Mover(*Combined);
- if (Error E = Mover.move(Obj->takeModule(), Keep,
- [](GlobalValue &, IRMover::ValueAdder) {})) {
- handleAllErrors(std::move(E), [&](const ErrorInfoBase &EIB) {
- fatal("failed to link module " + F.getName() + ": " + EIB.message());
- });
+ lto::InputFile &Obj = *F.Obj;
+ unsigned SymNum = 0;
+ std::vector<Symbol *> Syms = F.getSymbols();
+ std::vector<lto::SymbolResolution> Resols(Syms.size());
+
+ // Provide a resolution to the LTO API for each symbol.
+ for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
+ Symbol *Sym = Syms[SymNum];
+ lto::SymbolResolution &R = Resols[SymNum];
+ ++SymNum;
+ SymbolBody *B = Sym->body();
+
+ // Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
+ // reports two symbols for module ASM defined. Without this check, lld
+ // flags an undefined in IR with a definition in ASM as prevailing.
+ // Once IRObjectFile is fixed to report only one symbol this hack can
+ // be removed.
+ R.Prevailing =
+ !(ObjSym.getFlags() & object::BasicSymbolRef::SF_Undefined) &&
+ B->File == &F;
+
+ R.VisibleToRegularObj =
+ Sym->IsUsedInRegularObj || (R.Prevailing && Sym->includeInDynsym());
+ if (R.Prevailing)
+ undefine(Sym);
}
-}
-
-static void internalize(GlobalValue &GV) {
- assert(!GV.hasLocalLinkage() &&
- "Trying to internalize a symbol with local linkage!");
- GV.setLinkage(GlobalValue::InternalLinkage);
-}
-
-std::vector<std::unique_ptr<InputFile>> BitcodeCompiler::runSplitCodegen(
- const std::function<std::unique_ptr<TargetMachine>()> &TMFactory) {
- unsigned NumThreads = Config->LtoJobs;
- OwningData.resize(NumThreads);
-
- std::list<raw_svector_ostream> OSs;
- std::vector<raw_pwrite_stream *> OSPtrs;
- for (SmallString<0> &Obj : OwningData) {
- OSs.emplace_back(Obj);
- OSPtrs.push_back(&OSs.back());
- }
-
- splitCodeGen(std::move(Combined), OSPtrs, {}, TMFactory);
-
- std::vector<std::unique_ptr<InputFile>> ObjFiles;
- for (SmallString<0> &Obj : OwningData)
- ObjFiles.push_back(createObjectFile(
- MemoryBufferRef(Obj, "LLD-INTERNAL-combined-lto-object")));
-
- // If -save-temps is given, we need to save temporary objects to files.
- // This is for debugging.
- if (Config->SaveTemps) {
- if (NumThreads == 1) {
- saveBuffer(OwningData[0], Config->OutputFile + ".lto.o");
- } else {
- for (unsigned I = 0; I < NumThreads; ++I)
- saveBuffer(OwningData[I], Config->OutputFile + Twine(I) + ".lto.o");
- }
- }
-
- return ObjFiles;
+ checkError(LTOObj->add(std::move(F.Obj), Resols));
}
// Merge all the bitcode files we have seen, codegen the result
-// and return the resulting ObjectFile.
-std::vector<std::unique_ptr<InputFile>> BitcodeCompiler::compile() {
- for (const auto &Name : InternalizedSyms) {
- GlobalValue *GV = Combined->getNamedValue(Name.first());
- assert(GV);
- internalize(*GV);
+// and return the resulting ObjectFile(s).
+std::vector<InputFile *> BitcodeCompiler::compile() {
+ std::vector<InputFile *> Ret;
+ unsigned MaxTasks = LTOObj->getMaxTasks();
+ Buff.resize(MaxTasks);
+
+ checkError(LTOObj->run([&](size_t Task) {
+ return llvm::make_unique<lto::NativeObjectStream>(
+ llvm::make_unique<raw_svector_ostream>(Buff[Task]));
+ }));
+
+ for (unsigned I = 0; I != MaxTasks; ++I) {
+ if (Buff[I].empty())
+ continue;
+ if (Config->SaveTemps) {
+ if (MaxTasks == 1)
+ saveBuffer(Buff[I], Config->OutputFile + ".lto.o");
+ else
+ saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.o");
+ }
+ InputFile *Obj = createObjectFile(MemoryBufferRef(Buff[I], "lto.tmp"));
+ Ret.push_back(Obj);
}
-
- std::string TheTriple = Combined->getTargetTriple();
- std::string Msg;
- const Target *T = TargetRegistry::lookupTarget(TheTriple, Msg);
- if (!T)
- fatal("target not found: " + Msg);
-
- // LLD supports the new relocations.
- TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
- Options.RelaxELFRelocations = true;
-
- auto CreateTargetMachine = [&]() {
- return std::unique_ptr<TargetMachine>(T->createTargetMachine(
- TheTriple, "", "", Options, Config->Pic ? Reloc::PIC_ : Reloc::Static));
- };
-
- std::unique_ptr<TargetMachine> TM = CreateTargetMachine();
-
- // Update llvm.compiler.used so that optimizations won't strip
- // off AsmUndefinedReferences.
- updateCompilerUsed(*Combined, *TM, AsmUndefinedRefs);
-
- if (Config->SaveTemps)
- saveBCFile(*Combined, Config->OutputFile + ".lto.bc");
-
- runLTOPasses(*Combined, *TM);
- if (HasError)
- return {};
-
- return runSplitCodegen(CreateTargetMachine);
+ return Ret;
}
diff --git a/ELF/LTO.h b/ELF/LTO.h
index 81dffb6004b2..b3d734f2d381 100644
--- a/ELF/LTO.h
+++ b/ELF/LTO.h
@@ -23,9 +23,14 @@
#include "lld/Core/LLVM.h"
#include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/StringSet.h"
-#include "llvm/IR/Module.h"
-#include "llvm/Linker/IRMover.h"
+#include <memory>
+#include <vector>
+
+namespace llvm {
+namespace lto {
+class LTO;
+}
+}
namespace lld {
namespace elf {
@@ -36,17 +41,14 @@ class InputFile;
class BitcodeCompiler {
public:
BitcodeCompiler();
+ ~BitcodeCompiler();
+
void add(BitcodeFile &F);
- std::vector<std::unique_ptr<InputFile>> compile();
+ std::vector<InputFile *> compile();
private:
- std::vector<std::unique_ptr<InputFile>> runSplitCodegen(
- const std::function<std::unique_ptr<llvm::TargetMachine>()> &TMFactory);
-
- std::unique_ptr<llvm::Module> Combined;
- std::vector<SmallString<0>> OwningData;
- llvm::StringSet<> InternalizedSyms;
- llvm::StringSet<> AsmUndefinedRefs;
+ std::unique_ptr<llvm::lto::LTO> LTOObj;
+ std::vector<SmallString<0>> Buff;
};
}
}
diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp
index 61abdc185e11..5057b57a4a54 100644
--- a/ELF/LinkerScript.cpp
+++ b/ELF/LinkerScript.cpp
@@ -8,8 +8,6 @@
//===----------------------------------------------------------------------===//
//
// This file contains the parser/evaluator of the linker script.
-// It does not construct an AST but consume linker script directives directly.
-// Results are written to Driver or Config object.
//
//===----------------------------------------------------------------------===//
@@ -17,311 +15,971 @@
#include "Config.h"
#include "Driver.h"
#include "InputSection.h"
+#include "Memory.h"
#include "OutputSections.h"
#include "ScriptParser.h"
#include "Strings.h"
-#include "Symbols.h"
#include "SymbolTable.h"
+#include "Symbols.h"
+#include "SyntheticSections.h"
#include "Target.h"
+#include "Writer.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/ELF.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/MathExtras.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/StringSaver.h"
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <vector>
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
+using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
+LinkerScriptBase *elf::ScriptBase;
ScriptConfiguration *elf::ScriptConfig;
-// This is an operator-precedence parser to parse and evaluate
-// a linker script expression. For each linker script arithmetic
-// expression (e.g. ". = . + 0x1000"), a new instance of ExprParser
-// is created and ran.
-namespace {
-class ExprParser : public ScriptParserBase {
-public:
- ExprParser(std::vector<StringRef> &Tokens, uint64_t Dot)
- : ScriptParserBase(Tokens), Dot(Dot) {}
+template <class ELFT> static void addRegular(SymbolAssignment *Cmd) {
+ uint8_t Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT;
+ Symbol *Sym = Symtab<ELFT>::X->addRegular(Cmd->Name, Visibility, STT_NOTYPE,
+ 0, 0, STB_GLOBAL, nullptr, nullptr);
+ Cmd->Sym = Sym->body();
- uint64_t run();
+ // If we have no SECTIONS then we don't have '.' and don't call
+ // assignAddresses(). We calculate symbol value immediately in this case.
+ if (!ScriptConfig->HasSections)
+ cast<DefinedRegular<ELFT>>(Cmd->Sym)->Value = Cmd->Expression(0);
+}
-private:
- uint64_t parsePrimary();
- uint64_t parseTernary(uint64_t Cond);
- uint64_t apply(StringRef Op, uint64_t L, uint64_t R);
- uint64_t parseExpr1(uint64_t Lhs, int MinPrec);
- uint64_t parseExpr();
+template <class ELFT> static void addSynthetic(SymbolAssignment *Cmd) {
+ // If we have SECTIONS block then output sections haven't been created yet.
+ const OutputSectionBase *Sec =
+ ScriptConfig->HasSections ? nullptr : Cmd->Expression.Section();
+ Symbol *Sym = Symtab<ELFT>::X->addSynthetic(
+ Cmd->Name, Sec, 0, Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT);
+ Cmd->Sym = Sym->body();
- uint64_t Dot;
-};
+ // If we already know section then we can calculate symbol value immediately.
+ if (Sec)
+ cast<DefinedSynthetic>(Cmd->Sym)->Value = Cmd->Expression(0) - Sec->Addr;
}
-static int precedence(StringRef Op) {
- return StringSwitch<int>(Op)
- .Case("*", 4)
- .Case("/", 4)
- .Case("+", 3)
- .Case("-", 3)
- .Case("<", 2)
- .Case(">", 2)
- .Case(">=", 2)
- .Case("<=", 2)
- .Case("==", 2)
- .Case("!=", 2)
- .Case("&", 1)
- .Default(-1);
+static bool isUnderSysroot(StringRef Path) {
+ if (Config->Sysroot == "")
+ return false;
+ for (; !Path.empty(); Path = sys::path::parent_path(Path))
+ if (sys::fs::equivalent(Config->Sysroot, Path))
+ return true;
+ return false;
}
-static uint64_t evalExpr(std::vector<StringRef> &Tokens, uint64_t Dot) {
- return ExprParser(Tokens, Dot).run();
+template <class ELFT> static void addSymbol(SymbolAssignment *Cmd) {
+ if (Cmd->Expression.IsAbsolute())
+ addRegular<ELFT>(Cmd);
+ else
+ addSynthetic<ELFT>(Cmd);
+}
+// If a symbol was in PROVIDE(), we need to define it only when
+// it is an undefined symbol.
+template <class ELFT> static bool shouldDefine(SymbolAssignment *Cmd) {
+ if (Cmd->Name == ".")
+ return false;
+ if (!Cmd->Provide)
+ return true;
+ SymbolBody *B = Symtab<ELFT>::X->find(Cmd->Name);
+ return B && B->isUndefined();
}
-uint64_t ExprParser::run() {
- uint64_t V = parseExpr();
- if (!atEOF() && !Error)
- setError("stray token: " + peek());
- return V;
+bool SymbolAssignment::classof(const BaseCommand *C) {
+ return C->Kind == AssignmentKind;
}
-// This is a part of the operator-precedence parser to evaluate
-// arithmetic expressions in SECTIONS command. This function evaluates an
-// integer literal, a parenthesized expression, the ALIGN function,
-// or the special variable ".".
-uint64_t ExprParser::parsePrimary() {
- StringRef Tok = next();
- if (Tok == ".")
- return Dot;
- if (Tok == "(") {
- uint64_t V = parseExpr();
- expect(")");
- return V;
- }
- if (Tok == "ALIGN") {
- expect("(");
- uint64_t V = parseExpr();
- expect(")");
- return alignTo(Dot, V);
- }
- uint64_t V = 0;
- if (Tok.getAsInteger(0, V))
- setError("malformed number: " + Tok);
- return V;
+bool OutputSectionCommand::classof(const BaseCommand *C) {
+ return C->Kind == OutputSectionKind;
}
-uint64_t ExprParser::parseTernary(uint64_t Cond) {
- next();
- uint64_t V = parseExpr();
- expect(":");
- uint64_t W = parseExpr();
- return Cond ? V : W;
+bool InputSectionDescription::classof(const BaseCommand *C) {
+ return C->Kind == InputSectionKind;
}
-uint64_t ExprParser::apply(StringRef Op, uint64_t L, uint64_t R) {
- if (Op == "*")
- return L * R;
- if (Op == "/") {
- if (R == 0) {
- error("division by zero");
- return 0;
- }
- return L / R;
+bool AssertCommand::classof(const BaseCommand *C) {
+ return C->Kind == AssertKind;
+}
+
+bool BytesDataCommand::classof(const BaseCommand *C) {
+ return C->Kind == BytesDataKind;
+}
+
+template <class ELFT> LinkerScript<ELFT>::LinkerScript() = default;
+template <class ELFT> LinkerScript<ELFT>::~LinkerScript() = default;
+
+template <class ELFT> static StringRef basename(InputSectionBase<ELFT> *S) {
+ if (S->getFile())
+ return sys::path::filename(S->getFile()->getName());
+ return "";
+}
+
+template <class ELFT>
+bool LinkerScript<ELFT>::shouldKeep(InputSectionBase<ELFT> *S) {
+ for (InputSectionDescription *ID : Opt.KeptSections)
+ if (ID->FilePat.match(basename(S)))
+ for (SectionPattern &P : ID->SectionPatterns)
+ if (P.SectionPat.match(S->Name))
+ return true;
+ return false;
+}
+
+static bool comparePriority(InputSectionData *A, InputSectionData *B) {
+ return getPriority(A->Name) < getPriority(B->Name);
+}
+
+static bool compareName(InputSectionData *A, InputSectionData *B) {
+ return A->Name < B->Name;
+}
+
+static bool compareAlignment(InputSectionData *A, InputSectionData *B) {
+ // ">" is not a mistake. Larger alignments are placed before smaller
+ // alignments in order to reduce the amount of padding necessary.
+ // This is compatible with GNU.
+ return A->Alignment > B->Alignment;
+}
+
+static std::function<bool(InputSectionData *, InputSectionData *)>
+getComparator(SortSectionPolicy K) {
+ switch (K) {
+ case SortSectionPolicy::Alignment:
+ return compareAlignment;
+ case SortSectionPolicy::Name:
+ return compareName;
+ case SortSectionPolicy::Priority:
+ return comparePriority;
+ default:
+ llvm_unreachable("unknown sort policy");
}
- if (Op == "+")
- return L + R;
- if (Op == "-")
- return L - R;
- if (Op == "<")
- return L < R;
- if (Op == ">")
- return L > R;
- if (Op == ">=")
- return L >= R;
- if (Op == "<=")
- return L <= R;
- if (Op == "==")
- return L == R;
- if (Op == "!=")
- return L != R;
- if (Op == "&")
- return L & R;
- llvm_unreachable("invalid operator");
}
-// This is a part of the operator-precedence parser.
-// This function assumes that the remaining token stream starts
-// with an operator.
-uint64_t ExprParser::parseExpr1(uint64_t Lhs, int MinPrec) {
- while (!atEOF()) {
- // Read an operator and an expression.
- StringRef Op1 = peek();
- if (Op1 == "?")
- return parseTernary(Lhs);
- if (precedence(Op1) < MinPrec)
- return Lhs;
- next();
- uint64_t Rhs = parsePrimary();
+template <class ELFT>
+static bool matchConstraints(ArrayRef<InputSectionBase<ELFT> *> Sections,
+ ConstraintKind Kind) {
+ if (Kind == ConstraintKind::NoConstraint)
+ return true;
+ bool IsRW = llvm::any_of(Sections, [=](InputSectionData *Sec2) {
+ auto *Sec = static_cast<InputSectionBase<ELFT> *>(Sec2);
+ return Sec->Flags & SHF_WRITE;
+ });
+ return (IsRW && Kind == ConstraintKind::ReadWrite) ||
+ (!IsRW && Kind == ConstraintKind::ReadOnly);
+}
- // Evaluate the remaining part of the expression first if the
- // next operator has greater precedence than the previous one.
- // For example, if we have read "+" and "3", and if the next
- // operator is "*", then we'll evaluate 3 * ... part first.
- while (!atEOF()) {
- StringRef Op2 = peek();
- if (precedence(Op2) <= precedence(Op1))
- break;
- Rhs = parseExpr1(Rhs, precedence(Op2));
+static void sortSections(InputSectionData **Begin, InputSectionData **End,
+ SortSectionPolicy K) {
+ if (K != SortSectionPolicy::Default && K != SortSectionPolicy::None)
+ std::stable_sort(Begin, End, getComparator(K));
+}
+
+// Compute and remember which sections the InputSectionDescription matches.
+template <class ELFT>
+void LinkerScript<ELFT>::computeInputSections(InputSectionDescription *I) {
+ // Collects all sections that satisfy constraints of I
+ // and attach them to I.
+ for (SectionPattern &Pat : I->SectionPatterns) {
+ size_t SizeBefore = I->Sections.size();
+
+ for (InputSectionBase<ELFT> *S : Symtab<ELFT>::X->Sections) {
+ if (!S->Live || S->Assigned)
+ continue;
+
+ StringRef Filename = basename(S);
+ if (!I->FilePat.match(Filename) || Pat.ExcludedFilePat.match(Filename))
+ continue;
+ if (!Pat.SectionPat.match(S->Name))
+ continue;
+ I->Sections.push_back(S);
+ S->Assigned = true;
}
- Lhs = apply(Op1, Lhs, Rhs);
+ // Sort sections as instructed by SORT-family commands and --sort-section
+ // option. Because SORT-family commands can be nested at most two depth
+ // (e.g. SORT_BY_NAME(SORT_BY_ALIGNMENT(.text.*))) and because the command
+ // line option is respected even if a SORT command is given, the exact
+ // behavior we have here is a bit complicated. Here are the rules.
+ //
+ // 1. If two SORT commands are given, --sort-section is ignored.
+ // 2. If one SORT command is given, and if it is not SORT_NONE,
+ // --sort-section is handled as an inner SORT command.
+ // 3. If one SORT command is given, and if it is SORT_NONE, don't sort.
+ // 4. If no SORT command is given, sort according to --sort-section.
+ InputSectionData **Begin = I->Sections.data() + SizeBefore;
+ InputSectionData **End = I->Sections.data() + I->Sections.size();
+ if (Pat.SortOuter != SortSectionPolicy::None) {
+ if (Pat.SortInner == SortSectionPolicy::Default)
+ sortSections(Begin, End, Config->SortSection);
+ else
+ sortSections(Begin, End, Pat.SortInner);
+ sortSections(Begin, End, Pat.SortOuter);
+ }
}
- return Lhs;
}
-// Reads and evaluates an arithmetic expression.
-uint64_t ExprParser::parseExpr() { return parseExpr1(parsePrimary(), 0); }
-
template <class ELFT>
-StringRef LinkerScript<ELFT>::getOutputSection(InputSectionBase<ELFT> *S) {
- for (SectionRule &R : Opt.Sections)
- if (globMatch(R.SectionPattern, S->getSectionName()))
- return R.Dest;
- return "";
+void LinkerScript<ELFT>::discard(ArrayRef<InputSectionBase<ELFT> *> V) {
+ for (InputSectionBase<ELFT> *S : V) {
+ S->Live = false;
+ reportDiscarded(S);
+ }
}
template <class ELFT>
-bool LinkerScript<ELFT>::isDiscarded(InputSectionBase<ELFT> *S) {
- return getOutputSection(S) == "/DISCARD/";
+std::vector<InputSectionBase<ELFT> *>
+LinkerScript<ELFT>::createInputSectionList(OutputSectionCommand &OutCmd) {
+ std::vector<InputSectionBase<ELFT> *> Ret;
+
+ for (const std::unique_ptr<BaseCommand> &Base : OutCmd.Commands) {
+ auto *Cmd = dyn_cast<InputSectionDescription>(Base.get());
+ if (!Cmd)
+ continue;
+ computeInputSections(Cmd);
+ for (InputSectionData *S : Cmd->Sections)
+ Ret.push_back(static_cast<InputSectionBase<ELFT> *>(S));
+ }
+
+ return Ret;
}
template <class ELFT>
-bool LinkerScript<ELFT>::shouldKeep(InputSectionBase<ELFT> *S) {
- for (StringRef Pat : Opt.KeptSections)
- if (globMatch(Pat, S->getSectionName()))
- return true;
- return false;
+static SectionKey<ELFT::Is64Bits> createKey(InputSectionBase<ELFT> *C,
+ StringRef OutsecName) {
+ // When using linker script the merge rules are different.
+ // Unfortunately, linker scripts are name based. This means that expressions
+ // like *(.foo*) can refer to multiple input sections that would normally be
+ // placed in different output sections. We cannot put them in different
+ // output sections or we would produce wrong results for
+ // start = .; *(.foo.*) end = .; *(.bar)
+ // and a mapping of .foo1 and .bar1 to one section and .foo2 and .bar2 to
+ // another. The problem is that there is no way to layout those output
+ // sections such that the .foo sections are the only thing between the
+ // start and end symbols.
+
+ // An extra annoyance is that we cannot simply disable merging of the contents
+ // of SHF_MERGE sections, but our implementation requires one output section
+ // per "kind" (string or not, which size/aligment).
+ // Fortunately, creating symbols in the middle of a merge section is not
+ // supported by bfd or gold, so we can just create multiple section in that
+ // case.
+ typedef typename ELFT::uint uintX_t;
+ uintX_t Flags = C->Flags & (SHF_MERGE | SHF_STRINGS);
+
+ uintX_t Alignment = 0;
+ if (isa<MergeInputSection<ELFT>>(C))
+ Alignment = std::max<uintX_t>(C->Alignment, C->Entsize);
+
+ return SectionKey<ELFT::Is64Bits>{OutsecName, /*Type*/ 0, Flags, Alignment};
}
template <class ELFT>
-void LinkerScript<ELFT>::assignAddresses(
- ArrayRef<OutputSectionBase<ELFT> *> Sections) {
- // Orphan sections are sections present in the input files which
- // are not explicitly placed into the output file by the linker script.
- // We place orphan sections at end of file.
- // Other linkers places them using some heuristics as described in
- // https://sourceware.org/binutils/docs/ld/Orphan-Sections.html#Orphan-Sections.
- for (OutputSectionBase<ELFT> *Sec : Sections) {
- StringRef Name = Sec->getName();
- if (getSectionIndex(Name) == INT_MAX)
- Opt.Commands.push_back({SectionKind, {}, Name});
- }
+void LinkerScript<ELFT>::addSection(OutputSectionFactory<ELFT> &Factory,
+ InputSectionBase<ELFT> *Sec,
+ StringRef Name) {
+ OutputSectionBase *OutSec;
+ bool IsNew;
+ std::tie(OutSec, IsNew) = Factory.create(createKey(Sec, Name), Sec);
+ if (IsNew)
+ OutputSections->push_back(OutSec);
+ OutSec->addSection(Sec);
+}
- // Assign addresses as instructed by linker script SECTIONS sub-commands.
- Dot = Out<ELFT>::ElfHeader->getSize() + Out<ELFT>::ProgramHeaders->getSize();
- uintX_t MinVA = std::numeric_limits<uintX_t>::max();
- uintX_t ThreadBssOffset = 0;
+template <class ELFT>
+void LinkerScript<ELFT>::processCommands(OutputSectionFactory<ELFT> &Factory) {
+ for (unsigned I = 0; I < Opt.Commands.size(); ++I) {
+ auto Iter = Opt.Commands.begin() + I;
+ const std::unique_ptr<BaseCommand> &Base1 = *Iter;
- for (SectionsCommand &Cmd : Opt.Commands) {
- if (Cmd.Kind == AssignmentKind) {
- uint64_t Val = evalExpr(Cmd.Expr, Dot);
+ // Handle symbol assignments outside of any output section.
+ if (auto *Cmd = dyn_cast<SymbolAssignment>(Base1.get())) {
+ if (shouldDefine<ELFT>(Cmd))
+ addSymbol<ELFT>(Cmd);
+ continue;
+ }
- if (Cmd.Name == ".") {
- Dot = Val;
- } else {
- auto *D = cast<DefinedRegular<ELFT>>(Symtab<ELFT>::X->find(Cmd.Name));
- D->Value = Val;
- }
+ if (auto *Cmd = dyn_cast<AssertCommand>(Base1.get())) {
+ // If we don't have SECTIONS then output sections have already been
+ // created by Writer<ELFT>. The LinkerScript<ELFT>::assignAddresses
+ // will not be called, so ASSERT should be evaluated now.
+ if (!Opt.HasSections)
+ Cmd->Expression(0);
continue;
}
- // Find all the sections with required name. There can be more than
- // ont section with such name, if the alignment, flags or type
- // attribute differs.
- assert(Cmd.Kind == SectionKind);
- for (OutputSectionBase<ELFT> *Sec : Sections) {
- if (Sec->getName() != Cmd.Name)
+ if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base1.get())) {
+ std::vector<InputSectionBase<ELFT> *> V = createInputSectionList(*Cmd);
+
+ // The output section name `/DISCARD/' is special.
+ // Any input section assigned to it is discarded.
+ if (Cmd->Name == "/DISCARD/") {
+ discard(V);
continue;
+ }
- if ((Sec->getFlags() & SHF_TLS) && Sec->getType() == SHT_NOBITS) {
- uintX_t TVA = Dot + ThreadBssOffset;
- TVA = alignTo(TVA, Sec->getAlignment());
- Sec->setVA(TVA);
- ThreadBssOffset = TVA - Dot + Sec->getSize();
+ // This is for ONLY_IF_RO and ONLY_IF_RW. An output section directive
+ // ".foo : ONLY_IF_R[OW] { ... }" is handled only if all member input
+ // sections satisfy a given constraint. If not, a directive is handled
+ // as if it wasn't present from the beginning.
+ //
+ // Because we'll iterate over Commands many more times, the easiest
+ // way to "make it as if it wasn't present" is to just remove it.
+ if (!matchConstraints<ELFT>(V, Cmd->Constraint)) {
+ for (InputSectionBase<ELFT> *S : V)
+ S->Assigned = false;
+ Opt.Commands.erase(Iter);
+ --I;
continue;
}
- if (Sec->getFlags() & SHF_ALLOC) {
- Dot = alignTo(Dot, Sec->getAlignment());
- Sec->setVA(Dot);
- MinVA = std::min(MinVA, Dot);
- Dot += Sec->getSize();
+ // A directive may contain symbol definitions like this:
+ // ".foo : { ...; bar = .; }". Handle them.
+ for (const std::unique_ptr<BaseCommand> &Base : Cmd->Commands)
+ if (auto *OutCmd = dyn_cast<SymbolAssignment>(Base.get()))
+ if (shouldDefine<ELFT>(OutCmd))
+ addSymbol<ELFT>(OutCmd);
+
+ // Handle subalign (e.g. ".foo : SUBALIGN(32) { ... }"). If subalign
+ // is given, input sections are aligned to that value, whether the
+ // given value is larger or smaller than the original section alignment.
+ if (Cmd->SubalignExpr) {
+ uint32_t Subalign = Cmd->SubalignExpr(0);
+ for (InputSectionBase<ELFT> *S : V)
+ S->Alignment = Subalign;
+ }
+
+ // Add input sections to an output section.
+ for (InputSectionBase<ELFT> *S : V)
+ addSection(Factory, S, Cmd->Name);
+ }
+ }
+}
+
+// Add sections that didn't match any sections command.
+template <class ELFT>
+void LinkerScript<ELFT>::addOrphanSections(
+ OutputSectionFactory<ELFT> &Factory) {
+ for (InputSectionBase<ELFT> *S : Symtab<ELFT>::X->Sections)
+ if (S->Live && !S->OutSec)
+ addSection(Factory, S, getOutputSectionName(S->Name));
+}
+
+// Sets value of a section-defined symbol. Two kinds of
+// symbols are processed: synthetic symbols, whose value
+// is an offset from beginning of section and regular
+// symbols whose value is absolute.
+template <class ELFT>
+static void assignSectionSymbol(SymbolAssignment *Cmd,
+ typename ELFT::uint Value) {
+ if (!Cmd->Sym)
+ return;
+
+ if (auto *Body = dyn_cast<DefinedSynthetic>(Cmd->Sym)) {
+ Body->Section = Cmd->Expression.Section();
+ Body->Value = Cmd->Expression(Value) - Body->Section->Addr;
+ return;
+ }
+ auto *Body = cast<DefinedRegular<ELFT>>(Cmd->Sym);
+ Body->Value = Cmd->Expression(Value);
+}
+
+template <class ELFT> static bool isTbss(OutputSectionBase *Sec) {
+ return (Sec->Flags & SHF_TLS) && Sec->Type == SHT_NOBITS;
+}
+
+template <class ELFT> void LinkerScript<ELFT>::output(InputSection<ELFT> *S) {
+ if (!AlreadyOutputIS.insert(S).second)
+ return;
+ bool IsTbss = isTbss<ELFT>(CurOutSec);
+
+ uintX_t Pos = IsTbss ? Dot + ThreadBssOffset : Dot;
+ Pos = alignTo(Pos, S->Alignment);
+ S->OutSecOff = Pos - CurOutSec->Addr;
+ Pos += S->getSize();
+
+ // Update output section size after adding each section. This is so that
+ // SIZEOF works correctly in the case below:
+ // .foo { *(.aaa) a = SIZEOF(.foo); *(.bbb) }
+ CurOutSec->Size = Pos - CurOutSec->Addr;
+
+ if (IsTbss)
+ ThreadBssOffset = Pos - Dot;
+ else
+ Dot = Pos;
+}
+
+template <class ELFT> void LinkerScript<ELFT>::flush() {
+ if (!CurOutSec || !AlreadyOutputOS.insert(CurOutSec).second)
+ return;
+ if (auto *OutSec = dyn_cast<OutputSection<ELFT>>(CurOutSec)) {
+ for (InputSection<ELFT> *I : OutSec->Sections)
+ output(I);
+ } else {
+ Dot += CurOutSec->Size;
+ }
+}
+
+template <class ELFT>
+void LinkerScript<ELFT>::switchTo(OutputSectionBase *Sec) {
+ if (CurOutSec == Sec)
+ return;
+ if (AlreadyOutputOS.count(Sec))
+ return;
+
+ flush();
+ CurOutSec = Sec;
+
+ Dot = alignTo(Dot, CurOutSec->Addralign);
+ CurOutSec->Addr = isTbss<ELFT>(CurOutSec) ? Dot + ThreadBssOffset : Dot;
+
+ // 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
+ CurOutSec->setLMAOffset(LMAOffset);
+}
+
+template <class ELFT> void LinkerScript<ELFT>::process(BaseCommand &Base) {
+ // This handles the assignments to symbol or to a location counter (.)
+ if (auto *AssignCmd = dyn_cast<SymbolAssignment>(&Base)) {
+ if (AssignCmd->Name == ".") {
+ // Update to location counter means update to section size.
+ uintX_t Val = AssignCmd->Expression(Dot);
+ if (Val < Dot)
+ error("unable to move location counter backward for: " +
+ CurOutSec->Name);
+ Dot = Val;
+ CurOutSec->Size = Dot - CurOutSec->Addr;
+ return;
+ }
+ assignSectionSymbol<ELFT>(AssignCmd, Dot);
+ return;
+ }
+
+ // Handle BYTE(), SHORT(), LONG(), or QUAD().
+ if (auto *DataCmd = dyn_cast<BytesDataCommand>(&Base)) {
+ DataCmd->Offset = Dot - CurOutSec->Addr;
+ Dot += DataCmd->Size;
+ CurOutSec->Size = Dot - CurOutSec->Addr;
+ return;
+ }
+
+ if (auto *AssertCmd = dyn_cast<AssertCommand>(&Base)) {
+ AssertCmd->Expression(Dot);
+ return;
+ }
+
+ // It handles single input section description command,
+ // calculates and assigns the offsets for each section and also
+ // updates the output section size.
+ auto &ICmd = cast<InputSectionDescription>(Base);
+ for (InputSectionData *ID : ICmd.Sections) {
+ // We tentatively added all synthetic sections at the beginning and removed
+ // empty ones afterwards (because there is no way to know whether they were
+ // going be empty or not other than actually running linker scripts.)
+ // We need to ignore remains of empty sections.
+ if (auto *Sec = dyn_cast<SyntheticSection<ELFT>>(ID))
+ if (Sec->empty())
continue;
+
+ auto *IB = static_cast<InputSectionBase<ELFT> *>(ID);
+ switchTo(IB->OutSec);
+ if (auto *I = dyn_cast<InputSection<ELFT>>(IB))
+ output(I);
+ else
+ flush();
+ }
+}
+
+template <class ELFT>
+static std::vector<OutputSectionBase *>
+findSections(StringRef Name, const std::vector<OutputSectionBase *> &Sections) {
+ std::vector<OutputSectionBase *> Ret;
+ for (OutputSectionBase *Sec : Sections)
+ if (Sec->getName() == Name)
+ Ret.push_back(Sec);
+ return Ret;
+}
+
+// This function assigns offsets to input sections and an output section
+// for a single sections command (e.g. ".text { *(.text); }").
+template <class ELFT>
+void LinkerScript<ELFT>::assignOffsets(OutputSectionCommand *Cmd) {
+ if (Cmd->LMAExpr)
+ LMAOffset = Cmd->LMAExpr(Dot) - Dot;
+ std::vector<OutputSectionBase *> Sections =
+ findSections<ELFT>(Cmd->Name, *OutputSections);
+ if (Sections.empty())
+ return;
+ switchTo(Sections[0]);
+
+ // Find the last section output location. We will output orphan sections
+ // there so that end symbols point to the correct location.
+ auto E = std::find_if(Cmd->Commands.rbegin(), Cmd->Commands.rend(),
+ [](const std::unique_ptr<BaseCommand> &Cmd) {
+ return !isa<SymbolAssignment>(*Cmd);
+ })
+ .base();
+ for (auto I = Cmd->Commands.begin(); I != E; ++I)
+ process(**I);
+ for (OutputSectionBase *Base : Sections)
+ switchTo(Base);
+ flush();
+ std::for_each(E, Cmd->Commands.end(),
+ [this](std::unique_ptr<BaseCommand> &B) { process(*B.get()); });
+}
+
+template <class ELFT> void LinkerScript<ELFT>::removeEmptyCommands() {
+ // It is common practice to use very generic linker scripts. So for any
+ // given run some of the output sections in the script will be empty.
+ // We could create corresponding empty output sections, but that would
+ // clutter the output.
+ // We instead remove trivially empty sections. The bfd linker seems even
+ // more aggressive at removing them.
+ auto Pos = std::remove_if(
+ Opt.Commands.begin(), Opt.Commands.end(),
+ [&](const std::unique_ptr<BaseCommand> &Base) {
+ if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base.get()))
+ return findSections<ELFT>(Cmd->Name, *OutputSections).empty();
+ return false;
+ });
+ Opt.Commands.erase(Pos, Opt.Commands.end());
+}
+
+static bool isAllSectionDescription(const OutputSectionCommand &Cmd) {
+ for (const std::unique_ptr<BaseCommand> &I : Cmd.Commands)
+ if (!isa<InputSectionDescription>(*I))
+ return false;
+ return true;
+}
+
+template <class ELFT> void LinkerScript<ELFT>::adjustSectionsBeforeSorting() {
+ // If the output section contains only symbol assignments, create a
+ // corresponding output section. The bfd linker seems to only create them if
+ // '.' is assigned to, but creating these section should not have any bad
+ // consequeces and gives us a section to put the symbol in.
+ uintX_t Flags = SHF_ALLOC;
+ uint32_t Type = SHT_NOBITS;
+ for (const std::unique_ptr<BaseCommand> &Base : Opt.Commands) {
+ auto *Cmd = dyn_cast<OutputSectionCommand>(Base.get());
+ if (!Cmd)
+ continue;
+ std::vector<OutputSectionBase *> Secs =
+ findSections<ELFT>(Cmd->Name, *OutputSections);
+ if (!Secs.empty()) {
+ Flags = Secs[0]->Flags;
+ Type = Secs[0]->Type;
+ continue;
+ }
+
+ if (isAllSectionDescription(*Cmd))
+ continue;
+
+ auto *OutSec = make<OutputSection<ELFT>>(Cmd->Name, Type, Flags);
+ OutputSections->push_back(OutSec);
+ }
+}
+
+template <class ELFT> void LinkerScript<ELFT>::adjustSectionsAfterSorting() {
+ placeOrphanSections();
+
+ // If output section command doesn't specify any segments,
+ // and we haven't previously assigned any section to segment,
+ // then we simply assign section to the very first load segment.
+ // Below is an example of such linker script:
+ // PHDRS { seg PT_LOAD; }
+ // SECTIONS { .aaa : { *(.aaa) } }
+ std::vector<StringRef> DefPhdrs;
+ auto FirstPtLoad =
+ std::find_if(Opt.PhdrsCommands.begin(), Opt.PhdrsCommands.end(),
+ [](const PhdrsCommand &Cmd) { return Cmd.Type == PT_LOAD; });
+ if (FirstPtLoad != Opt.PhdrsCommands.end())
+ DefPhdrs.push_back(FirstPtLoad->Name);
+
+ // Walk the commands and propagate the program headers to commands that don't
+ // explicitly specify them.
+ for (const std::unique_ptr<BaseCommand> &Base : Opt.Commands) {
+ auto *Cmd = dyn_cast<OutputSectionCommand>(Base.get());
+ if (!Cmd)
+ continue;
+ if (Cmd->Phdrs.empty())
+ Cmd->Phdrs = DefPhdrs;
+ else
+ DefPhdrs = Cmd->Phdrs;
+ }
+
+ removeEmptyCommands();
+}
+
+// When placing orphan sections, we want to place them after symbol assignments
+// so that an orphan after
+// begin_foo = .;
+// foo : { *(foo) }
+// end_foo = .;
+// doesn't break the intended meaning of the begin/end symbols.
+// We don't want to go over sections since Writer<ELFT>::sortSections is the
+// one in charge of deciding the order of the sections.
+// We don't want to go over alignments, since doing so in
+// rx_sec : { *(rx_sec) }
+// . = ALIGN(0x1000);
+// /* The RW PT_LOAD starts here*/
+// rw_sec : { *(rw_sec) }
+// would mean that the RW PT_LOAD would become unaligned.
+static bool shouldSkip(const BaseCommand &Cmd) {
+ if (isa<OutputSectionCommand>(Cmd))
+ return false;
+ const auto *Assign = dyn_cast<SymbolAssignment>(&Cmd);
+ if (!Assign)
+ return true;
+ return Assign->Name != ".";
+}
+
+// Orphan sections are sections present in the input files which are not
+// explicitly placed into the output file by the linker script. This just
+// places them in the order already decided in OutputSections.
+template <class ELFT> void LinkerScript<ELFT>::placeOrphanSections() {
+ // The OutputSections are already in the correct order.
+ // This loops creates or moves commands as needed so that they are in the
+ // correct order.
+ int CmdIndex = 0;
+
+ // As a horrible special case, skip the first . assignment if it is before any
+ // section. We do this because it is common to set a load address by starting
+ // the script with ". = 0xabcd" and the expectation is that every section is
+ // after that.
+ auto FirstSectionOrDotAssignment =
+ std::find_if(Opt.Commands.begin(), Opt.Commands.end(),
+ [](const std::unique_ptr<BaseCommand> &Cmd) {
+ if (isa<OutputSectionCommand>(*Cmd))
+ return true;
+ const auto *Assign = dyn_cast<SymbolAssignment>(Cmd.get());
+ if (!Assign)
+ return false;
+ return Assign->Name == ".";
+ });
+ if (FirstSectionOrDotAssignment != Opt.Commands.end()) {
+ CmdIndex = FirstSectionOrDotAssignment - Opt.Commands.begin();
+ if (isa<SymbolAssignment>(**FirstSectionOrDotAssignment))
+ ++CmdIndex;
+ }
+
+ for (OutputSectionBase *Sec : *OutputSections) {
+ StringRef Name = Sec->getName();
+
+ // Find the last spot where we can insert a command and still get the
+ // correct result.
+ auto CmdIter = Opt.Commands.begin() + CmdIndex;
+ auto E = Opt.Commands.end();
+ while (CmdIter != E && shouldSkip(**CmdIter)) {
+ ++CmdIter;
+ ++CmdIndex;
+ }
+
+ auto Pos =
+ std::find_if(CmdIter, E, [&](const std::unique_ptr<BaseCommand> &Base) {
+ auto *Cmd = dyn_cast<OutputSectionCommand>(Base.get());
+ return Cmd && Cmd->Name == Name;
+ });
+ if (Pos == E) {
+ Opt.Commands.insert(CmdIter,
+ llvm::make_unique<OutputSectionCommand>(Name));
+ ++CmdIndex;
+ continue;
+ }
+
+ // Continue from where we found it.
+ CmdIndex = (Pos - Opt.Commands.begin()) + 1;
+ }
+}
+
+template <class ELFT>
+void LinkerScript<ELFT>::assignAddresses(std::vector<PhdrEntry> &Phdrs) {
+ // Assign addresses as instructed by linker script SECTIONS sub-commands.
+ Dot = 0;
+
+ for (const std::unique_ptr<BaseCommand> &Base : Opt.Commands) {
+ if (auto *Cmd = dyn_cast<SymbolAssignment>(Base.get())) {
+ if (Cmd->Name == ".") {
+ Dot = Cmd->Expression(Dot);
+ } else if (Cmd->Sym) {
+ assignSectionSymbol<ELFT>(Cmd, Dot);
}
+ continue;
}
+
+ if (auto *Cmd = dyn_cast<AssertCommand>(Base.get())) {
+ Cmd->Expression(Dot);
+ continue;
+ }
+
+ auto *Cmd = cast<OutputSectionCommand>(Base.get());
+ if (Cmd->AddrExpr)
+ Dot = Cmd->AddrExpr(Dot);
+ assignOffsets(Cmd);
}
+ uintX_t MinVA = std::numeric_limits<uintX_t>::max();
+ for (OutputSectionBase *Sec : *OutputSections) {
+ if (Sec->Flags & SHF_ALLOC)
+ MinVA = std::min<uint64_t>(MinVA, Sec->Addr);
+ else
+ Sec->Addr = 0;
+ }
+
+ uintX_t HeaderSize = getHeaderSize();
+ // If the linker script doesn't have PHDRS, add ElfHeader and ProgramHeaders
+ // now that we know we have space.
+ if (HeaderSize <= MinVA && !hasPhdrsCommands())
+ allocateHeaders<ELFT>(Phdrs, *OutputSections);
+
// ELF and Program headers need to be right before the first section in
- // memory.
- // Set their addresses accordingly.
- MinVA = alignDown(MinVA - Out<ELFT>::ElfHeader->getSize() -
- Out<ELFT>::ProgramHeaders->getSize(),
- Target->PageSize);
- Out<ELFT>::ElfHeader->setVA(MinVA);
- Out<ELFT>::ProgramHeaders->setVA(Out<ELFT>::ElfHeader->getSize() + MinVA);
+ // memory. Set their addresses accordingly.
+ MinVA = alignDown(MinVA - HeaderSize, Config->MaxPageSize);
+ Out<ELFT>::ElfHeader->Addr = MinVA;
+ Out<ELFT>::ProgramHeaders->Addr = Out<ELFT>::ElfHeader->Size + MinVA;
+}
+
+// Creates program headers as instructed by PHDRS linker script command.
+template <class ELFT> std::vector<PhdrEntry> LinkerScript<ELFT>::createPhdrs() {
+ std::vector<PhdrEntry> Ret;
+
+ // Process PHDRS and FILEHDR keywords because they are not
+ // real output sections and cannot be added in the following loop.
+ for (const PhdrsCommand &Cmd : Opt.PhdrsCommands) {
+ Ret.emplace_back(Cmd.Type, Cmd.Flags == UINT_MAX ? PF_R : Cmd.Flags);
+ PhdrEntry &Phdr = Ret.back();
+
+ if (Cmd.HasFilehdr)
+ Phdr.add(Out<ELFT>::ElfHeader);
+ if (Cmd.HasPhdrs)
+ Phdr.add(Out<ELFT>::ProgramHeaders);
+
+ if (Cmd.LMAExpr) {
+ Phdr.p_paddr = Cmd.LMAExpr(0);
+ Phdr.HasLMA = true;
+ }
+ }
+
+ // Add output sections to program headers.
+ for (OutputSectionBase *Sec : *OutputSections) {
+ if (!(Sec->Flags & SHF_ALLOC))
+ break;
+
+ // Assign headers specified by linker script
+ for (size_t Id : getPhdrIndices(Sec->getName())) {
+ Ret[Id].add(Sec);
+ if (Opt.PhdrsCommands[Id].Flags == UINT_MAX)
+ Ret[Id].p_flags |= Sec->getPhdrFlags();
+ }
+ }
+ return Ret;
+}
+
+template <class ELFT> bool LinkerScript<ELFT>::ignoreInterpSection() {
+ // Ignore .interp section in case we have PHDRS specification
+ // and PT_INTERP isn't listed.
+ return !Opt.PhdrsCommands.empty() &&
+ llvm::find_if(Opt.PhdrsCommands, [](const PhdrsCommand &Cmd) {
+ return Cmd.Type == PT_INTERP;
+ }) == Opt.PhdrsCommands.end();
+}
+
+template <class ELFT> uint32_t LinkerScript<ELFT>::getFiller(StringRef Name) {
+ for (const std::unique_ptr<BaseCommand> &Base : Opt.Commands)
+ if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base.get()))
+ if (Cmd->Name == Name)
+ return Cmd->Filler;
+ return 0;
}
template <class ELFT>
-ArrayRef<uint8_t> LinkerScript<ELFT>::getFiller(StringRef Name) {
- auto I = Opt.Filler.find(Name);
- if (I == Opt.Filler.end())
- return {};
- return I->second;
+static void writeInt(uint8_t *Buf, uint64_t Data, uint64_t Size) {
+ const endianness E = ELFT::TargetEndianness;
+
+ switch (Size) {
+ case 1:
+ *Buf = (uint8_t)Data;
+ break;
+ case 2:
+ write16<E>(Buf, Data);
+ break;
+ case 4:
+ write32<E>(Buf, Data);
+ break;
+ case 8:
+ write64<E>(Buf, Data);
+ break;
+ default:
+ llvm_unreachable("unsupported Size argument");
+ }
+}
+
+template <class ELFT>
+void LinkerScript<ELFT>::writeDataBytes(StringRef Name, uint8_t *Buf) {
+ int I = getSectionIndex(Name);
+ if (I == INT_MAX)
+ return;
+
+ auto *Cmd = dyn_cast<OutputSectionCommand>(Opt.Commands[I].get());
+ for (const std::unique_ptr<BaseCommand> &Base : Cmd->Commands)
+ if (auto *Data = dyn_cast<BytesDataCommand>(Base.get()))
+ writeInt<ELFT>(Buf + Data->Offset, Data->Expression(0), Data->Size);
+}
+
+template <class ELFT> bool LinkerScript<ELFT>::hasLMA(StringRef Name) {
+ for (const std::unique_ptr<BaseCommand> &Base : Opt.Commands)
+ if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base.get()))
+ if (Cmd->LMAExpr && Cmd->Name == Name)
+ return true;
+ return false;
}
// Returns the index of the given section name in linker script
// SECTIONS commands. Sections are laid out as the same order as they
// were in the script. If a given name did not appear in the script,
// it returns INT_MAX, so that it will be laid out at end of file.
+template <class ELFT> int LinkerScript<ELFT>::getSectionIndex(StringRef Name) {
+ for (int I = 0, E = Opt.Commands.size(); I != E; ++I)
+ if (auto *Cmd = dyn_cast<OutputSectionCommand>(Opt.Commands[I].get()))
+ if (Cmd->Name == Name)
+ return I;
+ return INT_MAX;
+}
+
+template <class ELFT> bool LinkerScript<ELFT>::hasPhdrsCommands() {
+ return !Opt.PhdrsCommands.empty();
+}
+
template <class ELFT>
-int LinkerScript<ELFT>::getSectionIndex(StringRef Name) {
- auto Begin = Opt.Commands.begin();
- auto End = Opt.Commands.end();
- auto I = std::find_if(Begin, End, [&](SectionsCommand &N) {
- return N.Kind == SectionKind && N.Name == Name;
- });
- return I == End ? INT_MAX : (I - Begin);
+const OutputSectionBase *LinkerScript<ELFT>::getOutputSection(const Twine &Loc,
+ StringRef Name) {
+ static OutputSectionBase FakeSec("", 0, 0);
+
+ for (OutputSectionBase *Sec : *OutputSections)
+ if (Sec->getName() == Name)
+ return Sec;
+
+ error(Loc + ": undefined section " + Name);
+ return &FakeSec;
+}
+
+// This function is essentially the same as getOutputSection(Name)->Size,
+// but it won't print out an error message if a given section is not found.
+//
+// Linker script does not create an output section if its content is empty.
+// We want to allow SIZEOF(.foo) where .foo is a section which happened to
+// be empty. That is why this function is different from getOutputSection().
+template <class ELFT>
+uint64_t LinkerScript<ELFT>::getOutputSectionSize(StringRef Name) {
+ for (OutputSectionBase *Sec : *OutputSections)
+ if (Sec->getName() == Name)
+ return Sec->Size;
+ return 0;
+}
+
+template <class ELFT> uint64_t LinkerScript<ELFT>::getHeaderSize() {
+ return elf::getHeaderSize<ELFT>();
}
-// A compartor to sort output sections. Returns -1 or 1 if
-// A or B are mentioned in linker script. Otherwise, returns 0.
template <class ELFT>
-int LinkerScript<ELFT>::compareSections(StringRef A, StringRef B) {
- int I = getSectionIndex(A);
- int J = getSectionIndex(B);
- if (I == INT_MAX && J == INT_MAX)
- return 0;
- return I < J ? -1 : 1;
+uint64_t LinkerScript<ELFT>::getSymbolValue(const Twine &Loc, StringRef S) {
+ if (SymbolBody *B = Symtab<ELFT>::X->find(S))
+ return B->getVA<ELFT>();
+ error(Loc + ": symbol not found: " + S);
+ return 0;
+}
+
+template <class ELFT> bool LinkerScript<ELFT>::isDefined(StringRef S) {
+ return Symtab<ELFT>::X->find(S) != nullptr;
+}
+
+template <class ELFT> bool LinkerScript<ELFT>::isAbsolute(StringRef S) {
+ SymbolBody *Sym = Symtab<ELFT>::X->find(S);
+ auto *DR = dyn_cast_or_null<DefinedRegular<ELFT>>(Sym);
+ return DR && !DR->Section;
}
+// Gets section symbol belongs to. Symbol "." doesn't belong to any
+// specific section but isn't absolute at the same time, so we try
+// to find suitable section for it as well.
template <class ELFT>
-void LinkerScript<ELFT>::addScriptedSymbols() {
- for (SectionsCommand &Cmd : Opt.Commands)
- if (Cmd.Kind == AssignmentKind)
- if (Cmd.Name != "." && Symtab<ELFT>::X->find(Cmd.Name) == nullptr)
- Symtab<ELFT>::X->addAbsolute(Cmd.Name, STV_DEFAULT);
+const OutputSectionBase *LinkerScript<ELFT>::getSymbolSection(StringRef S) {
+ SymbolBody *Sym = Symtab<ELFT>::X->find(S);
+ if (!Sym) {
+ if (OutputSections->empty())
+ return nullptr;
+ return CurOutSec ? CurOutSec : (*OutputSections)[0];
+ }
+
+ if (auto *DR = dyn_cast_or_null<DefinedRegular<ELFT>>(Sym))
+ return DR->Section ? DR->Section->OutSec : nullptr;
+ if (auto *DS = dyn_cast_or_null<DefinedSynthetic>(Sym))
+ return DS->Section;
+
+ return nullptr;
}
-class elf::ScriptParser : public ScriptParserBase {
+// Returns indices of ELF headers containing specific section, identified
+// by Name. Each index is a zero based number of ELF header listed within
+// PHDRS {} script block.
+template <class ELFT>
+std::vector<size_t> LinkerScript<ELFT>::getPhdrIndices(StringRef SectionName) {
+ for (const std::unique_ptr<BaseCommand> &Base : Opt.Commands) {
+ auto *Cmd = dyn_cast<OutputSectionCommand>(Base.get());
+ if (!Cmd || Cmd->Name != SectionName)
+ continue;
+
+ std::vector<size_t> Ret;
+ for (StringRef PhdrName : Cmd->Phdrs)
+ Ret.push_back(getPhdrIndex(Cmd->Location, PhdrName));
+ return Ret;
+ }
+ return {};
+}
+
+template <class ELFT>
+size_t LinkerScript<ELFT>::getPhdrIndex(const Twine &Loc, StringRef PhdrName) {
+ size_t I = 0;
+ for (PhdrsCommand &Cmd : Opt.PhdrsCommands) {
+ if (Cmd.Name == PhdrName)
+ return I;
+ ++I;
+ }
+ error(Loc + ": section header '" + PhdrName + "' is not listed in PHDRS");
+ return 0;
+}
+
+class elf::ScriptParser final : public ScriptParserBase {
typedef void (ScriptParser::*Handler)();
public:
- ScriptParser(StringRef S, bool B) : ScriptParserBase(S), IsUnderSysroot(B) {}
+ ScriptParser(MemoryBufferRef MB)
+ : ScriptParserBase(MB),
+ IsUnderSysroot(isUnderSysroot(MB.getBufferIdentifier())) {}
- void run();
+ void readLinkerScript();
+ void readVersionScript();
+ void readDynamicList();
private:
void addFile(StringRef Path);
@@ -331,53 +989,130 @@ private:
void readExtern();
void readGroup();
void readInclude();
- void readNothing() {}
void readOutput();
void readOutputArch();
void readOutputFormat();
+ void readPhdrs();
void readSearchDir();
void readSections();
+ void readVersion();
+ void readVersionScriptCommand();
- void readLocationCounterValue();
- void readOutputSectionDescription(StringRef OutSec);
- void readSymbolAssignment(StringRef Name);
- std::vector<StringRef> readSectionsCommandExpr();
+ SymbolAssignment *readAssignment(StringRef Name);
+ BytesDataCommand *readBytesDataCommand(StringRef Tok);
+ uint32_t readFill();
+ OutputSectionCommand *readOutputSectionDescription(StringRef OutSec);
+ uint32_t readOutputSectionFiller(StringRef Tok);
+ std::vector<StringRef> readOutputSectionPhdrs();
+ InputSectionDescription *readInputSectionDescription(StringRef Tok);
+ StringMatcher readFilePatterns();
+ std::vector<SectionPattern> readInputSectionsList();
+ InputSectionDescription *readInputSectionRules(StringRef FilePattern);
+ unsigned readPhdrType();
+ SortSectionPolicy readSortKind();
+ SymbolAssignment *readProvideHidden(bool Provide, bool Hidden);
+ SymbolAssignment *readProvideOrAssignment(StringRef Tok);
+ void readSort();
+ Expr readAssert();
+
+ Expr readExpr();
+ Expr readExpr1(Expr Lhs, int MinPrec);
+ StringRef readParenLiteral();
+ Expr readPrimary();
+ Expr readTernary(Expr Cond);
+ Expr readParenExpr();
+
+ // For parsing version script.
+ std::vector<SymbolVersion> readVersionExtern();
+ void readAnonymousDeclaration();
+ void readVersionDeclaration(StringRef VerStr);
+ std::vector<SymbolVersion> readSymbols();
- const static StringMap<Handler> Cmd;
ScriptConfiguration &Opt = *ScriptConfig;
- StringSaver Saver = {ScriptConfig->Alloc};
bool IsUnderSysroot;
};
-const StringMap<elf::ScriptParser::Handler> elf::ScriptParser::Cmd = {
- {"ENTRY", &ScriptParser::readEntry},
- {"EXTERN", &ScriptParser::readExtern},
- {"GROUP", &ScriptParser::readGroup},
- {"INCLUDE", &ScriptParser::readInclude},
- {"INPUT", &ScriptParser::readGroup},
- {"OUTPUT", &ScriptParser::readOutput},
- {"OUTPUT_ARCH", &ScriptParser::readOutputArch},
- {"OUTPUT_FORMAT", &ScriptParser::readOutputFormat},
- {"SEARCH_DIR", &ScriptParser::readSearchDir},
- {"SECTIONS", &ScriptParser::readSections},
- {";", &ScriptParser::readNothing}};
-
-void ScriptParser::run() {
+void ScriptParser::readDynamicList() {
+ expect("{");
+ readAnonymousDeclaration();
+ if (!atEOF())
+ setError("EOF expected, but got " + next());
+}
+
+void ScriptParser::readVersionScript() {
+ readVersionScriptCommand();
+ if (!atEOF())
+ setError("EOF expected, but got " + next());
+}
+
+void ScriptParser::readVersionScriptCommand() {
+ if (consume("{")) {
+ readAnonymousDeclaration();
+ return;
+ }
+
+ while (!atEOF() && !Error && peek() != "}") {
+ StringRef VerStr = next();
+ if (VerStr == "{") {
+ setError("anonymous version definition is used in "
+ "combination with other version definitions");
+ return;
+ }
+ expect("{");
+ readVersionDeclaration(VerStr);
+ }
+}
+
+void ScriptParser::readVersion() {
+ expect("{");
+ readVersionScriptCommand();
+ expect("}");
+}
+
+void ScriptParser::readLinkerScript() {
while (!atEOF()) {
StringRef Tok = next();
- if (Handler Fn = Cmd.lookup(Tok))
- (this->*Fn)();
- else
+ if (Tok == ";")
+ continue;
+
+ if (Tok == "ASSERT") {
+ Opt.Commands.emplace_back(new AssertCommand(readAssert()));
+ } else if (Tok == "ENTRY") {
+ readEntry();
+ } else if (Tok == "EXTERN") {
+ readExtern();
+ } else if (Tok == "GROUP" || Tok == "INPUT") {
+ readGroup();
+ } else if (Tok == "INCLUDE") {
+ readInclude();
+ } else if (Tok == "OUTPUT") {
+ readOutput();
+ } else if (Tok == "OUTPUT_ARCH") {
+ readOutputArch();
+ } else if (Tok == "OUTPUT_FORMAT") {
+ readOutputFormat();
+ } else if (Tok == "PHDRS") {
+ readPhdrs();
+ } else if (Tok == "SEARCH_DIR") {
+ readSearchDir();
+ } else if (Tok == "SECTIONS") {
+ readSections();
+ } else if (Tok == "VERSION") {
+ readVersion();
+ } else if (SymbolAssignment *Cmd = readProvideOrAssignment(Tok)) {
+ Opt.Commands.emplace_back(Cmd);
+ } else {
setError("unknown directive: " + Tok);
+ }
}
}
void ScriptParser::addFile(StringRef S) {
if (IsUnderSysroot && S.startswith("/")) {
- SmallString<128> Path;
- (Config->Sysroot + S).toStringRef(Path);
+ SmallString<128> PathData;
+ StringRef Path = (Config->Sysroot + S).toStringRef(PathData);
if (sys::fs::exists(Path)) {
- Driver->addFile(Saver.save(Path.str()));
+ Driver->addFile(Saver.save(Path));
return;
}
}
@@ -394,11 +1129,10 @@ void ScriptParser::addFile(StringRef S) {
} else if (sys::fs::exists(S)) {
Driver->addFile(S);
} else {
- std::string Path = findFromSearchPaths(S);
- if (Path.empty())
- setError("unable to find " + S);
+ if (Optional<std::string> Path = findFromSearchPaths(S))
+ Driver->addFile(Saver.save(*Path));
else
- Driver->addFile(Saver.save(Path));
+ setError("unable to find " + S);
}
}
@@ -406,12 +1140,8 @@ void ScriptParser::readAsNeeded() {
expect("(");
bool Orig = Config->AsNeeded;
Config->AsNeeded = true;
- while (!Error) {
- StringRef Tok = next();
- if (Tok == ")")
- break;
- addFile(Tok);
- }
+ while (!Error && !consume(")"))
+ addFile(unquote(next()));
Config->AsNeeded = Orig;
}
@@ -426,39 +1156,37 @@ void ScriptParser::readEntry() {
void ScriptParser::readExtern() {
expect("(");
- while (!Error) {
- StringRef Tok = next();
- if (Tok == ")")
- return;
- Config->Undefined.push_back(Tok);
- }
+ while (!Error && !consume(")"))
+ Config->Undefined.push_back(next());
}
void ScriptParser::readGroup() {
expect("(");
- while (!Error) {
+ while (!Error && !consume(")")) {
StringRef Tok = next();
- if (Tok == ")")
- return;
- if (Tok == "AS_NEEDED") {
+ if (Tok == "AS_NEEDED")
readAsNeeded();
- continue;
- }
- addFile(Tok);
+ else
+ addFile(unquote(Tok));
}
}
void ScriptParser::readInclude() {
- StringRef Tok = next();
+ StringRef Tok = unquote(next());
+ // https://sourceware.org/binutils/docs/ld/File-Commands.html:
+ // The file will be searched for in the current directory, and in any
+ // directory specified with the -L option.
auto MBOrErr = MemoryBuffer::getFile(Tok);
+ if (!MBOrErr)
+ if (Optional<std::string> Path = findFromSearchPaths(Tok))
+ MBOrErr = MemoryBuffer::getFile(*Path);
if (!MBOrErr) {
setError("cannot open " + Tok);
return;
}
- std::unique_ptr<MemoryBuffer> &MB = *MBOrErr;
- StringRef S = Saver.save(MB->getMemBufferRef().getBuffer());
- std::vector<StringRef> V = tokenize(S);
- Tokens.insert(Tokens.begin() + Pos, V.begin(), V.end());
+ MemoryBufferRef MBRef = (*MBOrErr)->getMemBufferRef();
+ make<std::unique_ptr<MemoryBuffer>>(std::move(*MBOrErr)); // take MB ownership
+ tokenize(MBRef);
}
void ScriptParser::readOutput() {
@@ -466,138 +1194,782 @@ void ScriptParser::readOutput() {
expect("(");
StringRef Tok = next();
if (Config->OutputFile.empty())
- Config->OutputFile = Tok;
+ Config->OutputFile = unquote(Tok);
expect(")");
}
void ScriptParser::readOutputArch() {
// Error checking only for now.
expect("(");
- next();
+ skip();
expect(")");
}
void ScriptParser::readOutputFormat() {
// Error checking only for now.
expect("(");
- next();
+ skip();
StringRef Tok = next();
if (Tok == ")")
- return;
+ return;
if (Tok != ",") {
setError("unexpected token: " + Tok);
return;
}
- next();
+ skip();
expect(",");
- next();
+ skip();
expect(")");
}
+void ScriptParser::readPhdrs() {
+ expect("{");
+ while (!Error && !consume("}")) {
+ StringRef Tok = next();
+ Opt.PhdrsCommands.push_back(
+ {Tok, PT_NULL, false, false, UINT_MAX, nullptr});
+ PhdrsCommand &PhdrCmd = Opt.PhdrsCommands.back();
+
+ PhdrCmd.Type = readPhdrType();
+ do {
+ Tok = next();
+ if (Tok == ";")
+ break;
+ if (Tok == "FILEHDR")
+ PhdrCmd.HasFilehdr = true;
+ else if (Tok == "PHDRS")
+ PhdrCmd.HasPhdrs = true;
+ else if (Tok == "AT")
+ PhdrCmd.LMAExpr = readParenExpr();
+ else if (Tok == "FLAGS") {
+ expect("(");
+ // Passing 0 for the value of dot is a bit of a hack. It means that
+ // we accept expressions like ".|1".
+ PhdrCmd.Flags = readExpr()(0);
+ expect(")");
+ } else
+ setError("unexpected header attribute: " + Tok);
+ } while (!Error);
+ }
+}
+
void ScriptParser::readSearchDir() {
expect("(");
- Config->SearchPaths.push_back(next());
+ StringRef Tok = next();
+ if (!Config->Nostdlib)
+ Config->SearchPaths.push_back(unquote(Tok));
expect(")");
}
void ScriptParser::readSections() {
- Opt.DoLayout = true;
+ Opt.HasSections = true;
+ // -no-rosegment is used to avoid placing read only non-executable sections in
+ // their own segment. We do the same if SECTIONS command is present in linker
+ // script. See comment for computeFlags().
+ Config->SingleRoRx = true;
+
expect("{");
- while (!Error && !skip("}")) {
- StringRef Tok = peek();
- if (Tok == ".") {
- readLocationCounterValue();
- continue;
+ while (!Error && !consume("}")) {
+ StringRef Tok = next();
+ BaseCommand *Cmd = readProvideOrAssignment(Tok);
+ if (!Cmd) {
+ if (Tok == "ASSERT")
+ Cmd = new AssertCommand(readAssert());
+ else
+ Cmd = readOutputSectionDescription(Tok);
}
- next();
- if (peek() == "=")
- readSymbolAssignment(Tok);
- else
- readOutputSectionDescription(Tok);
+ Opt.Commands.emplace_back(Cmd);
}
}
-void ScriptParser::readLocationCounterValue() {
- expect(".");
- expect("=");
- std::vector<StringRef> Expr = readSectionsCommandExpr();
- if (Expr.empty())
- error("error in location counter expression");
- else
- Opt.Commands.push_back({AssignmentKind, std::move(Expr), "."});
+static int precedence(StringRef Op) {
+ return StringSwitch<int>(Op)
+ .Cases("*", "/", 5)
+ .Cases("+", "-", 4)
+ .Cases("<<", ">>", 3)
+ .Cases("<", "<=", ">", ">=", "==", "!=", 2)
+ .Cases("&", "|", 1)
+ .Default(-1);
}
-void ScriptParser::readOutputSectionDescription(StringRef OutSec) {
- Opt.Commands.push_back({SectionKind, {}, OutSec});
- expect(":");
- expect("{");
+StringMatcher ScriptParser::readFilePatterns() {
+ std::vector<StringRef> V;
+ while (!Error && !consume(")"))
+ V.push_back(next());
+ return StringMatcher(V);
+}
- while (!Error && !skip("}")) {
- StringRef Tok = next();
- if (Tok == "*") {
- expect("(");
- while (!Error && !skip(")"))
- Opt.Sections.emplace_back(OutSec, next());
- } else if (Tok == "KEEP") {
+SortSectionPolicy ScriptParser::readSortKind() {
+ if (consume("SORT") || consume("SORT_BY_NAME"))
+ return SortSectionPolicy::Name;
+ if (consume("SORT_BY_ALIGNMENT"))
+ return SortSectionPolicy::Alignment;
+ if (consume("SORT_BY_INIT_PRIORITY"))
+ return SortSectionPolicy::Priority;
+ if (consume("SORT_NONE"))
+ return SortSectionPolicy::None;
+ return SortSectionPolicy::Default;
+}
+
+// Method reads a list of sequence of excluded files and section globs given in
+// a following form: ((EXCLUDE_FILE(file_pattern+))? section_pattern+)+
+// Example: *(.foo.1 EXCLUDE_FILE (*a.o) .foo.2 EXCLUDE_FILE (*b.o) .foo.3)
+// The semantics of that is next:
+// * Include .foo.1 from every file.
+// * Include .foo.2 from every file but a.o
+// * Include .foo.3 from every file but b.o
+std::vector<SectionPattern> ScriptParser::readInputSectionsList() {
+ std::vector<SectionPattern> Ret;
+ while (!Error && peek() != ")") {
+ StringMatcher ExcludeFilePat;
+ if (consume("EXCLUDE_FILE")) {
expect("(");
- expect("*");
+ ExcludeFilePat = readFilePatterns();
+ }
+
+ std::vector<StringRef> V;
+ while (!Error && peek() != ")" && peek() != "EXCLUDE_FILE")
+ V.push_back(next());
+
+ if (!V.empty())
+ Ret.push_back({std::move(ExcludeFilePat), StringMatcher(V)});
+ else
+ setError("section pattern is expected");
+ }
+ return Ret;
+}
+
+// Reads contents of "SECTIONS" directive. That directive contains a
+// list of glob patterns for input sections. The grammar is as follows.
+//
+// <patterns> ::= <section-list>
+// | <sort> "(" <section-list> ")"
+// | <sort> "(" <sort> "(" <section-list> ")" ")"
+//
+// <sort> ::= "SORT" | "SORT_BY_NAME" | "SORT_BY_ALIGNMENT"
+// | "SORT_BY_INIT_PRIORITY" | "SORT_NONE"
+//
+// <section-list> is parsed by readInputSectionsList().
+InputSectionDescription *
+ScriptParser::readInputSectionRules(StringRef FilePattern) {
+ auto *Cmd = new InputSectionDescription(FilePattern);
+ expect("(");
+ while (!Error && !consume(")")) {
+ SortSectionPolicy Outer = readSortKind();
+ SortSectionPolicy Inner = SortSectionPolicy::Default;
+ std::vector<SectionPattern> V;
+ if (Outer != SortSectionPolicy::Default) {
expect("(");
- while (!Error && !skip(")")) {
- StringRef Sec = next();
- Opt.Sections.emplace_back(OutSec, Sec);
- Opt.KeptSections.push_back(Sec);
+ Inner = readSortKind();
+ if (Inner != SortSectionPolicy::Default) {
+ expect("(");
+ V = readInputSectionsList();
+ expect(")");
+ } else {
+ V = readInputSectionsList();
}
expect(")");
} else {
+ V = readInputSectionsList();
+ }
+
+ for (SectionPattern &Pat : V) {
+ Pat.SortInner = Inner;
+ Pat.SortOuter = Outer;
+ }
+
+ std::move(V.begin(), V.end(), std::back_inserter(Cmd->SectionPatterns));
+ }
+ return Cmd;
+}
+
+InputSectionDescription *
+ScriptParser::readInputSectionDescription(StringRef Tok) {
+ // Input section wildcard can be surrounded by KEEP.
+ // https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep
+ if (Tok == "KEEP") {
+ expect("(");
+ StringRef FilePattern = next();
+ InputSectionDescription *Cmd = readInputSectionRules(FilePattern);
+ expect(")");
+ Opt.KeptSections.push_back(Cmd);
+ return Cmd;
+ }
+ return readInputSectionRules(Tok);
+}
+
+void ScriptParser::readSort() {
+ expect("(");
+ expect("CONSTRUCTORS");
+ expect(")");
+}
+
+Expr ScriptParser::readAssert() {
+ expect("(");
+ Expr E = readExpr();
+ expect(",");
+ StringRef Msg = unquote(next());
+ expect(")");
+ return [=](uint64_t Dot) {
+ uint64_t V = E(Dot);
+ if (!V)
+ error(Msg);
+ return V;
+ };
+}
+
+// Reads a FILL(expr) command. We handle the FILL command as an
+// alias for =fillexp section attribute, which is different from
+// what GNU linkers do.
+// https://sourceware.org/binutils/docs/ld/Output-Section-Data.html
+uint32_t ScriptParser::readFill() {
+ expect("(");
+ uint32_t V = readOutputSectionFiller(next());
+ expect(")");
+ expect(";");
+ return V;
+}
+
+OutputSectionCommand *
+ScriptParser::readOutputSectionDescription(StringRef OutSec) {
+ OutputSectionCommand *Cmd = new OutputSectionCommand(OutSec);
+ Cmd->Location = getCurrentLocation();
+
+ // Read an address expression.
+ // https://sourceware.org/binutils/docs/ld/Output-Section-Address.html#Output-Section-Address
+ if (peek() != ":")
+ Cmd->AddrExpr = readExpr();
+
+ expect(":");
+
+ if (consume("AT"))
+ Cmd->LMAExpr = readParenExpr();
+ if (consume("ALIGN"))
+ Cmd->AlignExpr = readParenExpr();
+ if (consume("SUBALIGN"))
+ Cmd->SubalignExpr = readParenExpr();
+
+ // Parse constraints.
+ if (consume("ONLY_IF_RO"))
+ Cmd->Constraint = ConstraintKind::ReadOnly;
+ if (consume("ONLY_IF_RW"))
+ Cmd->Constraint = ConstraintKind::ReadWrite;
+ expect("{");
+
+ while (!Error && !consume("}")) {
+ StringRef Tok = next();
+ if (SymbolAssignment *Assignment = readProvideOrAssignment(Tok)) {
+ Cmd->Commands.emplace_back(Assignment);
+ } else if (BytesDataCommand *Data = readBytesDataCommand(Tok)) {
+ Cmd->Commands.emplace_back(Data);
+ } else if (Tok == "ASSERT") {
+ Cmd->Commands.emplace_back(new AssertCommand(readAssert()));
+ expect(";");
+ } else if (Tok == "FILL") {
+ Cmd->Filler = readFill();
+ } else if (Tok == "SORT") {
+ readSort();
+ } else if (peek() == "(") {
+ Cmd->Commands.emplace_back(readInputSectionDescription(Tok));
+ } else {
setError("unknown command " + Tok);
}
}
+ Cmd->Phdrs = readOutputSectionPhdrs();
- StringRef Tok = peek();
- if (Tok.startswith("=")) {
- if (!Tok.startswith("=0x")) {
- setError("filler should be a hexadecimal value");
- return;
+ if (consume("="))
+ Cmd->Filler = readOutputSectionFiller(next());
+ else if (peek().startswith("="))
+ Cmd->Filler = readOutputSectionFiller(next().drop_front());
+
+ return Cmd;
+}
+
+// Read "=<number>" where <number> is an octal/decimal/hexadecimal number.
+// https://sourceware.org/binutils/docs/ld/Output-Section-Fill.html
+//
+// ld.gold is not fully compatible with ld.bfd. ld.bfd handles
+// hexstrings as blobs of arbitrary sizes, while ld.gold handles them
+// as 32-bit big-endian values. We will do the same as ld.gold does
+// because it's simpler than what ld.bfd does.
+uint32_t ScriptParser::readOutputSectionFiller(StringRef Tok) {
+ uint32_t V;
+ if (!Tok.getAsInteger(0, V))
+ return V;
+ setError("invalid filler expression: " + Tok);
+ return 0;
+}
+
+SymbolAssignment *ScriptParser::readProvideHidden(bool Provide, bool Hidden) {
+ expect("(");
+ SymbolAssignment *Cmd = readAssignment(next());
+ Cmd->Provide = Provide;
+ Cmd->Hidden = Hidden;
+ expect(")");
+ expect(";");
+ return Cmd;
+}
+
+SymbolAssignment *ScriptParser::readProvideOrAssignment(StringRef Tok) {
+ SymbolAssignment *Cmd = nullptr;
+ if (peek() == "=" || peek() == "+=") {
+ Cmd = readAssignment(Tok);
+ expect(";");
+ } else if (Tok == "PROVIDE") {
+ Cmd = readProvideHidden(true, false);
+ } else if (Tok == "HIDDEN") {
+ Cmd = readProvideHidden(false, true);
+ } else if (Tok == "PROVIDE_HIDDEN") {
+ Cmd = readProvideHidden(true, true);
+ }
+ return Cmd;
+}
+
+static uint64_t getSymbolValue(const Twine &Loc, StringRef S, uint64_t Dot) {
+ if (S == ".")
+ return Dot;
+ return ScriptBase->getSymbolValue(Loc, S);
+}
+
+static bool isAbsolute(StringRef S) {
+ if (S == ".")
+ return false;
+ return ScriptBase->isAbsolute(S);
+}
+
+SymbolAssignment *ScriptParser::readAssignment(StringRef Name) {
+ StringRef Op = next();
+ Expr E;
+ assert(Op == "=" || Op == "+=");
+ if (consume("ABSOLUTE")) {
+ // The RHS may be something like "ABSOLUTE(.) & 0xff".
+ // Call readExpr1 to read the whole expression.
+ E = readExpr1(readParenExpr(), 0);
+ E.IsAbsolute = [] { return true; };
+ } else {
+ E = readExpr();
+ }
+ if (Op == "+=") {
+ std::string Loc = getCurrentLocation();
+ E = [=](uint64_t Dot) {
+ return getSymbolValue(Loc, Name, Dot) + E(Dot);
+ };
+ }
+ return new SymbolAssignment(Name, E);
+}
+
+// This is an operator-precedence parser to parse a linker
+// script expression.
+Expr ScriptParser::readExpr() { return readExpr1(readPrimary(), 0); }
+
+static Expr combine(StringRef Op, Expr L, Expr R) {
+ if (Op == "*")
+ return [=](uint64_t Dot) { return L(Dot) * R(Dot); };
+ if (Op == "/") {
+ return [=](uint64_t Dot) -> uint64_t {
+ uint64_t RHS = R(Dot);
+ if (RHS == 0) {
+ error("division by zero");
+ return 0;
+ }
+ return L(Dot) / RHS;
+ };
+ }
+ if (Op == "+")
+ return {[=](uint64_t Dot) { return L(Dot) + R(Dot); },
+ [=] { return L.IsAbsolute() && R.IsAbsolute(); },
+ [=] {
+ const OutputSectionBase *S = L.Section();
+ return S ? S : R.Section();
+ }};
+ if (Op == "-")
+ return [=](uint64_t Dot) { return L(Dot) - R(Dot); };
+ if (Op == "<<")
+ return [=](uint64_t Dot) { return L(Dot) << R(Dot); };
+ if (Op == ">>")
+ return [=](uint64_t Dot) { return L(Dot) >> R(Dot); };
+ if (Op == "<")
+ return [=](uint64_t Dot) { return L(Dot) < R(Dot); };
+ if (Op == ">")
+ return [=](uint64_t Dot) { return L(Dot) > R(Dot); };
+ if (Op == ">=")
+ return [=](uint64_t Dot) { return L(Dot) >= R(Dot); };
+ if (Op == "<=")
+ return [=](uint64_t Dot) { return L(Dot) <= R(Dot); };
+ if (Op == "==")
+ return [=](uint64_t Dot) { return L(Dot) == R(Dot); };
+ if (Op == "!=")
+ return [=](uint64_t Dot) { return L(Dot) != R(Dot); };
+ if (Op == "&")
+ return [=](uint64_t Dot) { return L(Dot) & R(Dot); };
+ if (Op == "|")
+ return [=](uint64_t Dot) { return L(Dot) | R(Dot); };
+ llvm_unreachable("invalid operator");
+}
+
+// This is a part of the operator-precedence parser. This function
+// assumes that the remaining token stream starts with an operator.
+Expr ScriptParser::readExpr1(Expr Lhs, int MinPrec) {
+ while (!atEOF() && !Error) {
+ // Read an operator and an expression.
+ if (consume("?"))
+ return readTernary(Lhs);
+ StringRef Op1 = peek();
+ if (precedence(Op1) < MinPrec)
+ break;
+ skip();
+ Expr Rhs = readPrimary();
+
+ // Evaluate the remaining part of the expression first if the
+ // next operator has greater precedence than the previous one.
+ // For example, if we have read "+" and "3", and if the next
+ // operator is "*", then we'll evaluate 3 * ... part first.
+ while (!atEOF()) {
+ StringRef Op2 = peek();
+ if (precedence(Op2) <= precedence(Op1))
+ break;
+ Rhs = readExpr1(Rhs, precedence(Op2));
}
- Tok = Tok.substr(3);
- Opt.Filler[OutSec] = parseHex(Tok);
- next();
+
+ Lhs = combine(Op1, Lhs, Rhs);
}
+ return Lhs;
}
-void ScriptParser::readSymbolAssignment(StringRef Name) {
- expect("=");
- std::vector<StringRef> Expr = readSectionsCommandExpr();
- if (Expr.empty())
- error("error in symbol assignment expression");
- else
- Opt.Commands.push_back({AssignmentKind, std::move(Expr), Name});
+uint64_t static getConstant(StringRef S) {
+ if (S == "COMMONPAGESIZE")
+ return Target->PageSize;
+ if (S == "MAXPAGESIZE")
+ return Config->MaxPageSize;
+ error("unknown constant: " + S);
+ return 0;
+}
+
+// Parses Tok as an integer. Returns true if successful.
+// It recognizes hexadecimal (prefixed with "0x" or suffixed with "H")
+// and decimal numbers. Decimal numbers may have "K" (kilo) or
+// "M" (mega) prefixes.
+static bool readInteger(StringRef Tok, uint64_t &Result) {
+ // Negative number
+ if (Tok.startswith("-")) {
+ if (!readInteger(Tok.substr(1), Result))
+ return false;
+ Result = -Result;
+ return true;
+ }
+
+ // Hexadecimal
+ if (Tok.startswith_lower("0x"))
+ return !Tok.substr(2).getAsInteger(16, Result);
+ if (Tok.endswith_lower("H"))
+ return !Tok.drop_back().getAsInteger(16, Result);
+
+ // Decimal
+ int Suffix = 1;
+ if (Tok.endswith_lower("K")) {
+ Suffix = 1024;
+ Tok = Tok.drop_back();
+ } else if (Tok.endswith_lower("M")) {
+ Suffix = 1024 * 1024;
+ Tok = Tok.drop_back();
+ }
+ if (Tok.getAsInteger(10, Result))
+ return false;
+ Result *= Suffix;
+ return true;
+}
+
+BytesDataCommand *ScriptParser::readBytesDataCommand(StringRef Tok) {
+ int Size = StringSwitch<unsigned>(Tok)
+ .Case("BYTE", 1)
+ .Case("SHORT", 2)
+ .Case("LONG", 4)
+ .Case("QUAD", 8)
+ .Default(-1);
+ if (Size == -1)
+ return nullptr;
+
+ return new BytesDataCommand(readParenExpr(), Size);
+}
+
+StringRef ScriptParser::readParenLiteral() {
+ expect("(");
+ StringRef Tok = next();
+ expect(")");
+ return Tok;
+}
+
+Expr ScriptParser::readPrimary() {
+ if (peek() == "(")
+ return readParenExpr();
+
+ StringRef Tok = next();
+ std::string Location = getCurrentLocation();
+
+ if (Tok == "~") {
+ Expr E = readPrimary();
+ return [=](uint64_t Dot) { return ~E(Dot); };
+ }
+ if (Tok == "-") {
+ Expr E = readPrimary();
+ return [=](uint64_t Dot) { return -E(Dot); };
+ }
+
+ // Built-in functions are parsed here.
+ // https://sourceware.org/binutils/docs/ld/Builtin-Functions.html.
+ if (Tok == "ADDR") {
+ StringRef Name = readParenLiteral();
+ return {[=](uint64_t Dot) {
+ return ScriptBase->getOutputSection(Location, Name)->Addr;
+ },
+ [=] { return false; },
+ [=] { return ScriptBase->getOutputSection(Location, Name); }};
+ }
+ if (Tok == "LOADADDR") {
+ StringRef Name = readParenLiteral();
+ return [=](uint64_t Dot) {
+ return ScriptBase->getOutputSection(Location, Name)->getLMA();
+ };
+ }
+ if (Tok == "ASSERT")
+ return readAssert();
+ if (Tok == "ALIGN") {
+ expect("(");
+ Expr E = readExpr();
+ if (consume(",")) {
+ Expr E2 = readExpr();
+ expect(")");
+ return [=](uint64_t Dot) { return alignTo(E(Dot), E2(Dot)); };
+ }
+ expect(")");
+ return [=](uint64_t Dot) { return alignTo(Dot, E(Dot)); };
+ }
+ if (Tok == "CONSTANT") {
+ StringRef Name = readParenLiteral();
+ return [=](uint64_t Dot) { return getConstant(Name); };
+ }
+ if (Tok == "DEFINED") {
+ StringRef Name = readParenLiteral();
+ return [=](uint64_t Dot) { return ScriptBase->isDefined(Name) ? 1 : 0; };
+ }
+ if (Tok == "SEGMENT_START") {
+ expect("(");
+ skip();
+ expect(",");
+ Expr E = readExpr();
+ expect(")");
+ return [=](uint64_t Dot) { return E(Dot); };
+ }
+ if (Tok == "DATA_SEGMENT_ALIGN") {
+ expect("(");
+ Expr E = readExpr();
+ expect(",");
+ readExpr();
+ expect(")");
+ return [=](uint64_t Dot) { return alignTo(Dot, E(Dot)); };
+ }
+ if (Tok == "DATA_SEGMENT_END") {
+ expect("(");
+ expect(".");
+ expect(")");
+ return [](uint64_t Dot) { return Dot; };
+ }
+ // GNU linkers implements more complicated logic to handle
+ // DATA_SEGMENT_RELRO_END. We instead ignore the arguments and just align to
+ // the next page boundary for simplicity.
+ if (Tok == "DATA_SEGMENT_RELRO_END") {
+ expect("(");
+ readExpr();
+ expect(",");
+ readExpr();
+ expect(")");
+ return [](uint64_t Dot) { return alignTo(Dot, Target->PageSize); };
+ }
+ if (Tok == "SIZEOF") {
+ StringRef Name = readParenLiteral();
+ return [=](uint64_t Dot) { return ScriptBase->getOutputSectionSize(Name); };
+ }
+ if (Tok == "ALIGNOF") {
+ StringRef Name = readParenLiteral();
+ return [=](uint64_t Dot) {
+ return ScriptBase->getOutputSection(Location, Name)->Addralign;
+ };
+ }
+ if (Tok == "SIZEOF_HEADERS")
+ return [=](uint64_t Dot) { return ScriptBase->getHeaderSize(); };
+
+ // Tok is a literal number.
+ uint64_t V;
+ if (readInteger(Tok, V))
+ return [=](uint64_t Dot) { return V; };
+
+ // Tok is a symbol name.
+ if (Tok != "." && !isValidCIdentifier(Tok))
+ setError("malformed number: " + Tok);
+ return {[=](uint64_t Dot) { return getSymbolValue(Location, Tok, Dot); },
+ [=] { return isAbsolute(Tok); },
+ [=] { return ScriptBase->getSymbolSection(Tok); }};
}
-std::vector<StringRef> ScriptParser::readSectionsCommandExpr() {
- std::vector<StringRef> Expr;
- while (!Error) {
+Expr ScriptParser::readTernary(Expr Cond) {
+ Expr L = readExpr();
+ expect(":");
+ Expr R = readExpr();
+ return [=](uint64_t Dot) { return Cond(Dot) ? L(Dot) : R(Dot); };
+}
+
+Expr ScriptParser::readParenExpr() {
+ expect("(");
+ Expr E = readExpr();
+ expect(")");
+ return E;
+}
+
+std::vector<StringRef> ScriptParser::readOutputSectionPhdrs() {
+ std::vector<StringRef> Phdrs;
+ while (!Error && peek().startswith(":")) {
StringRef Tok = next();
- if (Tok == ";")
+ Phdrs.push_back((Tok.size() == 1) ? next() : Tok.substr(1));
+ }
+ return Phdrs;
+}
+
+// Read a program header type name. The next token must be a
+// name of a program header type or a constant (e.g. "0x3").
+unsigned ScriptParser::readPhdrType() {
+ StringRef Tok = next();
+ uint64_t Val;
+ if (readInteger(Tok, Val))
+ return Val;
+
+ unsigned Ret = StringSwitch<unsigned>(Tok)
+ .Case("PT_NULL", PT_NULL)
+ .Case("PT_LOAD", PT_LOAD)
+ .Case("PT_DYNAMIC", PT_DYNAMIC)
+ .Case("PT_INTERP", PT_INTERP)
+ .Case("PT_NOTE", PT_NOTE)
+ .Case("PT_SHLIB", PT_SHLIB)
+ .Case("PT_PHDR", PT_PHDR)
+ .Case("PT_TLS", PT_TLS)
+ .Case("PT_GNU_EH_FRAME", PT_GNU_EH_FRAME)
+ .Case("PT_GNU_STACK", PT_GNU_STACK)
+ .Case("PT_GNU_RELRO", PT_GNU_RELRO)
+ .Case("PT_OPENBSD_RANDOMIZE", PT_OPENBSD_RANDOMIZE)
+ .Case("PT_OPENBSD_WXNEEDED", PT_OPENBSD_WXNEEDED)
+ .Case("PT_OPENBSD_BOOTDATA", PT_OPENBSD_BOOTDATA)
+ .Default(-1);
+
+ if (Ret == (unsigned)-1) {
+ setError("invalid program header type: " + Tok);
+ return PT_NULL;
+ }
+ return Ret;
+}
+
+// Reads a list of symbols, e.g. "{ global: foo; bar; local: *; };".
+void ScriptParser::readAnonymousDeclaration() {
+ // Read global symbols first. "global:" is default, so if there's
+ // no label, we assume global symbols.
+ if (consume("global:") || peek() != "local:")
+ Config->VersionScriptGlobals = readSymbols();
+
+ // Next, read local symbols.
+ if (consume("local:")) {
+ if (consume("*")) {
+ Config->DefaultSymbolVersion = VER_NDX_LOCAL;
+ expect(";");
+ } else {
+ setError("local symbol list for anonymous version is not supported");
+ }
+ }
+ expect("}");
+ expect(";");
+}
+
+// Reads a list of symbols, e.g. "VerStr { global: foo; bar; local: *; };".
+void ScriptParser::readVersionDeclaration(StringRef VerStr) {
+ // Identifiers start at 2 because 0 and 1 are reserved
+ // for VER_NDX_LOCAL and VER_NDX_GLOBAL constants.
+ uint16_t VersionId = Config->VersionDefinitions.size() + 2;
+ Config->VersionDefinitions.push_back({VerStr, VersionId});
+
+ // Read global symbols.
+ if (consume("global:") || peek() != "local:")
+ Config->VersionDefinitions.back().Globals = readSymbols();
+
+ // Read local symbols.
+ if (consume("local:")) {
+ if (consume("*")) {
+ Config->DefaultSymbolVersion = VER_NDX_LOCAL;
+ expect(";");
+ } else {
+ for (SymbolVersion V : readSymbols())
+ Config->VersionScriptLocals.push_back(V);
+ }
+ }
+ expect("}");
+
+ // Each version may have a parent version. For example, "Ver2"
+ // defined as "Ver2 { global: foo; local: *; } Ver1;" has "Ver1"
+ // as a parent. This version hierarchy is, probably against your
+ // instinct, purely for hint; the runtime doesn't care about it
+ // at all. In LLD, we simply ignore it.
+ if (peek() != ";")
+ skip();
+ expect(";");
+}
+
+// Reads a list of symbols for a versions cript.
+std::vector<SymbolVersion> ScriptParser::readSymbols() {
+ std::vector<SymbolVersion> Ret;
+ for (;;) {
+ if (consume("extern")) {
+ for (SymbolVersion V : readVersionExtern())
+ Ret.push_back(V);
+ continue;
+ }
+
+ if (peek() == "}" || peek() == "local:" || Error)
break;
- Expr.push_back(Tok);
+ StringRef Tok = next();
+ Ret.push_back({unquote(Tok), false, hasWildcard(Tok)});
+ expect(";");
}
- return Expr;
+ return Ret;
}
-static bool isUnderSysroot(StringRef Path) {
- if (Config->Sysroot == "")
- return false;
- for (; !Path.empty(); Path = sys::path::parent_path(Path))
- if (sys::fs::equivalent(Config->Sysroot, Path))
- return true;
- return false;
+// Reads an "extern C++" directive, e.g.,
+// "extern "C++" { ns::*; "f(int, double)"; };"
+std::vector<SymbolVersion> ScriptParser::readVersionExtern() {
+ StringRef Tok = next();
+ bool IsCXX = Tok == "\"C++\"";
+ if (!IsCXX && Tok != "\"C\"")
+ setError("Unknown language");
+ expect("{");
+
+ std::vector<SymbolVersion> Ret;
+ while (!Error && peek() != "}") {
+ StringRef Tok = next();
+ bool HasWildcard = !Tok.startswith("\"") && hasWildcard(Tok);
+ Ret.push_back({unquote(Tok), IsCXX, HasWildcard});
+ expect(";");
+ }
+
+ expect("}");
+ expect(";");
+ return Ret;
}
-// Entry point.
void elf::readLinkerScript(MemoryBufferRef MB) {
- StringRef Path = MB.getBufferIdentifier();
- ScriptParser(MB.getBuffer(), isUnderSysroot(Path)).run();
+ ScriptParser(MB).readLinkerScript();
+}
+
+void elf::readVersionScript(MemoryBufferRef MB) {
+ ScriptParser(MB).readVersionScript();
+}
+
+void elf::readDynamicList(MemoryBufferRef MB) {
+ ScriptParser(MB).readDynamicList();
}
template class elf::LinkerScript<ELF32LE>;
diff --git a/ELF/LinkerScript.h b/ELF/LinkerScript.h
index 768f78a66468..505162f0ab43 100644
--- a/ELF/LinkerScript.h
+++ b/ELF/LinkerScript.h
@@ -10,86 +10,279 @@
#ifndef LLD_ELF_LINKER_SCRIPT_H
#define LLD_ELF_LINKER_SCRIPT_H
+#include "Config.h"
+#include "Strings.h"
+#include "Writer.h"
#include "lld/Core/LLVM.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/MapVector.h"
-#include "llvm/Support/Allocator.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/MemoryBuffer.h"
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <vector>
namespace lld {
namespace elf {
+class DefinedCommon;
+class ScriptParser;
+class SymbolBody;
+template <class ELFT> class InputSectionBase;
+template <class ELFT> class InputSection;
+class OutputSectionBase;
+template <class ELFT> class OutputSectionFactory;
+class InputSectionData;
+
+// This represents an expression in the linker script.
+// ScriptParser::readExpr reads an expression and returns an Expr.
+// Later, we evaluate the expression by calling the function
+// with the value of special context variable ".".
+struct Expr {
+ std::function<uint64_t(uint64_t)> Val;
+ std::function<bool()> IsAbsolute;
+
+ // If expression is section-relative the function below is used
+ // to get the output section pointer.
+ std::function<const OutputSectionBase *()> Section;
+
+ uint64_t operator()(uint64_t Dot) const { return Val(Dot); }
+ operator bool() const { return (bool)Val; }
+
+ Expr(std::function<uint64_t(uint64_t)> Val, std::function<bool()> IsAbsolute,
+ std::function<const OutputSectionBase *()> Section)
+ : Val(Val), IsAbsolute(IsAbsolute), Section(Section) {}
+ template <typename T>
+ Expr(T V) : Expr(V, [] { return true; }, [] { return nullptr; }) {}
+ Expr() : Expr(nullptr) {}
+};
+
// Parses a linker script. Calling this function updates
// Config and ScriptConfig.
void readLinkerScript(MemoryBufferRef MB);
-class ScriptParser;
-template <class ELFT> class InputSectionBase;
-template <class ELFT> class OutputSectionBase;
+// Parses a version script.
+void readVersionScript(MemoryBufferRef MB);
+
+void readDynamicList(MemoryBufferRef MB);
+
+// This enum is used to implement linker script SECTIONS command.
+// https://sourceware.org/binutils/docs/ld/SECTIONS.html#SECTIONS
+enum SectionsCommandKind {
+ AssignmentKind, // . = expr or <sym> = expr
+ OutputSectionKind,
+ InputSectionKind,
+ AssertKind, // ASSERT(expr)
+ BytesDataKind // BYTE(expr), SHORT(expr), LONG(expr) or QUAD(expr)
+};
+
+struct BaseCommand {
+ BaseCommand(int K) : Kind(K) {}
+
+ virtual ~BaseCommand() = default;
-// This class represents each rule in SECTIONS command.
-struct SectionRule {
- SectionRule(StringRef D, StringRef S)
- : Dest(D), SectionPattern(S) {}
+ int Kind;
+};
+
+// This represents ". = <expr>" or "<symbol> = <expr>".
+struct SymbolAssignment : BaseCommand {
+ SymbolAssignment(StringRef Name, Expr E)
+ : BaseCommand(AssignmentKind), Name(Name), Expression(E) {}
+
+ static bool classof(const BaseCommand *C);
+
+ // The LHS of an expression. Name is either a symbol name or ".".
+ StringRef Name;
+ SymbolBody *Sym = nullptr;
- StringRef Dest;
+ // The RHS of an expression.
+ Expr Expression;
- StringRef SectionPattern;
+ // Command attributes for PROVIDE, HIDDEN and PROVIDE_HIDDEN.
+ bool Provide = false;
+ bool Hidden = false;
};
-// This enum represents what we can observe in SECTIONS tag of script:
-// ExprKind is a location counter change, like ". = . + 0x1000"
-// SectionKind is a description of output section, like ".data :..."
-enum SectionsCommandKind { SectionKind, AssignmentKind };
+// Linker scripts allow additional constraints to be put on ouput sections.
+// If an output section is marked as ONLY_IF_RO, the section is created
+// only if its input sections are read-only. Likewise, an output section
+// with ONLY_IF_RW is created if all input sections are RW.
+enum class ConstraintKind { NoConstraint, ReadOnly, ReadWrite };
+
+struct OutputSectionCommand : BaseCommand {
+ OutputSectionCommand(StringRef Name)
+ : BaseCommand(OutputSectionKind), Name(Name) {}
+
+ static bool classof(const BaseCommand *C);
-struct SectionsCommand {
- SectionsCommandKind Kind;
- std::vector<StringRef> Expr;
StringRef Name;
+ Expr AddrExpr;
+ Expr AlignExpr;
+ Expr LMAExpr;
+ Expr SubalignExpr;
+ std::vector<std::unique_ptr<BaseCommand>> Commands;
+ std::vector<StringRef> Phdrs;
+ uint32_t Filler = 0;
+ ConstraintKind Constraint = ConstraintKind::NoConstraint;
+ std::string Location;
};
-// ScriptConfiguration holds linker script parse results.
-struct ScriptConfiguration {
- // SECTIONS commands.
- std::vector<SectionRule> Sections;
+// This struct represents one section match pattern in SECTIONS() command.
+// It can optionally have negative match pattern for EXCLUDED_FILE command.
+// Also it may be surrounded with SORT() command, so contains sorting rules.
+struct SectionPattern {
+ SectionPattern(StringMatcher &&Pat1, StringMatcher &&Pat2)
+ : ExcludedFilePat(Pat1), SectionPat(Pat2) {}
+
+ StringMatcher ExcludedFilePat;
+ StringMatcher SectionPat;
+ SortSectionPolicy SortOuter;
+ SortSectionPolicy SortInner;
+};
+
+struct InputSectionDescription : BaseCommand {
+ InputSectionDescription(StringRef FilePattern)
+ : BaseCommand(InputSectionKind), FilePat(FilePattern) {}
- // Section fill attribute for each section.
- llvm::StringMap<std::vector<uint8_t>> Filler;
+ static bool classof(const BaseCommand *C);
+ StringMatcher FilePat;
+
+ // Input sections that matches at least one of SectionPatterns
+ // will be associated with this InputSectionDescription.
+ std::vector<SectionPattern> SectionPatterns;
+
+ std::vector<InputSectionData *> Sections;
+};
+
+// Represents an ASSERT().
+struct AssertCommand : BaseCommand {
+ AssertCommand(Expr E) : BaseCommand(AssertKind), Expression(E) {}
+
+ static bool classof(const BaseCommand *C);
+
+ Expr Expression;
+};
+
+// Represents BYTE(), SHORT(), LONG(), or QUAD().
+struct BytesDataCommand : BaseCommand {
+ BytesDataCommand(Expr E, unsigned Size)
+ : BaseCommand(BytesDataKind), Expression(E), Size(Size) {}
+
+ static bool classof(const BaseCommand *C);
+
+ Expr Expression;
+ unsigned Offset;
+ unsigned Size;
+};
+
+struct PhdrsCommand {
+ StringRef Name;
+ unsigned Type;
+ bool HasFilehdr;
+ bool HasPhdrs;
+ unsigned Flags;
+ Expr LMAExpr;
+};
+
+class LinkerScriptBase {
+protected:
+ ~LinkerScriptBase() = default;
+
+public:
+ virtual uint64_t getHeaderSize() = 0;
+ virtual uint64_t getSymbolValue(const Twine &Loc, StringRef S) = 0;
+ virtual bool isDefined(StringRef S) = 0;
+ virtual bool isAbsolute(StringRef S) = 0;
+ virtual const OutputSectionBase *getSymbolSection(StringRef S) = 0;
+ virtual const OutputSectionBase *getOutputSection(const Twine &Loc,
+ StringRef S) = 0;
+ virtual uint64_t getOutputSectionSize(StringRef S) = 0;
+};
+
+// ScriptConfiguration holds linker script parse results.
+struct ScriptConfiguration {
// Used to assign addresses to sections.
- std::vector<SectionsCommand> Commands;
+ std::vector<std::unique_ptr<BaseCommand>> Commands;
- bool DoLayout = false;
+ // Used to assign sections to headers.
+ std::vector<PhdrsCommand> PhdrsCommands;
- llvm::BumpPtrAllocator Alloc;
+ bool HasSections = false;
// List of section patterns specified with KEEP commands. They will
// be kept even if they are unused and --gc-sections is specified.
- std::vector<StringRef> KeptSections;
+ std::vector<InputSectionDescription *> KeptSections;
};
extern ScriptConfiguration *ScriptConfig;
// This is a runner of the linker script.
-template <class ELFT> class LinkerScript {
+template <class ELFT> class LinkerScript final : public LinkerScriptBase {
typedef typename ELFT::uint uintX_t;
public:
- StringRef getOutputSection(InputSectionBase<ELFT> *S);
- ArrayRef<uint8_t> getFiller(StringRef Name);
- bool isDiscarded(InputSectionBase<ELFT> *S);
+ LinkerScript();
+ ~LinkerScript();
+
+ void processCommands(OutputSectionFactory<ELFT> &Factory);
+ void addOrphanSections(OutputSectionFactory<ELFT> &Factory);
+ void removeEmptyCommands();
+ void adjustSectionsBeforeSorting();
+ void adjustSectionsAfterSorting();
+
+ std::vector<PhdrEntry> createPhdrs();
+ bool ignoreInterpSection();
+
+ uint32_t getFiller(StringRef Name);
+ void writeDataBytes(StringRef Name, uint8_t *Buf);
+ bool hasLMA(StringRef Name);
bool shouldKeep(InputSectionBase<ELFT> *S);
- void assignAddresses(ArrayRef<OutputSectionBase<ELFT> *> S);
- int compareSections(StringRef A, StringRef B);
- void addScriptedSymbols();
+ void assignOffsets(OutputSectionCommand *Cmd);
+ void placeOrphanSections();
+ void assignAddresses(std::vector<PhdrEntry> &Phdrs);
+ bool hasPhdrsCommands();
+ uint64_t getHeaderSize() override;
+ uint64_t getSymbolValue(const Twine &Loc, StringRef S) override;
+ bool isDefined(StringRef S) override;
+ bool isAbsolute(StringRef S) override;
+ const OutputSectionBase *getSymbolSection(StringRef S) override;
+ const OutputSectionBase *getOutputSection(const Twine &Loc,
+ StringRef S) override;
+ uint64_t getOutputSectionSize(StringRef S) override;
+
+ std::vector<OutputSectionBase *> *OutputSections;
+
+ int getSectionIndex(StringRef Name);
private:
+ void computeInputSections(InputSectionDescription *);
+
+ void addSection(OutputSectionFactory<ELFT> &Factory,
+ InputSectionBase<ELFT> *Sec, StringRef Name);
+ void discard(ArrayRef<InputSectionBase<ELFT> *> V);
+
+ std::vector<InputSectionBase<ELFT> *>
+ createInputSectionList(OutputSectionCommand &Cmd);
+
// "ScriptConfig" is a bit too long, so define a short name for it.
ScriptConfiguration &Opt = *ScriptConfig;
- int getSectionIndex(StringRef Name);
+ std::vector<size_t> getPhdrIndices(StringRef SectionName);
+ size_t getPhdrIndex(const Twine &Loc, StringRef PhdrName);
uintX_t Dot;
+ uintX_t LMAOffset = 0;
+ OutputSectionBase *CurOutSec = nullptr;
+ uintX_t ThreadBssOffset = 0;
+ void switchTo(OutputSectionBase *Sec);
+ void flush();
+ void output(InputSection<ELFT> *Sec);
+ void process(BaseCommand &Base);
+ llvm::DenseSet<OutputSectionBase *> AlreadyOutputOS;
+ llvm::DenseSet<InputSectionData *> AlreadyOutputIS;
};
// Variable template is a C++14 feature, so we can't template
@@ -97,7 +290,9 @@ private:
template <class ELFT> struct Script { static LinkerScript<ELFT> *X; };
template <class ELFT> LinkerScript<ELFT> *Script<ELFT>::X;
-} // namespace elf
-} // namespace lld
+extern LinkerScriptBase *ScriptBase;
+
+} // end namespace elf
+} // end namespace lld
-#endif
+#endif // LLD_ELF_LINKER_SCRIPT_H
diff --git a/ELF/MarkLive.cpp b/ELF/MarkLive.cpp
index 41e30ce599d2..8d129fc3ff13 100644
--- a/ELF/MarkLive.cpp
+++ b/ELF/MarkLive.cpp
@@ -36,22 +36,24 @@
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
+using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
+namespace {
// A resolved relocation. The Sec and Offset fields are set if the relocation
// was resolved to an offset within a section.
-template <class ELFT>
-struct ResolvedReloc {
+template <class ELFT> struct ResolvedReloc {
InputSectionBase<ELFT> *Sec;
typename ELFT::uint Offset;
};
+} // end anonymous namespace
template <class ELFT>
static typename ELFT::uint getAddend(InputSectionBase<ELFT> &Sec,
const typename ELFT::Rel &Rel) {
- return Target->getImplicitAddend(Sec.getSectionData().begin(),
+ return Target->getImplicitAddend(Sec.Data.begin() + Rel.r_offset,
Rel.getType(Config->Mips64EL));
}
@@ -74,56 +76,103 @@ static ResolvedReloc<ELFT> resolveReloc(InputSectionBase<ELFT> &Sec,
return {D->Section->Repl, Offset};
}
-template <class ELFT, class Elf_Shdr>
-static void run(ELFFile<ELFT> &Obj, InputSectionBase<ELFT> &Sec,
- Elf_Shdr *RelSec, std::function<void(ResolvedReloc<ELFT>)> Fn) {
- if (RelSec->sh_type == SHT_RELA) {
- for (const typename ELFT::Rela &RI : Obj.relas(RelSec))
- Fn(resolveReloc(Sec, RI));
- } else {
- for (const typename ELFT::Rel &RI : Obj.rels(RelSec))
- Fn(resolveReloc(Sec, RI));
- }
-}
-
// Calls Fn for each section that Sec refers to via relocations.
template <class ELFT>
static void forEachSuccessor(InputSection<ELFT> &Sec,
std::function<void(ResolvedReloc<ELFT>)> Fn) {
- ELFFile<ELFT> &Obj = Sec.getFile()->getObj();
- for (const typename ELFT::Shdr *RelSec : Sec.RelocSections)
- run(Obj, Sec, RelSec, Fn);
+ if (Sec.AreRelocsRela) {
+ for (const typename ELFT::Rela &Rel : Sec.relas())
+ Fn(resolveReloc(Sec, Rel));
+ } else {
+ for (const typename ELFT::Rel &Rel : Sec.rels())
+ Fn(resolveReloc(Sec, Rel));
+ }
+ if (Sec.DependentSection)
+ Fn({Sec.DependentSection, 0});
+}
+
+// The .eh_frame section is an unfortunate special case.
+// The section is divided in CIEs and FDEs and the relocations it can have are
+// * CIEs can refer to a personality function.
+// * FDEs can refer to a LSDA
+// * FDEs refer to the function they contain information about
+// The last kind of relocation cannot keep the referred section alive, or they
+// would keep everything alive in a common object file. In fact, each FDE is
+// alive if the section it refers to is alive.
+// To keep things simple, in here we just ignore the last relocation kind. The
+// other two keep the referred section alive.
+//
+// A possible improvement would be to fully process .eh_frame in the middle of
+// the gc pass. With that we would be able to also gc some sections holding
+// LSDAs and personality functions if we found that they were unused.
+template <class ELFT, class RelTy>
+static void
+scanEhFrameSection(EhInputSection<ELFT> &EH, ArrayRef<RelTy> Rels,
+ std::function<void(ResolvedReloc<ELFT>)> Enqueue) {
+ const endianness E = ELFT::TargetEndianness;
+ for (unsigned I = 0, N = EH.Pieces.size(); I < N; ++I) {
+ EhSectionPiece &Piece = EH.Pieces[I];
+ unsigned FirstRelI = Piece.FirstRelocation;
+ if (FirstRelI == (unsigned)-1)
+ continue;
+ if (read32<E>(Piece.data().data() + 4) == 0) {
+ // This is a CIE, we only need to worry about the first relocation. It is
+ // known to point to the personality function.
+ Enqueue(resolveReloc(EH, Rels[FirstRelI]));
+ continue;
+ }
+ // This is a FDE. The relocations point to the described function or to
+ // a LSDA. We only need to keep the LSDA alive, so ignore anything that
+ // points to executable sections.
+ typename ELFT::uint PieceEnd = Piece.InputOff + Piece.size();
+ for (unsigned I2 = FirstRelI, N2 = Rels.size(); I2 < N2; ++I2) {
+ const RelTy &Rel = Rels[I2];
+ if (Rel.r_offset >= PieceEnd)
+ break;
+ ResolvedReloc<ELFT> R = resolveReloc(EH, Rels[I2]);
+ if (!R.Sec || R.Sec == &InputSection<ELFT>::Discarded)
+ continue;
+ if (R.Sec->Flags & SHF_EXECINSTR)
+ continue;
+ Enqueue({R.Sec, 0});
+ }
+ }
}
template <class ELFT>
-static void scanEhFrameSection(EhInputSection<ELFT> &EH,
- std::function<void(ResolvedReloc<ELFT>)> Fn) {
- if (!EH.RelocSection)
+static void
+scanEhFrameSection(EhInputSection<ELFT> &EH,
+ std::function<void(ResolvedReloc<ELFT>)> Enqueue) {
+ if (!EH.NumRelocations)
return;
- ELFFile<ELFT> &EObj = EH.getFile()->getObj();
- run<ELFT>(EObj, EH, EH.RelocSection, [&](ResolvedReloc<ELFT> R) {
- if (!R.Sec || R.Sec == &InputSection<ELFT>::Discarded)
- return;
- if (R.Sec->getSectionHdr()->sh_flags & SHF_EXECINSTR)
- return;
- Fn({R.Sec, 0});
- });
+
+ // Unfortunately we need to split .eh_frame early since some relocations in
+ // .eh_frame keep other section alive and some don't.
+ EH.split();
+
+ if (EH.AreRelocsRela)
+ scanEhFrameSection(EH, EH.relas(), Enqueue);
+ else
+ scanEhFrameSection(EH, EH.rels(), Enqueue);
}
-// Sections listed below are special because they are used by the loader
-// just by being in an ELF file. They should not be garbage-collected.
+// We do not garbage-collect two types of sections:
+// 1) Sections used by the loader (.init, .fini, .ctors, .dtors or .jcr)
+// 2) Non-allocatable sections which typically contain debugging information
template <class ELFT> static bool isReserved(InputSectionBase<ELFT> *Sec) {
- switch (Sec->getSectionHdr()->sh_type) {
+ switch (Sec->Type) {
case SHT_FINI_ARRAY:
case SHT_INIT_ARRAY:
case SHT_NOTE:
case SHT_PREINIT_ARRAY:
return true;
default:
- StringRef S = Sec->getSectionName();
+ if (!(Sec->Flags & SHF_ALLOC))
+ return true;
// We do not want to reclaim sections if they can be referred
// by __start_* and __stop_* symbols.
+ StringRef S = Sec->Name;
if (isValidCIdentifier(S))
return true;
@@ -140,7 +189,15 @@ template <class ELFT> void elf::markLive() {
SmallVector<InputSection<ELFT> *, 256> Q;
auto Enqueue = [&](ResolvedReloc<ELFT> R) {
- if (!R.Sec)
+ // Skip over discarded sections. This in theory shouldn't happen, because
+ // the ELF spec doesn't allow a relocation to point to a deduplicated
+ // COMDAT section directly. Unfortunately this happens in practice (e.g.
+ // .eh_frame) so we need to add a check.
+ if (!R.Sec || R.Sec == &InputSection<ELFT>::Discarded)
+ return;
+
+ // We don't gc non alloc sections.
+ if (!(R.Sec->Flags & SHF_ALLOC))
return;
// Usually, a whole section is marked as live or dead, but in mergeable
@@ -152,6 +209,7 @@ template <class ELFT> void elf::markLive() {
if (R.Sec->Live)
return;
R.Sec->Live = true;
+ // Add input section to the queue.
if (InputSection<ELFT> *S = dyn_cast<InputSection<ELFT>>(R.Sec))
Q.push_back(S);
};
@@ -162,8 +220,7 @@ template <class ELFT> void elf::markLive() {
};
// Add GC root symbols.
- if (Config->EntrySym)
- MarkSymbol(Config->EntrySym->body());
+ MarkSymbol(Symtab<ELFT>::X->find(Config->Entry));
MarkSymbol(Symtab<ELFT>::X->find(Config->Init));
MarkSymbol(Symtab<ELFT>::X->find(Config->Fini));
for (StringRef S : Config->Undefined)
@@ -177,18 +234,15 @@ template <class ELFT> void elf::markLive() {
// Preserve special sections and those which are specified in linker
// script KEEP command.
- for (const std::unique_ptr<ObjectFile<ELFT>> &F :
- Symtab<ELFT>::X->getObjectFiles())
- for (InputSectionBase<ELFT> *Sec : F->getSections())
- if (Sec && Sec != &InputSection<ELFT>::Discarded) {
- // .eh_frame is always marked as live now, but also it can reference to
- // sections that contain personality. We preserve all non-text sections
- // referred by .eh_frame here.
- if (auto *EH = dyn_cast_or_null<EhInputSection<ELFT>>(Sec))
- scanEhFrameSection<ELFT>(*EH, Enqueue);
- if (isReserved(Sec) || Script<ELFT>::X->shouldKeep(Sec))
- Enqueue({Sec, 0});
- }
+ for (InputSectionBase<ELFT> *Sec : Symtab<ELFT>::X->Sections) {
+ // .eh_frame is always marked as live now, but also it can reference to
+ // sections that contain personality. We preserve all non-text sections
+ // referred by .eh_frame here.
+ if (auto *EH = dyn_cast_or_null<EhInputSection<ELFT>>(Sec))
+ scanEhFrameSection<ELFT>(*EH, Enqueue);
+ if (isReserved(Sec) || Script<ELFT>::X->shouldKeep(Sec))
+ Enqueue({Sec, 0});
+ }
// Mark all reachable sections.
while (!Q.empty())
diff --git a/ELF/Memory.h b/ELF/Memory.h
new file mode 100644
index 000000000000..e5a04ed1e5a8
--- /dev/null
+++ b/ELF/Memory.h
@@ -0,0 +1,67 @@
+//===- Memory.h -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines arena allocators.
+//
+// Almost all large objects, such as files, sections or symbols, are
+// used for the entire lifetime of the linker once they are created.
+// This usage characteristic makes arena allocator an attractive choice
+// where the entire linker is one arena. With an arena, newly created
+// objects belong to the arena and freed all at once when everything is done.
+// Arena allocators are efficient and easy to understand.
+// Most objects are allocated using the arena allocators defined by this file.
+//
+// If you edit this file, please edit COFF/Memory.h too.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_MEMORY_H
+#define LLD_ELF_MEMORY_H
+
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/StringSaver.h"
+#include <vector>
+
+namespace lld {
+namespace elf {
+
+// Use this arena if your object doesn't have a destructor.
+extern llvm::BumpPtrAllocator BAlloc;
+extern llvm::StringSaver Saver;
+
+// These two classes are hack to keep track of all
+// SpecificBumpPtrAllocator instances.
+struct SpecificAllocBase {
+ SpecificAllocBase() { Instances.push_back(this); }
+ virtual ~SpecificAllocBase() = default;
+ virtual void reset() = 0;
+ static std::vector<SpecificAllocBase *> Instances;
+};
+
+template <class T> struct SpecificAlloc : public SpecificAllocBase {
+ void reset() override { Alloc.DestroyAll(); }
+ llvm::SpecificBumpPtrAllocator<T> Alloc;
+};
+
+// Use this arena if your object has a destructor.
+// Your destructor will be invoked from freeArena().
+template <typename T, typename... U> T *make(U &&... Args) {
+ static SpecificAlloc<T> Alloc;
+ return new (Alloc.Alloc.Allocate()) T(std::forward<U>(Args)...);
+}
+
+inline void freeArena() {
+ for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances)
+ Alloc->reset();
+ BAlloc.Reset();
+}
+}
+}
+
+#endif
diff --git a/ELF/Mips.cpp b/ELF/Mips.cpp
new file mode 100644
index 000000000000..ac65672b70fc
--- /dev/null
+++ b/ELF/Mips.cpp
@@ -0,0 +1,369 @@
+//===- Mips.cpp ----------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+//
+// This file contains a helper function for the Writer.
+//
+//===---------------------------------------------------------------------===//
+
+#include "Error.h"
+#include "InputFiles.h"
+#include "SymbolTable.h"
+#include "Writer.h"
+
+#include "llvm/Object/ELF.h"
+#include "llvm/Support/ELF.h"
+#include "llvm/Support/MipsABIFlags.h"
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::ELF;
+
+using namespace lld;
+using namespace lld::elf;
+
+namespace {
+struct ArchTreeEdge {
+ uint32_t Child;
+ uint32_t Parent;
+};
+
+struct FileFlags {
+ StringRef Filename;
+ uint32_t Flags;
+};
+}
+
+static StringRef getAbiName(uint32_t Flags) {
+ switch (Flags) {
+ case 0:
+ return "n64";
+ case EF_MIPS_ABI2:
+ return "n32";
+ case EF_MIPS_ABI_O32:
+ return "o32";
+ case EF_MIPS_ABI_O64:
+ return "o64";
+ case EF_MIPS_ABI_EABI32:
+ return "eabi32";
+ case EF_MIPS_ABI_EABI64:
+ return "eabi64";
+ default:
+ return "unknown";
+ }
+}
+
+static StringRef getNanName(bool IsNan2008) {
+ return IsNan2008 ? "2008" : "legacy";
+}
+
+static StringRef getFpName(bool IsFp64) { return IsFp64 ? "64" : "32"; }
+
+static void checkFlags(ArrayRef<FileFlags> Files) {
+ uint32_t ABI = Files[0].Flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
+ bool Nan = Files[0].Flags & EF_MIPS_NAN2008;
+ bool Fp = Files[0].Flags & EF_MIPS_FP64;
+
+ for (const FileFlags &F : Files.slice(1)) {
+ uint32_t ABI2 = F.Flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
+ if (ABI != ABI2)
+ error("target ABI '" + getAbiName(ABI) + "' is incompatible with '" +
+ getAbiName(ABI2) + "': " + F.Filename);
+
+ bool Nan2 = F.Flags & EF_MIPS_NAN2008;
+ if (Nan != Nan2)
+ error("target -mnan=" + getNanName(Nan) + " is incompatible with -mnan=" +
+ getNanName(Nan2) + ": " + F.Filename);
+
+ bool Fp2 = F.Flags & EF_MIPS_FP64;
+ if (Fp != Fp2)
+ error("target -mfp" + getFpName(Fp) + " is incompatible with -mfp" +
+ getFpName(Fp2) + ": " + F.Filename);
+ }
+}
+
+static uint32_t getMiscFlags(ArrayRef<FileFlags> Files) {
+ uint32_t Ret = 0;
+ for (const FileFlags &F : Files)
+ Ret |= F.Flags &
+ (EF_MIPS_ABI | EF_MIPS_ABI2 | EF_MIPS_ARCH_ASE | EF_MIPS_NOREORDER |
+ EF_MIPS_MICROMIPS | EF_MIPS_NAN2008 | EF_MIPS_32BITMODE);
+ return Ret;
+}
+
+static uint32_t getPicFlags(ArrayRef<FileFlags> Files) {
+ // Check PIC/non-PIC compatibility.
+ bool IsPic = Files[0].Flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
+ for (const FileFlags &F : Files.slice(1)) {
+ bool IsPic2 = F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
+ if (IsPic && !IsPic2)
+ warn("linking abicalls code with non-abicalls file: " + F.Filename);
+ if (!IsPic && IsPic2)
+ warn("linking non-abicalls code with abicalls file: " + F.Filename);
+ }
+
+ // Compute the result PIC/non-PIC flag.
+ uint32_t Ret = Files[0].Flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
+ for (const FileFlags &F : Files.slice(1))
+ Ret &= F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
+
+ // PIC code is inherently CPIC and may not set CPIC flag explicitly.
+ if (Ret & EF_MIPS_PIC)
+ Ret |= EF_MIPS_CPIC;
+ return Ret;
+}
+
+static ArchTreeEdge ArchTree[] = {
+ // MIPS32R6 and MIPS64R6 are not compatible with other extensions
+ // MIPS64R2 extensions.
+ {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, EF_MIPS_ARCH_64R2},
+ {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON2, EF_MIPS_ARCH_64R2},
+ {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, EF_MIPS_ARCH_64R2},
+ {EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_LS3A, EF_MIPS_ARCH_64R2},
+ // MIPS64 extensions.
+ {EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, EF_MIPS_ARCH_64},
+ {EF_MIPS_ARCH_64 | EF_MIPS_MACH_XLR, EF_MIPS_ARCH_64},
+ {EF_MIPS_ARCH_64R2, EF_MIPS_ARCH_64},
+ // MIPS V extensions.
+ {EF_MIPS_ARCH_64, EF_MIPS_ARCH_5},
+ // R5000 extensions.
+ {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5500, EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400},
+ // MIPS IV extensions.
+ {EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400, EF_MIPS_ARCH_4},
+ {EF_MIPS_ARCH_4 | EF_MIPS_MACH_9000, EF_MIPS_ARCH_4},
+ {EF_MIPS_ARCH_5, EF_MIPS_ARCH_4},
+ // VR4100 extensions.
+ {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4111, EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100},
+ {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4120, EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100},
+ // MIPS III extensions.
+ {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4010, EF_MIPS_ARCH_3},
+ {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100, EF_MIPS_ARCH_3},
+ {EF_MIPS_ARCH_3 | EF_MIPS_MACH_4650, EF_MIPS_ARCH_3},
+ {EF_MIPS_ARCH_3 | EF_MIPS_MACH_5900, EF_MIPS_ARCH_3},
+ {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2E, EF_MIPS_ARCH_3},
+ {EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2F, EF_MIPS_ARCH_3},
+ {EF_MIPS_ARCH_4, EF_MIPS_ARCH_3},
+ // MIPS32 extensions.
+ {EF_MIPS_ARCH_32R2, EF_MIPS_ARCH_32},
+ // MIPS II extensions.
+ {EF_MIPS_ARCH_3, EF_MIPS_ARCH_2},
+ {EF_MIPS_ARCH_32, EF_MIPS_ARCH_2},
+ // MIPS I extensions.
+ {EF_MIPS_ARCH_1 | EF_MIPS_MACH_3900, EF_MIPS_ARCH_1},
+ {EF_MIPS_ARCH_2, EF_MIPS_ARCH_1},
+};
+
+static bool isArchMatched(uint32_t New, uint32_t Res) {
+ if (New == Res)
+ return true;
+ if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, Res))
+ return true;
+ if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, Res))
+ return true;
+ for (const auto &Edge : ArchTree) {
+ if (Res == Edge.Child) {
+ Res = Edge.Parent;
+ if (Res == New)
+ return true;
+ }
+ }
+ return false;
+}
+
+static StringRef getMachName(uint32_t Flags) {
+ switch (Flags & EF_MIPS_MACH) {
+ case EF_MIPS_MACH_NONE:
+ return "";
+ case EF_MIPS_MACH_3900:
+ return "r3900";
+ case EF_MIPS_MACH_4010:
+ return "r4010";
+ case EF_MIPS_MACH_4100:
+ return "r4100";
+ case EF_MIPS_MACH_4650:
+ return "r4650";
+ case EF_MIPS_MACH_4120:
+ return "r4120";
+ case EF_MIPS_MACH_4111:
+ return "r4111";
+ case EF_MIPS_MACH_5400:
+ return "vr5400";
+ case EF_MIPS_MACH_5900:
+ return "vr5900";
+ case EF_MIPS_MACH_5500:
+ return "vr5500";
+ case EF_MIPS_MACH_9000:
+ return "rm9000";
+ case EF_MIPS_MACH_LS2E:
+ return "loongson2e";
+ case EF_MIPS_MACH_LS2F:
+ return "loongson2f";
+ case EF_MIPS_MACH_LS3A:
+ return "loongson3a";
+ case EF_MIPS_MACH_OCTEON:
+ return "octeon";
+ case EF_MIPS_MACH_OCTEON2:
+ return "octeon2";
+ case EF_MIPS_MACH_OCTEON3:
+ return "octeon3";
+ case EF_MIPS_MACH_SB1:
+ return "sb1";
+ case EF_MIPS_MACH_XLR:
+ return "xlr";
+ default:
+ return "unknown machine";
+ }
+}
+
+static StringRef getArchName(uint32_t Flags) {
+ StringRef S = getMachName(Flags);
+ if (!S.empty())
+ return S;
+
+ switch (Flags & EF_MIPS_ARCH) {
+ case EF_MIPS_ARCH_1:
+ return "mips1";
+ case EF_MIPS_ARCH_2:
+ return "mips2";
+ case EF_MIPS_ARCH_3:
+ return "mips3";
+ case EF_MIPS_ARCH_4:
+ return "mips4";
+ case EF_MIPS_ARCH_5:
+ return "mips5";
+ case EF_MIPS_ARCH_32:
+ return "mips32";
+ case EF_MIPS_ARCH_64:
+ return "mips64";
+ case EF_MIPS_ARCH_32R2:
+ return "mips32r2";
+ case EF_MIPS_ARCH_64R2:
+ return "mips64r2";
+ case EF_MIPS_ARCH_32R6:
+ return "mips32r6";
+ case EF_MIPS_ARCH_64R6:
+ return "mips64r6";
+ default:
+ return "unknown arch";
+ }
+}
+
+// There are (arguably too) many MIPS ISAs out there. Their relationships
+// can be represented as a forest. If all input files have ISAs which
+// reachable by repeated proceeding from the single child to the parent,
+// these input files are compatible. In that case we need to return "highest"
+// ISA. If there are incompatible input files, we show an error.
+// For example, mips1 is a "parent" of mips2 and such files are compatible.
+// Output file gets EF_MIPS_ARCH_2 flag. From the other side mips3 and mips32
+// are incompatible because nor mips3 is a parent for misp32, nor mips32
+// is a parent for mips3.
+static uint32_t getArchFlags(ArrayRef<FileFlags> Files) {
+ uint32_t Ret = Files[0].Flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
+
+ for (const FileFlags &F : Files.slice(1)) {
+ uint32_t New = F.Flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
+
+ // Check ISA compatibility.
+ if (isArchMatched(New, Ret))
+ continue;
+ if (!isArchMatched(Ret, New)) {
+ error("target ISA '" + getArchName(Ret) + "' is incompatible with '" +
+ getArchName(New) + "': " + F.Filename);
+ return 0;
+ }
+ Ret = New;
+ }
+ return Ret;
+}
+
+template <class ELFT> uint32_t elf::getMipsEFlags() {
+ std::vector<FileFlags> V;
+ for (elf::ObjectFile<ELFT> *F : Symtab<ELFT>::X->getObjectFiles())
+ V.push_back({F->getName(), F->getObj().getHeader()->e_flags});
+ if (V.empty())
+ return 0;
+ checkFlags(V);
+ return getMiscFlags(V) | getPicFlags(V) | getArchFlags(V);
+}
+
+static int compareMipsFpAbi(uint8_t FpA, uint8_t FpB) {
+ if (FpA == FpB)
+ return 0;
+ if (FpB == Mips::Val_GNU_MIPS_ABI_FP_ANY)
+ return 1;
+ if (FpB == Mips::Val_GNU_MIPS_ABI_FP_64A &&
+ FpA == Mips::Val_GNU_MIPS_ABI_FP_64)
+ return 1;
+ if (FpB != Mips::Val_GNU_MIPS_ABI_FP_XX)
+ return -1;
+ if (FpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE ||
+ FpA == Mips::Val_GNU_MIPS_ABI_FP_64 ||
+ FpA == Mips::Val_GNU_MIPS_ABI_FP_64A)
+ return 1;
+ return -1;
+}
+
+static StringRef getMipsFpAbiName(uint8_t FpAbi) {
+ switch (FpAbi) {
+ case Mips::Val_GNU_MIPS_ABI_FP_ANY:
+ return "any";
+ case Mips::Val_GNU_MIPS_ABI_FP_DOUBLE:
+ return "-mdouble-float";
+ case Mips::Val_GNU_MIPS_ABI_FP_SINGLE:
+ return "-msingle-float";
+ case Mips::Val_GNU_MIPS_ABI_FP_SOFT:
+ return "-msoft-float";
+ case Mips::Val_GNU_MIPS_ABI_FP_OLD_64:
+ return "-mips32r2 -mfp64 (old)";
+ case Mips::Val_GNU_MIPS_ABI_FP_XX:
+ return "-mfpxx";
+ case Mips::Val_GNU_MIPS_ABI_FP_64:
+ return "-mgp32 -mfp64";
+ case Mips::Val_GNU_MIPS_ABI_FP_64A:
+ return "-mgp32 -mfp64 -mno-odd-spreg";
+ default:
+ return "unknown";
+ }
+}
+
+uint8_t elf::getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
+ StringRef FileName) {
+ if (compareMipsFpAbi(NewFlag, OldFlag) >= 0)
+ return NewFlag;
+ if (compareMipsFpAbi(OldFlag, NewFlag) < 0)
+ error("target floating point ABI '" + getMipsFpAbiName(OldFlag) +
+ "' is incompatible with '" + getMipsFpAbiName(NewFlag) + "': " +
+ FileName);
+ return OldFlag;
+}
+
+template <class ELFT> static bool isN32Abi(const InputFile *F) {
+ if (auto *EF = dyn_cast<ELFFileBase<ELFT>>(F))
+ return EF->getObj().getHeader()->e_flags & EF_MIPS_ABI2;
+ return false;
+}
+
+bool elf::isMipsN32Abi(const InputFile *F) {
+ switch (Config->EKind) {
+ case ELF32LEKind:
+ return isN32Abi<ELF32LE>(F);
+ case ELF32BEKind:
+ return isN32Abi<ELF32BE>(F);
+ case ELF64LEKind:
+ return isN32Abi<ELF64LE>(F);
+ case ELF64BEKind:
+ return isN32Abi<ELF64BE>(F);
+ default:
+ llvm_unreachable("unknown Config->EKind");
+ }
+}
+
+template uint32_t elf::getMipsEFlags<ELF32LE>();
+template uint32_t elf::getMipsEFlags<ELF32BE>();
+template uint32_t elf::getMipsEFlags<ELF64LE>();
+template uint32_t elf::getMipsEFlags<ELF64BE>();
diff --git a/ELF/Options.td b/ELF/Options.td
index 010f37687f03..d436f056d013 100644
--- a/ELF/Options.td
+++ b/ELF/Options.td
@@ -7,6 +7,8 @@ class J<string name>: Joined<["--", "-"], name>;
class S<string name>: Separate<["--", "-"], name>;
class JS<string name>: JoinedOrSeparate<["--", "-"], name>;
+def auxiliary: S<"auxiliary">, HelpText<"Set DT_AUXILIARY field to the specified name">;
+
def Bsymbolic: F<"Bsymbolic">, HelpText<"Bind defined symbols locally">;
def Bsymbolic_functions: F<"Bsymbolic-functions">,
@@ -25,12 +27,24 @@ def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
def O: Joined<["-"], "O">, HelpText<"Optimize output file size">;
+def Tbss: S<"Tbss">, HelpText<"Same as --section-start with .bss as the sectionname">;
+
+def Tdata: S<"Tdata">, HelpText<"Same as --section-start with .data as the sectionname">;
+
+def Ttext: S<"Ttext">, HelpText<"Same as --section-start with .text as the sectionname">;
+
def allow_multiple_definition: F<"allow-multiple-definition">,
HelpText<"Allow multiple definitions">;
def as_needed: F<"as-needed">,
HelpText<"Only set DT_NEEDED for shared libraries if used">;
+def color_diagnostics: F<"color-diagnostics">,
+ HelpText<"Use colors in diagnostics">;
+
+def color_diagnostics_eq: J<"color-diagnostics=">,
+ HelpText<"Use colors in diagnostics">;
+
def disable_new_dtags: F<"disable-new-dtags">,
HelpText<"Disable new dynamic tags">;
@@ -60,6 +74,9 @@ def end_lib: F<"end-lib">,
def entry: S<"entry">, MetaVarName<"<entry>">,
HelpText<"Name of entry point symbol">;
+def error_limit: S<"error-limit">,
+ HelpText<"Maximum number of errors to emit before stopping (0 = no limit)">;
+
def export_dynamic: F<"export-dynamic">,
HelpText<"Put symbols in the dynamic symbol table">;
@@ -72,6 +89,18 @@ def fatal_warnings: F<"fatal-warnings">,
def fini: S<"fini">, MetaVarName<"<symbol>">,
HelpText<"Specify a finalizer function">;
+def full_shutdown : F<"full-shutdown">,
+ HelpText<"Perform a full shutdown instead of calling _exit">;
+
+def format: J<"format=">, MetaVarName<"<input-format>">,
+ HelpText<"Change the input format of the inputs following this option">;
+
+def gc_sections: F<"gc-sections">,
+ HelpText<"Enable garbage collection of unused sections">;
+
+def gdb_index: F<"gdb-index">,
+ HelpText<"Generate .gdb_index section">;
+
def hash_style: S<"hash-style">,
HelpText<"Specify hash style (sysv, gnu or both)">;
@@ -81,9 +110,6 @@ def icf: F<"icf=all">, HelpText<"Enable identical code folding">;
def image_base : J<"image-base=">, HelpText<"Set the base address">;
-def gc_sections: F<"gc-sections">,
- HelpText<"Enable garbage collection of unused sections">;
-
def init: S<"init">, MetaVarName<"<symbol>">,
HelpText<"Specify an initializer function">;
@@ -95,21 +121,37 @@ def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">,
def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
+def nostdlib: F<"nostdlib">,
+ HelpText<"Only search directories specified on the command line">;
+
def no_as_needed: F<"no-as-needed">,
HelpText<"Always DT_NEEDED for shared libraries">;
+def no_color_diagnostics: F<"no-color-diagnostics">,
+ HelpText<"Do not use colors in diagnostics">;
+
def no_demangle: F<"no-demangle">,
HelpText<"Do not demangle symbol names">;
+def no_gc_sections: F<"no-gc-sections">,
+ HelpText<"Disable garbage collection of unused sections">;
+
def no_gnu_unique: F<"no-gnu-unique">,
HelpText<"Disable STB_GNU_UNIQUE symbol binding">;
+def no_threads: F<"no-threads">,
+ HelpText<"Do not run the linker multi-threaded">;
+
def no_whole_archive: F<"no-whole-archive">,
HelpText<"Restores the default behavior of loading archive members">;
def noinhibit_exec: F<"noinhibit-exec">,
HelpText<"Retain the executable output file whenever it is still usable">;
+def nopie: F<"nopie">, HelpText<"Do not create a position independent executable">;
+
+def no_rosegment: F<"no-rosegment">, HelpText<"Do not put read-only non-executable sections in their own segment">;
+
def no_undefined: F<"no-undefined">,
HelpText<"Report unresolved symbols even if the linker is creating a shared library">;
@@ -119,6 +161,12 @@ def no_undefined_version: F<"no-undefined-version">,
def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">,
HelpText<"Path to file to write output">;
+def oformat: Separate<["--"], "oformat">, MetaVarName<"<format>">,
+ HelpText<"Specify the binary format for the output object file">;
+
+def omagic: F<"omagic">, MetaVarName<"<magic>">,
+ HelpText<"Set the text and data sections to be readable and writable">;
+
def pie: F<"pie">, HelpText<"Create a position independent executable">;
def print_gc_sections: F<"print-gc-sections">,
@@ -131,12 +179,20 @@ def rpath: S<"rpath">, HelpText<"Add a DT_RUNPATH to the output">;
def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">;
+def retain_symbols_file: J<"retain-symbols-file=">, MetaVarName<"<file>">,
+ HelpText<"Retain only the symbols listed in the file">;
+
def script: S<"script">, HelpText<"Read linker script">;
+def section_start: S<"section-start">, MetaVarName<"<address>">,
+ HelpText<"Set address of section">;
+
def shared: F<"shared">, HelpText<"Build a shared object">;
def soname: J<"soname=">, HelpText<"Set DT_SONAME">;
+def sort_section: S<"sort-section">, HelpText<"Specifies sections sorting rule when linkerscript is used">;
+
def start_lib: F<"start-lib">,
HelpText<"Start a grouping of objects that should be treated as if they were together in an archive">;
@@ -144,15 +200,24 @@ def strip_all: F<"strip-all">, HelpText<"Strip all symbols">;
def strip_debug: F<"strip-debug">, HelpText<"Strip debugging information">;
+def symbol_ordering_file: S<"symbol-ordering-file">,
+ HelpText<"Layout sections in the order specified by symbol file">;
+
def sysroot: J<"sysroot=">, HelpText<"Set the system root">;
-def threads: F<"threads">, HelpText<"Enable use of threads">;
+def target1_rel: F<"target1-rel">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_REL32">;
+
+def target1_abs: F<"target1-abs">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_ABS32">;
+
+def target2: J<"target2=">, MetaVarName<"<type>">, HelpText<"Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel">;
+
+def threads: F<"threads">, HelpText<"Run the linker multi-threaded">;
def trace: F<"trace">, HelpText<"Print the names of the input files">;
def trace_symbol : J<"trace-symbol=">, HelpText<"Trace references to symbols">;
-def undefined: J<"undefined=">,
+def undefined: S<"undefined">,
HelpText<"Force undefined symbol during linking">;
def unresolved_symbols: J<"unresolved-symbols=">,
@@ -161,9 +226,11 @@ def unresolved_symbols: J<"unresolved-symbols=">,
def rsp_quoting: J<"rsp-quoting=">,
HelpText<"Quoting style for response files. Values supported: windows|posix">;
+def v: Flag<["-"], "v">, HelpText<"Display the version number">;
+
def verbose: F<"verbose">, HelpText<"Verbose mode">;
-def version: F<"version">, HelpText<"Display the version number">;
+def version: F<"version">, HelpText<"Display the version number and exit">;
def version_script: S<"version-script">,
HelpText<"Read a version script">;
@@ -181,6 +248,7 @@ def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
HelpText<"Linker option extensions">;
// Aliases
+def alias_auxiliary: Separate<["-"], "f">, Alias<auxiliary>;
def alias_Bdynamic_call_shared: F<"call_shared">, Alias<Bdynamic>;
def alias_Bdynamic_dy: F<"dy">, Alias<Bdynamic>;
def alias_Bstatic_dn: F<"dn">, Alias<Bstatic>;
@@ -192,29 +260,41 @@ def alias_discard_locals_X: Flag<["-"], "X">, Alias<discard_locals>;
def alias_dynamic_list: J<"dynamic-list=">, Alias<dynamic_list>;
def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias<entry>;
def alias_entry_entry: J<"entry=">, Alias<entry>;
+def alias_error_limit: J<"error-limit=">, Alias<error_limit>;
def alias_export_dynamic_E: Flag<["-"], "E">, Alias<export_dynamic>;
def alias_export_dynamic_symbol: J<"export-dynamic-symbol=">,
Alias<export_dynamic_symbol>;
def alias_fini_fini: J<"fini=">, Alias<fini>;
+def alias_format_b: S<"b">, Alias<format>;
def alias_hash_style_hash_style: J<"hash-style=">, Alias<hash_style>;
def alias_init_init: J<"init=">, Alias<init>;
def alias_l__library: J<"library=">, Alias<l>;
+def alias_omagic: Flag<["-"], "N">, Alias<omagic>;
def alias_o_output: Joined<["--"], "output=">, Alias<o>;
+def alias_o_output2 : Separate<["--"], "output">, Alias<o>;
def alias_pie_pic_executable: F<"pic-executable">, Alias<pie>;
def alias_relocatable_r: Flag<["-"], "r">, Alias<relocatable>;
-def alias_rpath_R: Joined<["-"], "R">, Alias<rpath>;
+def alias_retain_symbols_file: S<"retain-symbols-file">, Alias<retain_symbols_file>;
+def alias_rpath_R: JoinedOrSeparate<["-"], "R">, Alias<rpath>;
def alias_rpath_rpath: J<"rpath=">, Alias<rpath>;
def alias_script_T: JoinedOrSeparate<["-"], "T">, Alias<script>;
def alias_shared_Bshareable: F<"Bshareable">, Alias<shared>;
def alias_soname_h: JoinedOrSeparate<["-"], "h">, Alias<soname>;
def alias_soname_soname: S<"soname">, Alias<soname>;
+def alias_sort_section: J<"sort-section=">, Alias<sort_section>;
+def alias_script: J<"script=">, Alias<script>;
def alias_strip_all: Flag<["-"], "s">, Alias<strip_all>;
def alias_strip_debug_S: Flag<["-"], "S">, Alias<strip_debug>;
+def alias_Tbss: J<"Tbss=">, Alias<Tbss>;
+def alias_Tdata: J<"Tdata=">, Alias<Tdata>;
def alias_trace: Flag<["-"], "t">, Alias<trace>;
def alias_trace_symbol_y : JoinedOrSeparate<["-"], "y">, Alias<trace_symbol>;
+def alias_Ttext: J<"Ttext=">, Alias<Ttext>;
+def alias_Ttext_segment: S<"Ttext-segment">, Alias<Ttext>;
+def alias_Ttext_segment_eq: J<"Ttext-segment=">, Alias<Ttext>;
+def alias_undefined_eq: J<"undefined=">, Alias<undefined>;
def alias_undefined_u: JoinedOrSeparate<["-"], "u">, Alias<undefined>;
def alias_version_V: Flag<["-"], "V">, Alias<version>;
-def alias_version_v: Flag<["-"], "v">, Alias<version>;
def alias_wrap_wrap: J<"wrap=">, Alias<wrap>;
// Our symbol resolution algorithm handles symbols in archive files differently
@@ -239,9 +319,13 @@ def plugin_opt_eq: J<"plugin-opt=">;
// Options listed below are silently ignored for now for compatibility.
def allow_shlib_undefined: F<"allow-shlib-undefined">;
+def cref: Flag<["--"], "cref">;
def define_common: F<"define-common">;
def demangle: F<"demangle">;
def detect_odr_violations: F<"detect-odr-violations">;
+def g: Flag<["-"], "g">;
+def M: Flag<["-"], "M">;
+def Map: JS<"Map">;
def no_add_needed: F<"no-add-needed">;
def no_allow_shlib_undefined: F<"no-allow-shlib-undefined">;
def no_copy_dt_needed_entries: F<"no-copy-dt-needed-entries">,
@@ -254,23 +338,30 @@ def no_warn_mismatch: F<"no-warn-mismatch">;
def rpath_link: S<"rpath-link">;
def rpath_link_eq: J<"rpath-link=">;
def sort_common: F<"sort-common">;
+def stats: F<"stats">;
def warn_execstack: F<"warn-execstack">;
def warn_shared_textrel: F<"warn-shared-textrel">;
-def G: Separate<["-"], "G">;
+def EB : F<"EB">;
+def EL : F<"EL">;
+def G: JoinedOrSeparate<["-"], "G">;
+def Qy : F<"Qy">;
// Aliases for ignored options
def alias_define_common_d: Flag<["-"], "d">, Alias<define_common>;
def alias_define_common_dc: F<"dc">, Alias<define_common>;
def alias_define_common_dp: F<"dp">, Alias<define_common>;
+def alias_Map_eq: J<"Map=">, Alias<Map>;
def alias_version_script_version_script: J<"version-script=">,
Alias<version_script>;
// LTO-related options.
-def lto_jobs: J<"lto-jobs=">, HelpText<"Number of threads to run codegen">;
def lto_aa_pipeline: J<"lto-aa-pipeline=">,
HelpText<"AA pipeline to run during LTO. Used in conjunction with -lto-newpm-passes">;
def lto_newpm_passes: J<"lto-newpm-passes=">,
HelpText<"Passes to run during LTO">;
+def lto_partitions: J<"lto-partitions=">,
+ HelpText<"Number of LTO codegen partitions">;
def disable_verify: F<"disable-verify">;
def mllvm: S<"mllvm">;
def save_temps: F<"save-temps">;
+def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
diff --git a/ELF/OutputSections.cpp b/ELF/OutputSections.cpp
index 50b94015f229..bf7f9c29a29a 100644
--- a/ELF/OutputSections.cpp
+++ b/ELF/OutputSections.cpp
@@ -11,15 +11,16 @@
#include "Config.h"
#include "EhFrame.h"
#include "LinkerScript.h"
+#include "Memory.h"
#include "Strings.h"
#include "SymbolTable.h"
+#include "SyntheticSections.h"
#include "Target.h"
-#include "lld/Core/Parallel.h"
+#include "Threads.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Support/MD5.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/SHA1.h"
-#include <map>
using namespace llvm;
using namespace llvm::dwarf;
@@ -30,855 +31,152 @@ using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
-template <class ELFT>
-OutputSectionBase<ELFT>::OutputSectionBase(StringRef Name, uint32_t Type,
- uintX_t Flags)
+OutputSectionBase::OutputSectionBase(StringRef Name, uint32_t Type,
+ uint64_t Flags)
: Name(Name) {
- memset(&Header, 0, sizeof(Elf_Shdr));
- Header.sh_type = Type;
- Header.sh_flags = Flags;
- Header.sh_addralign = 1;
+ this->Type = Type;
+ this->Flags = Flags;
+ this->Addralign = 1;
}
-template <class ELFT>
-void OutputSectionBase<ELFT>::writeHeaderTo(Elf_Shdr *Shdr) {
- *Shdr = Header;
+uint32_t OutputSectionBase::getPhdrFlags() const {
+ uint32_t Ret = PF_R;
+ if (Flags & SHF_WRITE)
+ Ret |= PF_W;
+ if (Flags & SHF_EXECINSTR)
+ Ret |= PF_X;
+ return Ret;
}
template <class ELFT>
-GotPltSection<ELFT>::GotPltSection()
- : OutputSectionBase<ELFT>(".got.plt", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE) {
- this->Header.sh_addralign = Target->GotPltEntrySize;
-}
-
-template <class ELFT> void GotPltSection<ELFT>::addEntry(SymbolBody &Sym) {
- Sym.GotPltIndex = Target->GotPltHeaderEntriesNum + Entries.size();
- Entries.push_back(&Sym);
-}
-
-template <class ELFT> bool GotPltSection<ELFT>::empty() const {
- return Entries.empty();
-}
-
-template <class ELFT> void GotPltSection<ELFT>::finalize() {
- this->Header.sh_size = (Target->GotPltHeaderEntriesNum + Entries.size()) *
- Target->GotPltEntrySize;
-}
-
-template <class ELFT> void GotPltSection<ELFT>::writeTo(uint8_t *Buf) {
- Target->writeGotPltHeader(Buf);
- Buf += Target->GotPltHeaderEntriesNum * Target->GotPltEntrySize;
- for (const SymbolBody *B : Entries) {
- Target->writeGotPlt(Buf, *B);
- Buf += sizeof(uintX_t);
+void OutputSectionBase::writeHeaderTo(typename ELFT::Shdr *Shdr) {
+ Shdr->sh_entsize = Entsize;
+ Shdr->sh_addralign = Addralign;
+ Shdr->sh_type = Type;
+ Shdr->sh_offset = Offset;
+ Shdr->sh_flags = Flags;
+ Shdr->sh_info = Info;
+ Shdr->sh_link = Link;
+ Shdr->sh_addr = Addr;
+ Shdr->sh_size = Size;
+ Shdr->sh_name = ShName;
+}
+
+template <class ELFT> static uint64_t getEntsize(uint32_t Type) {
+ switch (Type) {
+ case SHT_RELA:
+ return sizeof(typename ELFT::Rela);
+ case SHT_REL:
+ return sizeof(typename ELFT::Rel);
+ case SHT_MIPS_REGINFO:
+ return sizeof(Elf_Mips_RegInfo<ELFT>);
+ case SHT_MIPS_OPTIONS:
+ return sizeof(Elf_Mips_Options<ELFT>) + sizeof(Elf_Mips_RegInfo<ELFT>);
+ case SHT_MIPS_ABIFLAGS:
+ return sizeof(Elf_Mips_ABIFlags<ELFT>);
+ default:
+ return 0;
}
}
template <class ELFT>
-GotSection<ELFT>::GotSection()
- : OutputSectionBase<ELFT>(".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE) {
- if (Config->EMachine == EM_MIPS)
- this->Header.sh_flags |= SHF_MIPS_GPREL;
- this->Header.sh_addralign = Target->GotEntrySize;
-}
-
-template <class ELFT>
-void GotSection<ELFT>::addEntry(SymbolBody &Sym) {
- Sym.GotIndex = Entries.size();
- Entries.push_back(&Sym);
-}
-
-template <class ELFT>
-void GotSection<ELFT>::addMipsEntry(SymbolBody &Sym, uintX_t Addend,
- RelExpr Expr) {
- // For "true" local symbols which can be referenced from the same module
- // only compiler creates two instructions for address loading:
- //
- // lw $8, 0($gp) # R_MIPS_GOT16
- // addi $8, $8, 0 # R_MIPS_LO16
- //
- // The first instruction loads high 16 bits of the symbol address while
- // the second adds an offset. That allows to reduce number of required
- // GOT entries because only one global offset table entry is necessary
- // for every 64 KBytes of local data. So for local symbols we need to
- // allocate number of GOT entries to hold all required "page" addresses.
- //
- // All global symbols (hidden and regular) considered by compiler uniformly.
- // It always generates a single `lw` instruction and R_MIPS_GOT16 relocation
- // to load address of the symbol. So for each such symbol we need to
- // allocate dedicated GOT entry to store its address.
- //
- // If a symbol is preemptible we need help of dynamic linker to get its
- // final address. The corresponding GOT entries are allocated in the
- // "global" part of GOT. Entries for non preemptible global symbol allocated
- // in the "local" part of GOT.
- //
- // See "Global Offset Table" in Chapter 5:
- // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- if (Expr == R_MIPS_GOT_LOCAL_PAGE) {
- // At this point we do not know final symbol value so to reduce number
- // of allocated GOT entries do the following trick. Save all output
- // sections referenced by GOT relocations. Then later in the `finalize`
- // method calculate number of "pages" required to cover all saved output
- // section and allocate appropriate number of GOT entries.
- auto *OutSec = cast<DefinedRegular<ELFT>>(&Sym)->Section->OutSec;
- MipsOutSections.insert(OutSec);
- return;
- }
- if (Sym.isTls()) {
- // GOT entries created for MIPS TLS relocations behave like
- // almost GOT entries from other ABIs. They go to the end
- // of the global offset table.
- Sym.GotIndex = Entries.size();
- Entries.push_back(&Sym);
- return;
- }
- auto AddEntry = [&](SymbolBody &S, uintX_t A, MipsGotEntries &Items) {
- if (S.isInGot() && !A)
- return;
- size_t NewIndex = Items.size();
- if (!MipsGotMap.insert({{&S, A}, NewIndex}).second)
- return;
- Items.emplace_back(&S, A);
- if (!A)
- S.GotIndex = NewIndex;
- };
- if (Sym.isPreemptible()) {
- // Ignore addends for preemptible symbols. They got single GOT entry anyway.
- AddEntry(Sym, 0, MipsGlobal);
- Sym.IsInGlobalMipsGot = true;
- } else
- AddEntry(Sym, Addend, MipsLocal);
-}
-
-template <class ELFT> bool GotSection<ELFT>::addDynTlsEntry(SymbolBody &Sym) {
- if (Sym.GlobalDynIndex != -1U)
- return false;
- Sym.GlobalDynIndex = Entries.size();
- // Global Dynamic TLS entries take two GOT slots.
- Entries.push_back(nullptr);
- Entries.push_back(&Sym);
- return true;
+OutputSection<ELFT>::OutputSection(StringRef Name, uint32_t Type, uintX_t Flags)
+ : OutputSectionBase(Name, Type, Flags) {
+ this->Entsize = getEntsize<ELFT>(Type);
}
-// Reserves TLS entries for a TLS module ID and a TLS block offset.
-// In total it takes two GOT slots.
-template <class ELFT> bool GotSection<ELFT>::addTlsIndex() {
- if (TlsIndexOff != uint32_t(-1))
+template <typename ELFT>
+static bool compareByFilePosition(InputSection<ELFT> *A,
+ InputSection<ELFT> *B) {
+ // Synthetic doesn't have link order dependecy, stable_sort will keep it last
+ if (A->kind() == InputSectionData::Synthetic ||
+ B->kind() == InputSectionData::Synthetic)
return false;
- TlsIndexOff = Entries.size() * sizeof(uintX_t);
- Entries.push_back(nullptr);
- Entries.push_back(nullptr);
- return true;
-}
-
-template <class ELFT>
-typename GotSection<ELFT>::uintX_t
-GotSection<ELFT>::getMipsLocalPageOffset(uintX_t EntryValue) {
- // Initialize the entry by the %hi(EntryValue) expression
- // but without right-shifting.
- EntryValue = (EntryValue + 0x8000) & ~0xffff;
- // Take into account MIPS GOT header.
- // See comment in the GotSection::writeTo.
- size_t NewIndex = MipsLocalGotPos.size() + 2;
- auto P = MipsLocalGotPos.insert(std::make_pair(EntryValue, NewIndex));
- assert(!P.second || MipsLocalGotPos.size() <= MipsPageEntries);
- return (uintX_t)P.first->second * sizeof(uintX_t) - MipsGPOffset;
-}
-
-template <class ELFT>
-typename GotSection<ELFT>::uintX_t
-GotSection<ELFT>::getMipsGotOffset(const SymbolBody &B, uintX_t Addend) const {
- uintX_t Off = MipsPageEntries;
- if (B.isTls())
- Off += MipsLocal.size() + MipsGlobal.size() + B.GotIndex;
- else if (B.IsInGlobalMipsGot)
- Off += MipsLocal.size() + B.GotIndex;
- else if (B.isInGot())
- Off += B.GotIndex;
- else {
- auto It = MipsGotMap.find({&B, Addend});
- assert(It != MipsGotMap.end());
- Off += It->second;
- }
- return Off * sizeof(uintX_t) - MipsGPOffset;
-}
-
-template <class ELFT>
-typename GotSection<ELFT>::uintX_t GotSection<ELFT>::getMipsTlsOffset() {
- return (MipsPageEntries + MipsLocal.size() + MipsGlobal.size()) *
- sizeof(uintX_t);
-}
-
-template <class ELFT>
-typename GotSection<ELFT>::uintX_t
-GotSection<ELFT>::getGlobalDynAddr(const SymbolBody &B) const {
- return this->getVA() + B.GlobalDynIndex * sizeof(uintX_t);
-}
-
-template <class ELFT>
-typename GotSection<ELFT>::uintX_t
-GotSection<ELFT>::getGlobalDynOffset(const SymbolBody &B) const {
- return B.GlobalDynIndex * sizeof(uintX_t);
-}
-
-template <class ELFT>
-const SymbolBody *GotSection<ELFT>::getMipsFirstGlobalEntry() const {
- return MipsGlobal.empty() ? nullptr : MipsGlobal.front().first;
-}
-
-template <class ELFT>
-unsigned GotSection<ELFT>::getMipsLocalEntriesNum() const {
- return MipsPageEntries + MipsLocal.size();
+ auto *LA = cast<InputSection<ELFT>>(A->getLinkOrderDep());
+ auto *LB = cast<InputSection<ELFT>>(B->getLinkOrderDep());
+ OutputSectionBase *AOut = LA->OutSec;
+ OutputSectionBase *BOut = LB->OutSec;
+ if (AOut != BOut)
+ return AOut->SectionIndex < BOut->SectionIndex;
+ return LA->OutSecOff < LB->OutSecOff;
}
-template <class ELFT> void GotSection<ELFT>::finalize() {
- size_t EntriesNum = Entries.size();
- if (Config->EMachine == EM_MIPS) {
- // Take into account MIPS GOT header.
- // See comment in the GotSection::writeTo.
- MipsPageEntries += 2;
- for (const OutputSectionBase<ELFT> *OutSec : MipsOutSections) {
- // Calculate an upper bound of MIPS GOT entries required to store page
- // addresses of local symbols. We assume the worst case - each 64kb
- // page of the output section has at least one GOT relocation against it.
- // Add 0x8000 to the section's size because the page address stored
- // in the GOT entry is calculated as (value + 0x8000) & ~0xffff.
- MipsPageEntries += (OutSec->getSize() + 0x8000 + 0xfffe) / 0xffff;
- }
- EntriesNum += MipsPageEntries + MipsLocal.size() + MipsGlobal.size();
- }
- this->Header.sh_size = EntriesNum * sizeof(uintX_t);
-}
-
-template <class ELFT> void GotSection<ELFT>::writeMipsGot(uint8_t *&Buf) {
- // Set the MSB of the second GOT slot. This is not required by any
- // MIPS ABI documentation, though.
- //
- // There is a comment in glibc saying that "The MSB of got[1] of a
- // gnu object is set to identify gnu objects," and in GNU gold it
- // says "the second entry will be used by some runtime loaders".
- // But how this field is being used is unclear.
- //
- // We are not really willing to mimic other linkers behaviors
- // without understanding why they do that, but because all files
- // generated by GNU tools have this special GOT value, and because
- // we've been doing this for years, it is probably a safe bet to
- // keep doing this for now. We really need to revisit this to see
- // if we had to do this.
- auto *P = reinterpret_cast<typename ELFT::Off *>(Buf);
- P[1] = uintX_t(1) << (ELFT::Is64Bits ? 63 : 31);
- // Write 'page address' entries to the local part of the GOT.
- for (std::pair<uintX_t, size_t> &L : MipsLocalGotPos) {
- uint8_t *Entry = Buf + L.second * sizeof(uintX_t);
- write<uintX_t, ELFT::TargetEndianness, sizeof(uintX_t)>(Entry, L.first);
- }
- Buf += MipsPageEntries * sizeof(uintX_t);
- auto AddEntry = [&](const MipsGotEntry &SA) {
- uint8_t *Entry = Buf;
- Buf += sizeof(uintX_t);
- const SymbolBody* Body = SA.first;
- uintX_t VA = Body->template getVA<ELFT>(SA.second);
- write<uintX_t, ELFT::TargetEndianness, sizeof(uintX_t)>(Entry, VA);
- };
- std::for_each(std::begin(MipsLocal), std::end(MipsLocal), AddEntry);
- std::for_each(std::begin(MipsGlobal), std::end(MipsGlobal), AddEntry);
-}
-
-template <class ELFT> void GotSection<ELFT>::writeTo(uint8_t *Buf) {
- if (Config->EMachine == EM_MIPS)
- writeMipsGot(Buf);
- for (const SymbolBody *B : Entries) {
- uint8_t *Entry = Buf;
- Buf += sizeof(uintX_t);
- if (!B)
- continue;
- if (B->isPreemptible())
- continue; // The dynamic linker will take care of it.
- uintX_t VA = B->getVA<ELFT>();
- write<uintX_t, ELFT::TargetEndianness, sizeof(uintX_t)>(Entry, VA);
- }
-}
-
-template <class ELFT>
-PltSection<ELFT>::PltSection()
- : OutputSectionBase<ELFT>(".plt", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR) {
- this->Header.sh_addralign = 16;
-}
-
-template <class ELFT> void PltSection<ELFT>::writeTo(uint8_t *Buf) {
- // At beginning of PLT, we have code to call the dynamic linker
- // to resolve dynsyms at runtime. Write such code.
- Target->writePltHeader(Buf);
- size_t Off = Target->PltHeaderSize;
-
- for (auto &I : Entries) {
- const SymbolBody *B = I.first;
- unsigned RelOff = I.second;
- uint64_t Got = B->getGotPltVA<ELFT>();
- uint64_t Plt = this->getVA() + Off;
- Target->writePlt(Buf + Off, Got, Plt, B->PltIndex, RelOff);
- Off += Target->PltEntrySize;
- }
-}
-
-template <class ELFT> void PltSection<ELFT>::addEntry(SymbolBody &Sym) {
- Sym.PltIndex = Entries.size();
- unsigned RelOff = Out<ELFT>::RelaPlt->getRelocOffset();
- Entries.push_back(std::make_pair(&Sym, RelOff));
-}
-
-template <class ELFT> void PltSection<ELFT>::finalize() {
- this->Header.sh_size =
- Target->PltHeaderSize + Entries.size() * Target->PltEntrySize;
-}
-
-template <class ELFT>
-RelocationSection<ELFT>::RelocationSection(StringRef Name, bool Sort)
- : OutputSectionBase<ELFT>(Name, Config->Rela ? SHT_RELA : SHT_REL,
- SHF_ALLOC),
- Sort(Sort) {
- this->Header.sh_entsize = Config->Rela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
- this->Header.sh_addralign = sizeof(uintX_t);
-}
-
-template <class ELFT>
-void RelocationSection<ELFT>::addReloc(const DynamicReloc<ELFT> &Reloc) {
- Relocs.push_back(Reloc);
-}
-
-template <class ELFT, class RelTy>
-static bool compRelocations(const RelTy &A, const RelTy &B) {
- return A.getSymbol(Config->Mips64EL) < B.getSymbol(Config->Mips64EL);
-}
-
-template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *Buf) {
- uint8_t *BufBegin = Buf;
- for (const DynamicReloc<ELFT> &Rel : Relocs) {
- auto *P = reinterpret_cast<Elf_Rela *>(Buf);
- Buf += Config->Rela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
-
- if (Config->Rela)
- P->r_addend = Rel.getAddend();
- P->r_offset = Rel.getOffset();
- if (Config->EMachine == EM_MIPS && Rel.getOutputSec() == Out<ELFT>::Got)
- // Dynamic relocation against MIPS GOT section make deal TLS entries
- // allocated in the end of the GOT. We need to adjust the offset to take
- // in account 'local' and 'global' GOT entries.
- P->r_offset += Out<ELFT>::Got->getMipsTlsOffset();
- P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->Mips64EL);
- }
-
- if (Sort) {
- if (Config->Rela)
- std::stable_sort((Elf_Rela *)BufBegin,
- (Elf_Rela *)BufBegin + Relocs.size(),
- compRelocations<ELFT, Elf_Rela>);
- else
- std::stable_sort((Elf_Rel *)BufBegin, (Elf_Rel *)BufBegin + Relocs.size(),
- compRelocations<ELFT, Elf_Rel>);
- }
-}
-
-template <class ELFT> unsigned RelocationSection<ELFT>::getRelocOffset() {
- return this->Header.sh_entsize * Relocs.size();
-}
-
-template <class ELFT> void RelocationSection<ELFT>::finalize() {
- this->Header.sh_link = Static ? Out<ELFT>::SymTab->SectionIndex
- : Out<ELFT>::DynSymTab->SectionIndex;
- this->Header.sh_size = Relocs.size() * this->Header.sh_entsize;
-}
-
-template <class ELFT>
-InterpSection<ELFT>::InterpSection()
- : OutputSectionBase<ELFT>(".interp", SHT_PROGBITS, SHF_ALLOC) {
- this->Header.sh_size = Config->DynamicLinker.size() + 1;
-}
-
-template <class ELFT> void InterpSection<ELFT>::writeTo(uint8_t *Buf) {
- StringRef S = Config->DynamicLinker;
- memcpy(Buf, S.data(), S.size());
-}
-
-template <class ELFT>
-HashTableSection<ELFT>::HashTableSection()
- : OutputSectionBase<ELFT>(".hash", SHT_HASH, SHF_ALLOC) {
- this->Header.sh_entsize = sizeof(Elf_Word);
- this->Header.sh_addralign = sizeof(Elf_Word);
-}
-
-static uint32_t hashSysv(StringRef Name) {
- uint32_t H = 0;
- for (char C : Name) {
- H = (H << 4) + C;
- uint32_t G = H & 0xf0000000;
- if (G)
- H ^= G >> 24;
- H &= ~G;
- }
- return H;
-}
-
-template <class ELFT> void HashTableSection<ELFT>::finalize() {
- this->Header.sh_link = Out<ELFT>::DynSymTab->SectionIndex;
-
- unsigned NumEntries = 2; // nbucket and nchain.
- NumEntries += Out<ELFT>::DynSymTab->getNumSymbols(); // The chain entries.
-
- // Create as many buckets as there are symbols.
- // FIXME: This is simplistic. We can try to optimize it, but implementing
- // support for SHT_GNU_HASH is probably even more profitable.
- NumEntries += Out<ELFT>::DynSymTab->getNumSymbols();
- this->Header.sh_size = NumEntries * sizeof(Elf_Word);
-}
-
-template <class ELFT> void HashTableSection<ELFT>::writeTo(uint8_t *Buf) {
- unsigned NumSymbols = Out<ELFT>::DynSymTab->getNumSymbols();
- auto *P = reinterpret_cast<Elf_Word *>(Buf);
- *P++ = NumSymbols; // nbucket
- *P++ = NumSymbols; // nchain
-
- Elf_Word *Buckets = P;
- Elf_Word *Chains = P + NumSymbols;
-
- for (const std::pair<SymbolBody *, unsigned> &P :
- Out<ELFT>::DynSymTab->getSymbols()) {
- SymbolBody *Body = P.first;
- StringRef Name = Body->getName();
- unsigned I = Body->DynsymIndex;
- uint32_t Hash = hashSysv(Name) % NumSymbols;
- Chains[I] = Buckets[Hash];
- Buckets[Hash] = I;
- }
-}
-
-static uint32_t hashGnu(StringRef Name) {
- uint32_t H = 5381;
- for (uint8_t C : Name)
- H = (H << 5) + H + C;
- return H;
-}
-
-template <class ELFT>
-GnuHashTableSection<ELFT>::GnuHashTableSection()
- : OutputSectionBase<ELFT>(".gnu.hash", SHT_GNU_HASH, SHF_ALLOC) {
- this->Header.sh_entsize = ELFT::Is64Bits ? 0 : 4;
- this->Header.sh_addralign = sizeof(uintX_t);
-}
-
-template <class ELFT>
-unsigned GnuHashTableSection<ELFT>::calcNBuckets(unsigned NumHashed) {
- if (!NumHashed)
- return 0;
-
- // These values are prime numbers which are not greater than 2^(N-1) + 1.
- // In result, for any particular NumHashed we return a prime number
- // which is not greater than NumHashed.
- static const unsigned Primes[] = {
- 1, 1, 3, 3, 7, 13, 31, 61, 127, 251,
- 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071};
-
- return Primes[std::min<unsigned>(Log2_32_Ceil(NumHashed),
- array_lengthof(Primes) - 1)];
-}
-
-// Bloom filter estimation: at least 8 bits for each hashed symbol.
-// GNU Hash table requirement: it should be a power of 2,
-// the minimum value is 1, even for an empty table.
-// Expected results for a 32-bit target:
-// calcMaskWords(0..4) = 1
-// calcMaskWords(5..8) = 2
-// calcMaskWords(9..16) = 4
-// For a 64-bit target:
-// calcMaskWords(0..8) = 1
-// calcMaskWords(9..16) = 2
-// calcMaskWords(17..32) = 4
-template <class ELFT>
-unsigned GnuHashTableSection<ELFT>::calcMaskWords(unsigned NumHashed) {
- if (!NumHashed)
- return 1;
- return NextPowerOf2((NumHashed - 1) / sizeof(Elf_Off));
-}
-
-template <class ELFT> void GnuHashTableSection<ELFT>::finalize() {
- unsigned NumHashed = Symbols.size();
- NBuckets = calcNBuckets(NumHashed);
- MaskWords = calcMaskWords(NumHashed);
- // Second hash shift estimation: just predefined values.
- Shift2 = ELFT::Is64Bits ? 6 : 5;
-
- this->Header.sh_link = Out<ELFT>::DynSymTab->SectionIndex;
- this->Header.sh_size = sizeof(Elf_Word) * 4 // Header
- + sizeof(Elf_Off) * MaskWords // Bloom Filter
- + sizeof(Elf_Word) * NBuckets // Hash Buckets
- + sizeof(Elf_Word) * NumHashed; // Hash Values
-}
-
-template <class ELFT> void GnuHashTableSection<ELFT>::writeTo(uint8_t *Buf) {
- writeHeader(Buf);
- if (Symbols.empty())
- return;
- writeBloomFilter(Buf);
- writeHashTable(Buf);
-}
-
-template <class ELFT>
-void GnuHashTableSection<ELFT>::writeHeader(uint8_t *&Buf) {
- auto *P = reinterpret_cast<Elf_Word *>(Buf);
- *P++ = NBuckets;
- *P++ = Out<ELFT>::DynSymTab->getNumSymbols() - Symbols.size();
- *P++ = MaskWords;
- *P++ = Shift2;
- Buf = reinterpret_cast<uint8_t *>(P);
-}
-
-template <class ELFT>
-void GnuHashTableSection<ELFT>::writeBloomFilter(uint8_t *&Buf) {
- unsigned C = sizeof(Elf_Off) * 8;
-
- auto *Masks = reinterpret_cast<Elf_Off *>(Buf);
- for (const SymbolData &Sym : Symbols) {
- size_t Pos = (Sym.Hash / C) & (MaskWords - 1);
- uintX_t V = (uintX_t(1) << (Sym.Hash % C)) |
- (uintX_t(1) << ((Sym.Hash >> Shift2) % C));
- Masks[Pos] |= V;
- }
- Buf += sizeof(Elf_Off) * MaskWords;
-}
+template <class ELFT> void OutputSection<ELFT>::finalize() {
+ if ((this->Flags & SHF_LINK_ORDER) && !this->Sections.empty()) {
+ std::sort(Sections.begin(), Sections.end(), compareByFilePosition<ELFT>);
+ Size = 0;
+ assignOffsets();
-template <class ELFT>
-void GnuHashTableSection<ELFT>::writeHashTable(uint8_t *Buf) {
- Elf_Word *Buckets = reinterpret_cast<Elf_Word *>(Buf);
- Elf_Word *Values = Buckets + NBuckets;
-
- int PrevBucket = -1;
- int I = 0;
- for (const SymbolData &Sym : Symbols) {
- int Bucket = Sym.Hash % NBuckets;
- assert(PrevBucket <= Bucket);
- if (Bucket != PrevBucket) {
- Buckets[Bucket] = Sym.Body->DynsymIndex;
- PrevBucket = Bucket;
- if (I > 0)
- Values[I - 1] |= 1;
- }
- Values[I] = Sym.Hash & ~1;
- ++I;
+ // We must preserve the link order dependency of sections with the
+ // SHF_LINK_ORDER flag. The dependency is indicated by the sh_link field. We
+ // need to translate the InputSection sh_link to the OutputSection sh_link,
+ // all InputSections in the OutputSection have the same dependency.
+ if (auto *D = this->Sections.front()->getLinkOrderDep())
+ this->Link = D->OutSec->SectionIndex;
}
- if (I > 0)
- Values[I - 1] |= 1;
-}
-// Add symbols to this symbol hash table. Note that this function
-// destructively sort a given vector -- which is needed because
-// GNU-style hash table places some sorting requirements.
-template <class ELFT>
-void GnuHashTableSection<ELFT>::addSymbols(
- std::vector<std::pair<SymbolBody *, size_t>> &V) {
- // Ideally this will just be 'auto' but GCC 6.1 is not able
- // to deduce it correctly.
- std::vector<std::pair<SymbolBody *, size_t>>::iterator Mid =
- std::stable_partition(V.begin(), V.end(),
- [](std::pair<SymbolBody *, size_t> &P) {
- return P.first->isUndefined();
- });
- if (Mid == V.end())
+ uint32_t Type = this->Type;
+ if (!Config->Relocatable || (Type != SHT_RELA && Type != SHT_REL))
return;
- for (auto I = Mid, E = V.end(); I != E; ++I) {
- SymbolBody *B = I->first;
- size_t StrOff = I->second;
- Symbols.push_back({B, StrOff, hashGnu(B->getName())});
- }
-
- unsigned NBuckets = calcNBuckets(Symbols.size());
- std::stable_sort(Symbols.begin(), Symbols.end(),
- [&](const SymbolData &L, const SymbolData &R) {
- return L.Hash % NBuckets < R.Hash % NBuckets;
- });
-
- V.erase(Mid, V.end());
- for (const SymbolData &Sym : Symbols)
- V.push_back({Sym.Body, Sym.STName});
-}
-
-// Returns the number of version definition entries. Because the first entry
-// is for the version definition itself, it is the number of versioned symbols
-// plus one. Note that we don't support multiple versions yet.
-static unsigned getVerDefNum() { return Config->VersionDefinitions.size() + 1; }
-
-template <class ELFT>
-DynamicSection<ELFT>::DynamicSection()
- : OutputSectionBase<ELFT>(".dynamic", SHT_DYNAMIC, SHF_ALLOC | SHF_WRITE) {
- Elf_Shdr &Header = this->Header;
- Header.sh_addralign = sizeof(uintX_t);
- Header.sh_entsize = ELFT::Is64Bits ? 16 : 8;
-
- // .dynamic section is not writable on MIPS.
- // See "Special Section" in Chapter 4 in the following document:
- // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- if (Config->EMachine == EM_MIPS)
- Header.sh_flags = SHF_ALLOC;
-}
-
-template <class ELFT> void DynamicSection<ELFT>::finalize() {
- if (this->Header.sh_size)
- return; // Already finalized.
-
- Elf_Shdr &Header = this->Header;
- Header.sh_link = Out<ELFT>::DynStrTab->SectionIndex;
-
- auto Add = [=](Entry E) { Entries.push_back(E); };
-
- // Add strings. We know that these are the last strings to be added to
- // DynStrTab and doing this here allows this function to set DT_STRSZ.
- if (!Config->RPath.empty())
- Add({Config->EnableNewDtags ? DT_RUNPATH : DT_RPATH,
- Out<ELFT>::DynStrTab->addString(Config->RPath)});
- for (const std::unique_ptr<SharedFile<ELFT>> &F :
- Symtab<ELFT>::X->getSharedFiles())
- if (F->isNeeded())
- Add({DT_NEEDED, Out<ELFT>::DynStrTab->addString(F->getSoName())});
- if (!Config->SoName.empty())
- Add({DT_SONAME, Out<ELFT>::DynStrTab->addString(Config->SoName)});
-
- Out<ELFT>::DynStrTab->finalize();
-
- if (Out<ELFT>::RelaDyn->hasRelocs()) {
- bool IsRela = Config->Rela;
- Add({IsRela ? DT_RELA : DT_REL, Out<ELFT>::RelaDyn});
- Add({IsRela ? DT_RELASZ : DT_RELSZ, Out<ELFT>::RelaDyn->getSize()});
- Add({IsRela ? DT_RELAENT : DT_RELENT,
- uintX_t(IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel))});
- }
- if (Out<ELFT>::RelaPlt && Out<ELFT>::RelaPlt->hasRelocs()) {
- Add({DT_JMPREL, Out<ELFT>::RelaPlt});
- Add({DT_PLTRELSZ, Out<ELFT>::RelaPlt->getSize()});
- Add({Config->EMachine == EM_MIPS ? DT_MIPS_PLTGOT : DT_PLTGOT,
- Out<ELFT>::GotPlt});
- Add({DT_PLTREL, uint64_t(Config->Rela ? DT_RELA : DT_REL)});
- }
- Add({DT_SYMTAB, Out<ELFT>::DynSymTab});
- Add({DT_SYMENT, sizeof(Elf_Sym)});
- Add({DT_STRTAB, Out<ELFT>::DynStrTab});
- Add({DT_STRSZ, Out<ELFT>::DynStrTab->getSize()});
- if (Out<ELFT>::GnuHashTab)
- Add({DT_GNU_HASH, Out<ELFT>::GnuHashTab});
- if (Out<ELFT>::HashTab)
- Add({DT_HASH, Out<ELFT>::HashTab});
-
- if (PreInitArraySec) {
- Add({DT_PREINIT_ARRAY, PreInitArraySec});
- Add({DT_PREINIT_ARRAYSZ, PreInitArraySec->getSize()});
- }
- if (InitArraySec) {
- Add({DT_INIT_ARRAY, InitArraySec});
- Add({DT_INIT_ARRAYSZ, (uintX_t)InitArraySec->getSize()});
- }
- if (FiniArraySec) {
- Add({DT_FINI_ARRAY, FiniArraySec});
- Add({DT_FINI_ARRAYSZ, (uintX_t)FiniArraySec->getSize()});
- }
-
- if (SymbolBody *B = Symtab<ELFT>::X->find(Config->Init))
- Add({DT_INIT, B});
- if (SymbolBody *B = Symtab<ELFT>::X->find(Config->Fini))
- Add({DT_FINI, B});
-
- uint32_t DtFlags = 0;
- uint32_t DtFlags1 = 0;
- if (Config->Bsymbolic)
- DtFlags |= DF_SYMBOLIC;
- if (Config->ZNodelete)
- DtFlags1 |= DF_1_NODELETE;
- if (Config->ZNow) {
- DtFlags |= DF_BIND_NOW;
- DtFlags1 |= DF_1_NOW;
- }
- if (Config->ZOrigin) {
- DtFlags |= DF_ORIGIN;
- DtFlags1 |= DF_1_ORIGIN;
- }
-
- if (DtFlags)
- Add({DT_FLAGS, DtFlags});
- if (DtFlags1)
- Add({DT_FLAGS_1, DtFlags1});
-
- if (!Config->Entry.empty())
- Add({DT_DEBUG, (uint64_t)0});
-
- bool HasVerNeed = Out<ELFT>::VerNeed->getNeedNum() != 0;
- if (HasVerNeed || Out<ELFT>::VerDef)
- Add({DT_VERSYM, Out<ELFT>::VerSym});
- if (Out<ELFT>::VerDef) {
- Add({DT_VERDEF, Out<ELFT>::VerDef});
- Add({DT_VERDEFNUM, getVerDefNum()});
- }
- if (HasVerNeed) {
- Add({DT_VERNEED, Out<ELFT>::VerNeed});
- Add({DT_VERNEEDNUM, Out<ELFT>::VerNeed->getNeedNum()});
- }
-
- if (Config->EMachine == EM_MIPS) {
- Add({DT_MIPS_RLD_VERSION, 1});
- Add({DT_MIPS_FLAGS, RHF_NOTPOT});
- Add({DT_MIPS_BASE_ADDRESS, Config->ImageBase});
- Add({DT_MIPS_SYMTABNO, Out<ELFT>::DynSymTab->getNumSymbols()});
- Add({DT_MIPS_LOCAL_GOTNO, Out<ELFT>::Got->getMipsLocalEntriesNum()});
- if (const SymbolBody *B = Out<ELFT>::Got->getMipsFirstGlobalEntry())
- Add({DT_MIPS_GOTSYM, B->DynsymIndex});
- else
- Add({DT_MIPS_GOTSYM, Out<ELFT>::DynSymTab->getNumSymbols()});
- Add({DT_PLTGOT, Out<ELFT>::Got});
- if (Out<ELFT>::MipsRldMap)
- Add({DT_MIPS_RLD_MAP, Out<ELFT>::MipsRldMap});
- }
-
- // +1 for DT_NULL
- Header.sh_size = (Entries.size() + 1) * Header.sh_entsize;
-}
-
-template <class ELFT> void DynamicSection<ELFT>::writeTo(uint8_t *Buf) {
- auto *P = reinterpret_cast<Elf_Dyn *>(Buf);
-
- for (const Entry &E : Entries) {
- P->d_tag = E.Tag;
- switch (E.Kind) {
- case Entry::SecAddr:
- P->d_un.d_ptr = E.OutSec->getVA();
- break;
- case Entry::SymAddr:
- P->d_un.d_ptr = E.Sym->template getVA<ELFT>();
- break;
- case Entry::PlainInt:
- P->d_un.d_val = E.Val;
- break;
- }
- ++P;
- }
-}
-
-template <class ELFT>
-EhFrameHeader<ELFT>::EhFrameHeader()
- : OutputSectionBase<ELFT>(".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC) {}
-
-// .eh_frame_hdr contains a binary search table of pointers to FDEs.
-// Each entry of the search table consists of two values,
-// the starting PC from where FDEs covers, and the FDE's address.
-// It is sorted by PC.
-template <class ELFT> void EhFrameHeader<ELFT>::writeTo(uint8_t *Buf) {
- const endianness E = ELFT::TargetEndianness;
-
- // Sort the FDE list by their PC and uniqueify. Usually there is only
- // one FDE for a PC (i.e. function), but if ICF merges two functions
- // into one, there can be more than one FDEs pointing to the address.
- auto Less = [](const FdeData &A, const FdeData &B) { return A.Pc < B.Pc; };
- std::stable_sort(Fdes.begin(), Fdes.end(), Less);
- auto Eq = [](const FdeData &A, const FdeData &B) { return A.Pc == B.Pc; };
- Fdes.erase(std::unique(Fdes.begin(), Fdes.end(), Eq), Fdes.end());
-
- Buf[0] = 1;
- Buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4;
- Buf[2] = DW_EH_PE_udata4;
- Buf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;
- write32<E>(Buf + 4, Out<ELFT>::EhFrame->getVA() - this->getVA() - 4);
- write32<E>(Buf + 8, Fdes.size());
- Buf += 12;
-
- uintX_t VA = this->getVA();
- for (FdeData &Fde : Fdes) {
- write32<E>(Buf, Fde.Pc - VA);
- write32<E>(Buf + 4, Fde.FdeVA - VA);
- Buf += 8;
- }
-}
-
-template <class ELFT> void EhFrameHeader<ELFT>::finalize() {
- // .eh_frame_hdr has a 12 bytes header followed by an array of FDEs.
- this->Header.sh_size = 12 + Out<ELFT>::EhFrame->NumFdes * 8;
-}
-
-template <class ELFT>
-void EhFrameHeader<ELFT>::addFde(uint32_t Pc, uint32_t FdeVA) {
- Fdes.push_back({Pc, FdeVA});
-}
-
-template <class ELFT>
-OutputSection<ELFT>::OutputSection(StringRef Name, uint32_t Type, uintX_t Flags)
- : OutputSectionBase<ELFT>(Name, Type, Flags) {
- if (Type == SHT_RELA)
- this->Header.sh_entsize = sizeof(Elf_Rela);
- else if (Type == SHT_REL)
- this->Header.sh_entsize = sizeof(Elf_Rel);
-}
-
-template <class ELFT> void OutputSection<ELFT>::finalize() {
- uint32_t Type = this->Header.sh_type;
- if (Type != SHT_RELA && Type != SHT_REL)
- return;
- this->Header.sh_link = Out<ELFT>::SymTab->SectionIndex;
+ this->Link = In<ELFT>::SymTab->OutSec->SectionIndex;
// sh_info for SHT_REL[A] sections should contain the section header index of
// the section to which the relocation applies.
InputSectionBase<ELFT> *S = Sections[0]->getRelocatedSection();
- this->Header.sh_info = S->OutSec->SectionIndex;
+ this->Info = S->OutSec->SectionIndex;
}
template <class ELFT>
-void OutputSection<ELFT>::addSection(InputSectionBase<ELFT> *C) {
+void OutputSection<ELFT>::addSection(InputSectionData *C) {
assert(C->Live);
auto *S = cast<InputSection<ELFT>>(C);
Sections.push_back(S);
S->OutSec = this;
this->updateAlignment(S->Alignment);
-}
-
-// If an input string is in the form of "foo.N" where N is a number,
-// return N. Otherwise, returns 65536, which is one greater than the
-// lowest priority.
-static int getPriority(StringRef S) {
- size_t Pos = S.rfind('.');
- if (Pos == StringRef::npos)
- return 65536;
- int V;
- if (S.substr(Pos + 1).getAsInteger(10, V))
- return 65536;
- return V;
+ // Keep sh_entsize value of the input section to be able to perform merging
+ // later during a final linking using the generated relocatable object.
+ if (Config->Relocatable && (S->Flags & SHF_MERGE))
+ this->Entsize = S->Entsize;
}
// This function is called after we sort input sections
// and scan relocations to setup sections' offsets.
template <class ELFT> void OutputSection<ELFT>::assignOffsets() {
- uintX_t Off = this->Header.sh_size;
+ uintX_t Off = this->Size;
for (InputSection<ELFT> *S : Sections) {
Off = alignTo(Off, S->Alignment);
S->OutSecOff = Off;
Off += S->getSize();
}
- this->Header.sh_size = Off;
+ this->Size = Off;
}
-// Sorts input sections by section name suffixes, so that .foo.N comes
-// before .foo.M if N < M. Used to sort .{init,fini}_array.N sections.
-// We want to keep the original order if the priorities are the same
-// because the compiler keeps the original initialization order in a
-// translation unit and we need to respect that.
-// For more detail, read the section of the GCC's manual about init_priority.
-template <class ELFT> void OutputSection<ELFT>::sortInitFini() {
- // Sort sections by priority.
- typedef std::pair<int, InputSection<ELFT> *> Pair;
+template <class ELFT>
+void OutputSection<ELFT>::sort(
+ std::function<int(InputSection<ELFT> *S)> Order) {
+ typedef std::pair<unsigned, InputSection<ELFT> *> Pair;
auto Comp = [](const Pair &A, const Pair &B) { return A.first < B.first; };
std::vector<Pair> V;
for (InputSection<ELFT> *S : Sections)
- V.push_back({getPriority(S->getSectionName()), S});
+ V.push_back({Order(S), S});
std::stable_sort(V.begin(), V.end(), Comp);
Sections.clear();
for (Pair &P : V)
Sections.push_back(P.second);
}
+// Sorts input sections by section name suffixes, so that .foo.N comes
+// before .foo.M if N < M. Used to sort .{init,fini}_array.N sections.
+// We want to keep the original order if the priorities are the same
+// because the compiler keeps the original initialization order in a
+// translation unit and we need to respect that.
+// For more detail, read the section of the GCC's manual about init_priority.
+template <class ELFT> void OutputSection<ELFT>::sortInitFini() {
+ // Sort sections by priority.
+ sort([](InputSection<ELFT> *S) { return getPriority(S->Name); });
+}
+
// Returns true if S matches /Filename.?\.o$/.
static bool isCrtBeginEnd(StringRef S, StringRef Filename) {
if (!S.endswith(".o"))
@@ -921,8 +219,8 @@ static bool compCtors(const InputSection<ELFT> *A,
bool EndB = isCrtend(B->getFile()->getName());
if (EndA != EndB)
return EndB;
- StringRef X = A->getSectionName();
- StringRef Y = B->getSectionName();
+ StringRef X = A->Name;
+ StringRef Y = B->Name;
assert(X.startswith(".ctors") || X.startswith(".dtors"));
assert(Y.startswith(".ctors") || Y.startswith(".dtors"));
X = X.substr(6);
@@ -939,65 +237,50 @@ template <class ELFT> void OutputSection<ELFT>::sortCtorsDtors() {
std::stable_sort(Sections.begin(), Sections.end(), compCtors<ELFT>);
}
-static void fill(uint8_t *Buf, size_t Size, ArrayRef<uint8_t> A) {
+// Fill [Buf, Buf + Size) with Filler. Filler is written in big
+// endian order. This is used for linker script "=fillexp" command.
+void fill(uint8_t *Buf, size_t Size, uint32_t Filler) {
+ uint8_t V[4];
+ write32be(V, Filler);
size_t I = 0;
- for (; I + A.size() < Size; I += A.size())
- memcpy(Buf + I, A.data(), A.size());
- memcpy(Buf + I, A.data(), Size - I);
+ for (; I + 4 < Size; I += 4)
+ memcpy(Buf + I, V, 4);
+ memcpy(Buf + I, V, Size - I);
}
template <class ELFT> void OutputSection<ELFT>::writeTo(uint8_t *Buf) {
- ArrayRef<uint8_t> Filler = Script<ELFT>::X->getFiller(this->Name);
- if (!Filler.empty())
- fill(Buf, this->getSize(), Filler);
- if (Config->Threads) {
- parallel_for_each(Sections.begin(), Sections.end(),
- [=](InputSection<ELFT> *C) { C->writeTo(Buf); });
- } else {
- for (InputSection<ELFT> *C : Sections)
- C->writeTo(Buf);
- }
+ Loc = Buf;
+ if (uint32_t Filler = Script<ELFT>::X->getFiller(this->Name))
+ fill(Buf, this->Size, Filler);
+
+ auto Fn = [=](InputSection<ELFT> *IS) { IS->writeTo(Buf); };
+ forEach(Sections.begin(), Sections.end(), Fn);
+
+ // Linker scripts may have BYTE()-family commands with which you
+ // can write arbitrary bytes to the output. Process them if any.
+ Script<ELFT>::X->writeDataBytes(this->Name, Buf);
}
template <class ELFT>
EhOutputSection<ELFT>::EhOutputSection()
- : OutputSectionBase<ELFT>(".eh_frame", SHT_PROGBITS, SHF_ALLOC) {}
-
-// Returns the first relocation that points to a region
-// between Begin and Begin+Size.
-template <class IntTy, class RelTy>
-static const RelTy *getReloc(IntTy Begin, IntTy Size, ArrayRef<RelTy> &Rels) {
- for (auto I = Rels.begin(), E = Rels.end(); I != E; ++I) {
- if (I->r_offset < Begin)
- continue;
-
- // Truncate Rels for fast access. That means we expect that the
- // relocations are sorted and we are looking up symbols in
- // sequential order. It is naturally satisfied for .eh_frame.
- Rels = Rels.slice(I - Rels.begin());
- if (I->r_offset < Begin + Size)
- return I;
- return nullptr;
- }
- Rels = ArrayRef<RelTy>();
- return nullptr;
-}
+ : OutputSectionBase(".eh_frame", SHT_PROGBITS, SHF_ALLOC) {}
// Search for an existing CIE record or create a new one.
// CIE records from input object files are uniquified by their contents
// and where their relocations point to.
template <class ELFT>
template <class RelTy>
-CieRecord *EhOutputSection<ELFT>::addCie(SectionPiece &Piece,
- EhInputSection<ELFT> *Sec,
- ArrayRef<RelTy> &Rels) {
+CieRecord *EhOutputSection<ELFT>::addCie(EhSectionPiece &Piece,
+ ArrayRef<RelTy> Rels) {
+ auto *Sec = cast<EhInputSection<ELFT>>(Piece.ID);
const endianness E = ELFT::TargetEndianness;
if (read32<E>(Piece.data().data() + 4) != 0)
- fatal("CIE expected at beginning of .eh_frame: " + Sec->getSectionName());
+ fatal(toString(Sec) + ": CIE expected at beginning of .eh_frame");
SymbolBody *Personality = nullptr;
- if (const RelTy *Rel = getReloc(Piece.InputOff, Piece.size(), Rels))
- Personality = &Sec->getFile()->getRelocTargetSym(*Rel);
+ unsigned FirstRelI = Piece.FirstRelocation;
+ if (FirstRelI != (unsigned)-1)
+ Personality = &Sec->getFile()->getRelocTargetSym(Rels[FirstRelI]);
// Search for an existing CIE by CIE contents/relocation target pair.
CieRecord *Cie = &CieMap[{Piece.data(), Personality}];
@@ -1014,13 +297,14 @@ CieRecord *EhOutputSection<ELFT>::addCie(SectionPiece &Piece,
// points to a live function.
template <class ELFT>
template <class RelTy>
-bool EhOutputSection<ELFT>::isFdeLive(SectionPiece &Piece,
- EhInputSection<ELFT> *Sec,
- ArrayRef<RelTy> &Rels) {
- const RelTy *Rel = getReloc(Piece.InputOff, Piece.size(), Rels);
- if (!Rel)
- fatal("FDE doesn't reference another section");
- SymbolBody &B = Sec->getFile()->getRelocTargetSym(*Rel);
+bool EhOutputSection<ELFT>::isFdeLive(EhSectionPiece &Piece,
+ ArrayRef<RelTy> Rels) {
+ auto *Sec = cast<EhInputSection<ELFT>>(Piece.ID);
+ unsigned FirstRelI = Piece.FirstRelocation;
+ if (FirstRelI == (unsigned)-1)
+ fatal(toString(Sec) + ": FDE doesn't reference another section");
+ const RelTy &Rel = Rels[FirstRelI];
+ SymbolBody &B = Sec->getFile()->getRelocTargetSym(Rel);
auto *D = dyn_cast<DefinedRegular<ELFT>>(&B);
if (!D || !D->Section)
return false;
@@ -1039,7 +323,7 @@ void EhOutputSection<ELFT>::addSectionAux(EhInputSection<ELFT> *Sec,
const endianness E = ELFT::TargetEndianness;
DenseMap<size_t, CieRecord *> OffsetToCie;
- for (SectionPiece &Piece : Sec->Pieces) {
+ for (EhSectionPiece &Piece : Sec->Pieces) {
// The empty record is the end marker.
if (Piece.size() == 4)
return;
@@ -1047,16 +331,16 @@ void EhOutputSection<ELFT>::addSectionAux(EhInputSection<ELFT> *Sec,
size_t Offset = Piece.InputOff;
uint32_t ID = read32<E>(Piece.data().data() + 4);
if (ID == 0) {
- OffsetToCie[Offset] = addCie(Piece, Sec, Rels);
+ OffsetToCie[Offset] = addCie(Piece, Rels);
continue;
}
uint32_t CieOffset = Offset + 4 - ID;
CieRecord *Cie = OffsetToCie[CieOffset];
if (!Cie)
- fatal("invalid CIE reference");
+ fatal(toString(Sec) + ": invalid CIE reference");
- if (!isFdeLive(Piece, Sec, Rels))
+ if (!isFdeLive(Piece, Rels))
continue;
Cie->FdePieces.push_back(&Piece);
NumFdes++;
@@ -1064,7 +348,7 @@ void EhOutputSection<ELFT>::addSectionAux(EhInputSection<ELFT> *Sec,
}
template <class ELFT>
-void EhOutputSection<ELFT>::addSection(InputSectionBase<ELFT> *C) {
+void EhOutputSection<ELFT>::addSection(InputSectionData *C) {
auto *Sec = cast<EhInputSection<ELFT>>(C);
Sec->OutSec = this;
this->updateAlignment(Sec->Alignment);
@@ -1077,12 +361,11 @@ void EhOutputSection<ELFT>::addSection(InputSectionBase<ELFT> *C) {
if (Sec->Pieces.empty())
return;
- if (const Elf_Shdr *RelSec = Sec->RelocSection) {
- ELFFile<ELFT> &Obj = Sec->getFile()->getObj();
- if (RelSec->sh_type == SHT_RELA)
- addSectionAux(Sec, Obj.relas(RelSec));
+ if (Sec->NumRelocations) {
+ if (Sec->AreRelocsRela)
+ addSectionAux(Sec, Sec->relas());
else
- addSectionAux(Sec, Obj.rels(RelSec));
+ addSectionAux(Sec, Sec->rels());
return;
}
addSectionAux(Sec, makeArrayRef<Elf_Rela>(nullptr, nullptr));
@@ -1098,7 +381,7 @@ static void writeCieFde(uint8_t *Buf, ArrayRef<uint8_t> D) {
}
template <class ELFT> void EhOutputSection<ELFT>::finalize() {
- if (this->Header.sh_size)
+ if (this->Size)
return; // Already finalized.
size_t Off = 0;
@@ -1106,12 +389,12 @@ template <class ELFT> void EhOutputSection<ELFT>::finalize() {
Cie->Piece->OutputOff = Off;
Off += alignTo(Cie->Piece->size(), sizeof(uintX_t));
- for (SectionPiece *Fde : Cie->FdePieces) {
+ for (EhSectionPiece *Fde : Cie->FdePieces) {
Fde->OutputOff = Off;
Off += alignTo(Fde->size(), sizeof(uintX_t));
}
}
- this->Header.sh_size = Off;
+ this->Size = Off;
}
template <class ELFT> static uint64_t readFdeAddr(uint8_t *Buf, int Size) {
@@ -1143,7 +426,7 @@ typename ELFT::uint EhOutputSection<ELFT>::getFdePc(uint8_t *Buf, size_t FdeOff,
if ((Enc & 0x70) == DW_EH_PE_absptr)
return Addr;
if ((Enc & 0x70) == DW_EH_PE_pcrel)
- return Addr + this->getVA() + Off;
+ return Addr + this->Addr + Off;
fatal("unknown FDE size relative encoding");
}
@@ -1153,7 +436,7 @@ template <class ELFT> void EhOutputSection<ELFT>::writeTo(uint8_t *Buf) {
size_t CieOffset = Cie->Piece->OutputOff;
writeCieFde<ELFT>(Buf + CieOffset, Cie->Piece->data());
- for (SectionPiece *Fde : Cie->FdePieces) {
+ for (EhSectionPiece *Fde : Cie->FdePieces) {
size_t Off = Fde->OutputOff;
writeCieFde<ELFT>(Buf + Off, Fde->data());
@@ -1169,13 +452,13 @@ template <class ELFT> void EhOutputSection<ELFT>::writeTo(uint8_t *Buf) {
// Construct .eh_frame_hdr. .eh_frame_hdr is a binary search table
// to get a FDE from an address to which FDE is applied. So here
// we obtain two addresses and pass them to EhFrameHdr object.
- if (Out<ELFT>::EhFrameHdr) {
+ if (In<ELFT>::EhFrameHdr) {
for (CieRecord *Cie : Cies) {
- uint8_t Enc = getFdeEncoding<ELFT>(Cie->Piece->data());
+ uint8_t Enc = getFdeEncoding<ELFT>(Cie->Piece);
for (SectionPiece *Fde : Cie->FdePieces) {
uintX_t Pc = getFdePc(Buf, Fde->OutputOff, Enc);
- uintX_t FdeVA = this->getVA() + Fde->OutputOff;
- Out<ELFT>::EhFrameHdr->addFde(Pc, FdeVA);
+ uintX_t FdeVA = this->Addr + Fde->OutputOff;
+ In<ELFT>::EhFrameHdr->addFde(Pc, FdeVA);
}
}
}
@@ -1184,620 +467,127 @@ template <class ELFT> void EhOutputSection<ELFT>::writeTo(uint8_t *Buf) {
template <class ELFT>
MergeOutputSection<ELFT>::MergeOutputSection(StringRef Name, uint32_t Type,
uintX_t Flags, uintX_t Alignment)
- : OutputSectionBase<ELFT>(Name, Type, Flags),
+ : OutputSectionBase(Name, Type, Flags),
Builder(StringTableBuilder::RAW, Alignment) {}
template <class ELFT> void MergeOutputSection<ELFT>::writeTo(uint8_t *Buf) {
- if (shouldTailMerge()) {
- StringRef Data = Builder.data();
- memcpy(Buf, Data.data(), Data.size());
- return;
- }
- for (const std::pair<CachedHash<StringRef>, size_t> &P : Builder.getMap()) {
- StringRef Data = P.first.Val;
- memcpy(Buf + P.second, Data.data(), Data.size());
- }
-}
-
-static StringRef toStringRef(ArrayRef<uint8_t> A) {
- return {(const char *)A.data(), A.size()};
+ Builder.write(Buf);
}
template <class ELFT>
-void MergeOutputSection<ELFT>::addSection(InputSectionBase<ELFT> *C) {
+void MergeOutputSection<ELFT>::addSection(InputSectionData *C) {
auto *Sec = cast<MergeInputSection<ELFT>>(C);
Sec->OutSec = this;
this->updateAlignment(Sec->Alignment);
- this->Header.sh_entsize = Sec->getSectionHdr()->sh_entsize;
+ this->Entsize = Sec->Entsize;
Sections.push_back(Sec);
-
- bool IsString = this->Header.sh_flags & SHF_STRINGS;
-
- for (SectionPiece &Piece : Sec->Pieces) {
- if (!Piece.Live)
- continue;
- uintX_t OutputOffset = Builder.add(toStringRef(Piece.data()));
- if (!IsString || !shouldTailMerge())
- Piece.OutputOff = OutputOffset;
- }
-}
-
-template <class ELFT>
-unsigned MergeOutputSection<ELFT>::getOffset(StringRef Val) {
- return Builder.getOffset(Val);
}
template <class ELFT> bool MergeOutputSection<ELFT>::shouldTailMerge() const {
- return Config->Optimize >= 2 && this->Header.sh_flags & SHF_STRINGS;
+ return (this->Flags & SHF_STRINGS) && Config->Optimize >= 2;
}
-template <class ELFT> void MergeOutputSection<ELFT>::finalize() {
- if (shouldTailMerge())
- Builder.finalize();
- this->Header.sh_size = Builder.getSize();
-}
-
-template <class ELFT> void MergeOutputSection<ELFT>::finalizePieces() {
+template <class ELFT> void MergeOutputSection<ELFT>::finalizeTailMerge() {
+ // Add all string pieces to the string table builder to create section
+ // contents.
for (MergeInputSection<ELFT> *Sec : Sections)
- Sec->finalizePieces();
-}
-
-template <class ELFT>
-StringTableSection<ELFT>::StringTableSection(StringRef Name, bool Dynamic)
- : OutputSectionBase<ELFT>(Name, SHT_STRTAB,
- Dynamic ? (uintX_t)SHF_ALLOC : 0),
- Dynamic(Dynamic) {}
-
-// Adds a string to the string table. If HashIt is true we hash and check for
-// duplicates. It is optional because the name of global symbols are already
-// uniqued and hashing them again has a big cost for a small value: uniquing
-// them with some other string that happens to be the same.
-template <class ELFT>
-unsigned StringTableSection<ELFT>::addString(StringRef S, bool HashIt) {
- if (HashIt) {
- auto R = StringMap.insert(std::make_pair(S, Size));
- if (!R.second)
- return R.first->second;
- }
- unsigned Ret = Size;
- Size += S.size() + 1;
- Strings.push_back(S);
- return Ret;
-}
-
-template <class ELFT> void StringTableSection<ELFT>::writeTo(uint8_t *Buf) {
- // ELF string tables start with NUL byte, so advance the pointer by one.
- ++Buf;
- for (StringRef S : Strings) {
- memcpy(Buf, S.data(), S.size());
- Buf += S.size() + 1;
- }
-}
-
-template <class ELFT>
-typename ELFT::uint DynamicReloc<ELFT>::getOffset() const {
- if (OutputSec)
- return OutputSec->getVA() + OffsetInSec;
- return InputSec->OutSec->getVA() + InputSec->getOffset(OffsetInSec);
-}
-
-template <class ELFT>
-typename ELFT::uint DynamicReloc<ELFT>::getAddend() const {
- if (UseSymVA)
- return Sym->getVA<ELFT>(Addend);
- return Addend;
-}
-
-template <class ELFT> uint32_t DynamicReloc<ELFT>::getSymIndex() const {
- if (Sym && !UseSymVA)
- return Sym->DynsymIndex;
- return 0;
-}
-
-template <class ELFT>
-SymbolTableSection<ELFT>::SymbolTableSection(
- StringTableSection<ELFT> &StrTabSec)
- : OutputSectionBase<ELFT>(StrTabSec.isDynamic() ? ".dynsym" : ".symtab",
- StrTabSec.isDynamic() ? SHT_DYNSYM : SHT_SYMTAB,
- StrTabSec.isDynamic() ? (uintX_t)SHF_ALLOC : 0),
- StrTabSec(StrTabSec) {
- this->Header.sh_entsize = sizeof(Elf_Sym);
- this->Header.sh_addralign = sizeof(uintX_t);
-}
-
-// Orders symbols according to their positions in the GOT,
-// in compliance with MIPS ABI rules.
-// See "Global Offset Table" in Chapter 5 in the following document
-// for detailed description:
-// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
-static bool sortMipsSymbols(const std::pair<SymbolBody *, unsigned> &L,
- const std::pair<SymbolBody *, unsigned> &R) {
- // Sort entries related to non-local preemptible symbols by GOT indexes.
- // All other entries go to the first part of GOT in arbitrary order.
- bool LIsInLocalGot = !L.first->IsInGlobalMipsGot;
- bool RIsInLocalGot = !R.first->IsInGlobalMipsGot;
- if (LIsInLocalGot || RIsInLocalGot)
- return !RIsInLocalGot;
- return L.first->GotIndex < R.first->GotIndex;
-}
-
-static uint8_t getSymbolBinding(SymbolBody *Body) {
- Symbol *S = Body->symbol();
- uint8_t Visibility = S->Visibility;
- if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED)
- return STB_LOCAL;
- if (Config->NoGnuUnique && S->Binding == STB_GNU_UNIQUE)
- return STB_GLOBAL;
- return S->Binding;
-}
-
-template <class ELFT> void SymbolTableSection<ELFT>::finalize() {
- if (this->Header.sh_size)
- return; // Already finalized.
-
- this->Header.sh_size = getNumSymbols() * sizeof(Elf_Sym);
- this->Header.sh_link = StrTabSec.SectionIndex;
- this->Header.sh_info = NumLocals + 1;
-
- if (Config->Relocatable) {
- size_t I = NumLocals;
- for (const std::pair<SymbolBody *, size_t> &P : Symbols)
- P.first->DynsymIndex = ++I;
- return;
- }
-
- if (!StrTabSec.isDynamic()) {
- std::stable_sort(Symbols.begin(), Symbols.end(),
- [](const std::pair<SymbolBody *, unsigned> &L,
- const std::pair<SymbolBody *, unsigned> &R) {
- return getSymbolBinding(L.first) == STB_LOCAL &&
- getSymbolBinding(R.first) != STB_LOCAL;
- });
- return;
- }
- if (Out<ELFT>::GnuHashTab)
- // NB: It also sorts Symbols to meet the GNU hash table requirements.
- Out<ELFT>::GnuHashTab->addSymbols(Symbols);
- else if (Config->EMachine == EM_MIPS)
- std::stable_sort(Symbols.begin(), Symbols.end(), sortMipsSymbols);
- size_t I = 0;
- for (const std::pair<SymbolBody *, size_t> &P : Symbols)
- P.first->DynsymIndex = ++I;
-}
-
-template <class ELFT>
-void SymbolTableSection<ELFT>::addSymbol(SymbolBody *B) {
- Symbols.push_back({B, StrTabSec.addString(B->getName(), false)});
-}
-
-template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) {
- Buf += sizeof(Elf_Sym);
-
- // All symbols with STB_LOCAL binding precede the weak and global symbols.
- // .dynsym only contains global symbols.
- if (!Config->DiscardAll && !StrTabSec.isDynamic())
- writeLocalSymbols(Buf);
-
- writeGlobalSymbols(Buf);
-}
-
-template <class ELFT>
-void SymbolTableSection<ELFT>::writeLocalSymbols(uint8_t *&Buf) {
- // Iterate over all input object files to copy their local symbols
- // to the output symbol table pointed by Buf.
- for (const std::unique_ptr<ObjectFile<ELFT>> &File :
- Symtab<ELFT>::X->getObjectFiles()) {
- for (const std::pair<const DefinedRegular<ELFT> *, size_t> &P :
- File->KeptLocalSyms) {
- const DefinedRegular<ELFT> &Body = *P.first;
- InputSectionBase<ELFT> *Section = Body.Section;
- auto *ESym = reinterpret_cast<Elf_Sym *>(Buf);
-
- if (!Section) {
- ESym->st_shndx = SHN_ABS;
- ESym->st_value = Body.Value;
- } else {
- const OutputSectionBase<ELFT> *OutSec = Section->OutSec;
- ESym->st_shndx = OutSec->SectionIndex;
- ESym->st_value = OutSec->getVA() + Section->getOffset(Body);
- }
- ESym->st_name = P.second;
- ESym->st_size = Body.template getSize<ELFT>();
- ESym->setBindingAndType(STB_LOCAL, Body.Type);
- Buf += sizeof(*ESym);
- }
- }
-}
-
-template <class ELFT>
-void SymbolTableSection<ELFT>::writeGlobalSymbols(uint8_t *Buf) {
- // Write the internal symbol table contents to the output symbol table
- // pointed by Buf.
- auto *ESym = reinterpret_cast<Elf_Sym *>(Buf);
- for (const std::pair<SymbolBody *, size_t> &P : Symbols) {
- SymbolBody *Body = P.first;
- size_t StrOff = P.second;
-
- uint8_t Type = Body->Type;
- uintX_t Size = Body->getSize<ELFT>();
-
- ESym->setBindingAndType(getSymbolBinding(Body), Type);
- ESym->st_size = Size;
- ESym->st_name = StrOff;
- ESym->setVisibility(Body->symbol()->Visibility);
- ESym->st_value = Body->getVA<ELFT>();
-
- if (const OutputSectionBase<ELFT> *OutSec = getOutputSection(Body))
- ESym->st_shndx = OutSec->SectionIndex;
- else if (isa<DefinedRegular<ELFT>>(Body))
- ESym->st_shndx = SHN_ABS;
-
- // On MIPS we need to mark symbol which has a PLT entry and requires pointer
- // equality by STO_MIPS_PLT flag. That is necessary to help dynamic linker
- // distinguish such symbols and MIPS lazy-binding stubs.
- // https://sourceware.org/ml/binutils/2008-07/txt00000.txt
- if (Config->EMachine == EM_MIPS && Body->isInPlt() &&
- Body->NeedsCopyOrPltAddr)
- ESym->st_other |= STO_MIPS_PLT;
- ++ESym;
- }
-}
-
-template <class ELFT>
-const OutputSectionBase<ELFT> *
-SymbolTableSection<ELFT>::getOutputSection(SymbolBody *Sym) {
- switch (Sym->kind()) {
- case SymbolBody::DefinedSyntheticKind:
- return cast<DefinedSynthetic<ELFT>>(Sym)->Section;
- case SymbolBody::DefinedRegularKind: {
- auto &D = cast<DefinedRegular<ELFT>>(*Sym);
- if (D.Section)
- return D.Section->OutSec;
- break;
- }
- case SymbolBody::DefinedCommonKind:
- return Out<ELFT>::Bss;
- case SymbolBody::SharedKind:
- if (cast<SharedSymbol<ELFT>>(Sym)->needsCopy())
- return Out<ELFT>::Bss;
- break;
- case SymbolBody::UndefinedKind:
- case SymbolBody::LazyArchiveKind:
- case SymbolBody::LazyObjectKind:
- break;
- case SymbolBody::DefinedBitcodeKind:
- llvm_unreachable("should have been replaced");
- }
- return nullptr;
-}
-
-template <class ELFT>
-VersionDefinitionSection<ELFT>::VersionDefinitionSection()
- : OutputSectionBase<ELFT>(".gnu.version_d", SHT_GNU_verdef, SHF_ALLOC) {
- this->Header.sh_addralign = sizeof(uint32_t);
-}
-
-static StringRef getFileDefName() {
- if (!Config->SoName.empty())
- return Config->SoName;
- return Config->OutputFile;
-}
-
-template <class ELFT> void VersionDefinitionSection<ELFT>::finalize() {
- FileDefNameOff = Out<ELFT>::DynStrTab->addString(getFileDefName());
- for (VersionDefinition &V : Config->VersionDefinitions)
- V.NameOff = Out<ELFT>::DynStrTab->addString(V.Name);
-
- this->Header.sh_size =
- (sizeof(Elf_Verdef) + sizeof(Elf_Verdaux)) * getVerDefNum();
- this->Header.sh_link = Out<ELFT>::DynStrTab->SectionIndex;
-
- // sh_info should be set to the number of definitions. This fact is missed in
- // documentation, but confirmed by binutils community:
- // https://sourceware.org/ml/binutils/2014-11/msg00355.html
- this->Header.sh_info = getVerDefNum();
-}
-
-template <class ELFT>
-void VersionDefinitionSection<ELFT>::writeOne(uint8_t *Buf, uint32_t Index,
- StringRef Name, size_t NameOff) {
- auto *Verdef = reinterpret_cast<Elf_Verdef *>(Buf);
- Verdef->vd_version = 1;
- Verdef->vd_cnt = 1;
- Verdef->vd_aux = sizeof(Elf_Verdef);
- Verdef->vd_next = sizeof(Elf_Verdef) + sizeof(Elf_Verdaux);
- Verdef->vd_flags = (Index == 1 ? VER_FLG_BASE : 0);
- Verdef->vd_ndx = Index;
- Verdef->vd_hash = hashSysv(Name);
-
- auto *Verdaux = reinterpret_cast<Elf_Verdaux *>(Buf + sizeof(Elf_Verdef));
- Verdaux->vda_name = NameOff;
- Verdaux->vda_next = 0;
-}
-
-template <class ELFT>
-void VersionDefinitionSection<ELFT>::writeTo(uint8_t *Buf) {
- writeOne(Buf, 1, getFileDefName(), FileDefNameOff);
-
- for (VersionDefinition &V : Config->VersionDefinitions) {
- Buf += sizeof(Elf_Verdef) + sizeof(Elf_Verdaux);
- writeOne(Buf, V.Id, V.Name, V.NameOff);
- }
-
- // Need to terminate the last version definition.
- Elf_Verdef *Verdef = reinterpret_cast<Elf_Verdef *>(Buf);
- Verdef->vd_next = 0;
-}
-
-template <class ELFT>
-VersionTableSection<ELFT>::VersionTableSection()
- : OutputSectionBase<ELFT>(".gnu.version", SHT_GNU_versym, SHF_ALLOC) {
- this->Header.sh_addralign = sizeof(uint16_t);
-}
-
-template <class ELFT> void VersionTableSection<ELFT>::finalize() {
- this->Header.sh_size =
- sizeof(Elf_Versym) * (Out<ELFT>::DynSymTab->getSymbols().size() + 1);
- this->Header.sh_entsize = sizeof(Elf_Versym);
- // At the moment of june 2016 GNU docs does not mention that sh_link field
- // should be set, but Sun docs do. Also readelf relies on this field.
- this->Header.sh_link = Out<ELFT>::DynSymTab->SectionIndex;
-}
-
-template <class ELFT> void VersionTableSection<ELFT>::writeTo(uint8_t *Buf) {
- auto *OutVersym = reinterpret_cast<Elf_Versym *>(Buf) + 1;
- for (const std::pair<SymbolBody *, size_t> &P :
- Out<ELFT>::DynSymTab->getSymbols()) {
- OutVersym->vs_index = P.first->symbol()->VersionId;
- ++OutVersym;
- }
-}
-
-template <class ELFT>
-VersionNeedSection<ELFT>::VersionNeedSection()
- : OutputSectionBase<ELFT>(".gnu.version_r", SHT_GNU_verneed, SHF_ALLOC) {
- this->Header.sh_addralign = sizeof(uint32_t);
-
- // Identifiers in verneed section start at 2 because 0 and 1 are reserved
- // for VER_NDX_LOCAL and VER_NDX_GLOBAL.
- // First identifiers are reserved by verdef section if it exist.
- NextIndex = getVerDefNum() + 1;
-}
-
-template <class ELFT>
-void VersionNeedSection<ELFT>::addSymbol(SharedSymbol<ELFT> *SS) {
- if (!SS->Verdef) {
- SS->symbol()->VersionId = VER_NDX_GLOBAL;
- return;
- }
- SharedFile<ELFT> *F = SS->file();
- // If we don't already know that we need an Elf_Verneed for this DSO, prepare
- // to create one by adding it to our needed list and creating a dynstr entry
- // for the soname.
- if (F->VerdefMap.empty())
- Needed.push_back({F, Out<ELFT>::DynStrTab->addString(F->getSoName())});
- typename SharedFile<ELFT>::NeededVer &NV = F->VerdefMap[SS->Verdef];
- // If we don't already know that we need an Elf_Vernaux for this Elf_Verdef,
- // prepare to create one by allocating a version identifier and creating a
- // dynstr entry for the version name.
- if (NV.Index == 0) {
- NV.StrTab = Out<ELFT>::DynStrTab->addString(
- SS->file()->getStringTable().data() + SS->Verdef->getAux()->vda_name);
- NV.Index = NextIndex++;
- }
- SS->symbol()->VersionId = NV.Index;
-}
-
-template <class ELFT> void VersionNeedSection<ELFT>::writeTo(uint8_t *Buf) {
- // The Elf_Verneeds need to appear first, followed by the Elf_Vernauxs.
- auto *Verneed = reinterpret_cast<Elf_Verneed *>(Buf);
- auto *Vernaux = reinterpret_cast<Elf_Vernaux *>(Verneed + Needed.size());
-
- for (std::pair<SharedFile<ELFT> *, size_t> &P : Needed) {
- // Create an Elf_Verneed for this DSO.
- Verneed->vn_version = 1;
- Verneed->vn_cnt = P.first->VerdefMap.size();
- Verneed->vn_file = P.second;
- Verneed->vn_aux =
- reinterpret_cast<char *>(Vernaux) - reinterpret_cast<char *>(Verneed);
- Verneed->vn_next = sizeof(Elf_Verneed);
- ++Verneed;
-
- // Create the Elf_Vernauxs for this Elf_Verneed. The loop iterates over
- // VerdefMap, which will only contain references to needed version
- // definitions. Each Elf_Vernaux is based on the information contained in
- // the Elf_Verdef in the source DSO. This loop iterates over a std::map of
- // pointers, but is deterministic because the pointers refer to Elf_Verdef
- // data structures within a single input file.
- for (auto &NV : P.first->VerdefMap) {
- Vernaux->vna_hash = NV.first->vd_hash;
- Vernaux->vna_flags = 0;
- Vernaux->vna_other = NV.second.Index;
- Vernaux->vna_name = NV.second.StrTab;
- Vernaux->vna_next = sizeof(Elf_Vernaux);
- ++Vernaux;
- }
-
- Vernaux[-1].vna_next = 0;
- }
- Verneed[-1].vn_next = 0;
-}
-
-template <class ELFT> void VersionNeedSection<ELFT>::finalize() {
- this->Header.sh_link = Out<ELFT>::DynStrTab->SectionIndex;
- this->Header.sh_info = Needed.size();
- unsigned Size = Needed.size() * sizeof(Elf_Verneed);
- for (std::pair<SharedFile<ELFT> *, size_t> &P : Needed)
- Size += P.first->VerdefMap.size() * sizeof(Elf_Vernaux);
- this->Header.sh_size = Size;
-}
-
-template <class ELFT>
-BuildIdSection<ELFT>::BuildIdSection(size_t HashSize)
- : OutputSectionBase<ELFT>(".note.gnu.build-id", SHT_NOTE, SHF_ALLOC),
- HashSize(HashSize) {
- // 16 bytes for the note section header.
- this->Header.sh_size = 16 + HashSize;
-}
-
-template <class ELFT> void BuildIdSection<ELFT>::writeTo(uint8_t *Buf) {
- const endianness E = ELFT::TargetEndianness;
- write32<E>(Buf, 4); // Name size
- write32<E>(Buf + 4, HashSize); // Content size
- write32<E>(Buf + 8, NT_GNU_BUILD_ID); // Type
- memcpy(Buf + 12, "GNU", 4); // Name string
- HashBuf = Buf + 16;
-}
-
-template <class ELFT>
-void BuildIdFnv1<ELFT>::writeBuildId(ArrayRef<ArrayRef<uint8_t>> Bufs) {
- const endianness E = ELFT::TargetEndianness;
+ for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I)
+ if (Sec->Pieces[I].Live)
+ Builder.add(Sec->getData(I));
- // 64-bit FNV-1 hash
- uint64_t Hash = 0xcbf29ce484222325;
- for (ArrayRef<uint8_t> Buf : Bufs) {
- for (uint8_t B : Buf) {
- Hash *= 0x100000001b3;
- Hash ^= B;
- }
- }
- write64<E>(this->HashBuf, Hash);
-}
+ // Fix the string table content. After this, the contents will never change.
+ Builder.finalize();
+ this->Size = Builder.getSize();
-template <class ELFT>
-void BuildIdMd5<ELFT>::writeBuildId(ArrayRef<ArrayRef<uint8_t>> Bufs) {
- MD5 Hash;
- for (ArrayRef<uint8_t> Buf : Bufs)
- Hash.update(Buf);
- MD5::MD5Result Res;
- Hash.final(Res);
- memcpy(this->HashBuf, Res, 16);
-}
-
-template <class ELFT>
-void BuildIdSha1<ELFT>::writeBuildId(ArrayRef<ArrayRef<uint8_t>> Bufs) {
- SHA1 Hash;
- for (ArrayRef<uint8_t> Buf : Bufs)
- Hash.update(Buf);
- memcpy(this->HashBuf, Hash.final().data(), 20);
+ // finalize() fixed tail-optimized strings, so we can now get
+ // offsets of strings. Get an offset for each string and save it
+ // to a corresponding StringPiece for easy access.
+ for (MergeInputSection<ELFT> *Sec : Sections)
+ for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I)
+ if (Sec->Pieces[I].Live)
+ Sec->Pieces[I].OutputOff = Builder.getOffset(Sec->getData(I));
}
-template <class ELFT>
-BuildIdHexstring<ELFT>::BuildIdHexstring()
- : BuildIdSection<ELFT>(Config->BuildIdVector.size()) {}
+template <class ELFT> void MergeOutputSection<ELFT>::finalizeNoTailMerge() {
+ // Add all string pieces to the string table builder to create section
+ // contents. Because we are not tail-optimizing, offsets of strings are
+ // fixed when they are added to the builder (string table builder contains
+ // a hash table from strings to offsets).
+ for (MergeInputSection<ELFT> *Sec : Sections)
+ for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I)
+ if (Sec->Pieces[I].Live)
+ Sec->Pieces[I].OutputOff = Builder.add(Sec->getData(I));
-template <class ELFT>
-void BuildIdHexstring<ELFT>::writeBuildId(ArrayRef<ArrayRef<uint8_t>> Bufs) {
- memcpy(this->HashBuf, Config->BuildIdVector.data(),
- Config->BuildIdVector.size());
+ Builder.finalizeInOrder();
+ this->Size = Builder.getSize();
}
-template <class ELFT>
-MipsReginfoOutputSection<ELFT>::MipsReginfoOutputSection()
- : OutputSectionBase<ELFT>(".reginfo", SHT_MIPS_REGINFO, SHF_ALLOC) {
- this->Header.sh_addralign = 4;
- this->Header.sh_entsize = sizeof(Elf_Mips_RegInfo);
- this->Header.sh_size = sizeof(Elf_Mips_RegInfo);
+template <class ELFT> void MergeOutputSection<ELFT>::finalize() {
+ if (shouldTailMerge())
+ finalizeTailMerge();
+ else
+ finalizeNoTailMerge();
}
template <class ELFT>
-void MipsReginfoOutputSection<ELFT>::writeTo(uint8_t *Buf) {
- auto *R = reinterpret_cast<Elf_Mips_RegInfo *>(Buf);
- R->ri_gp_value = Out<ELFT>::Got->getVA() + MipsGPOffset;
- R->ri_gprmask = GprMask;
+static typename ELFT::uint getOutFlags(InputSectionBase<ELFT> *S) {
+ return S->Flags & ~SHF_GROUP & ~SHF_COMPRESSED;
}
template <class ELFT>
-void MipsReginfoOutputSection<ELFT>::addSection(InputSectionBase<ELFT> *C) {
- // Copy input object file's .reginfo gprmask to output.
- auto *S = cast<MipsReginfoInputSection<ELFT>>(C);
- GprMask |= S->Reginfo->ri_gprmask;
- S->OutSec = this;
-}
+static SectionKey<ELFT::Is64Bits> createKey(InputSectionBase<ELFT> *C,
+ StringRef OutsecName) {
+ typedef typename ELFT::uint uintX_t;
+ uintX_t Flags = getOutFlags(C);
-template <class ELFT>
-MipsOptionsOutputSection<ELFT>::MipsOptionsOutputSection()
- : OutputSectionBase<ELFT>(".MIPS.options", SHT_MIPS_OPTIONS,
- SHF_ALLOC | SHF_MIPS_NOSTRIP) {
- this->Header.sh_addralign = 8;
- this->Header.sh_entsize = 1;
- this->Header.sh_size = sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo);
-}
+ // For SHF_MERGE we create different output sections for each alignment.
+ // This makes each output section simple and keeps a single level mapping from
+ // input to output.
+ // In case of relocatable object generation we do not try to perform merging
+ // and treat SHF_MERGE sections as regular ones, but also create different
+ // output sections for them to allow merging at final linking stage.
+ uintX_t Alignment = 0;
+ if (isa<MergeInputSection<ELFT>>(C) ||
+ (Config->Relocatable && (C->Flags & SHF_MERGE)))
+ Alignment = std::max<uintX_t>(C->Alignment, C->Entsize);
-template <class ELFT>
-void MipsOptionsOutputSection<ELFT>::writeTo(uint8_t *Buf) {
- auto *Opt = reinterpret_cast<Elf_Mips_Options *>(Buf);
- Opt->kind = ODK_REGINFO;
- Opt->size = this->Header.sh_size;
- Opt->section = 0;
- Opt->info = 0;
- auto *Reg = reinterpret_cast<Elf_Mips_RegInfo *>(Buf + sizeof(*Opt));
- Reg->ri_gp_value = Out<ELFT>::Got->getVA() + MipsGPOffset;
- Reg->ri_gprmask = GprMask;
+ return SectionKey<ELFT::Is64Bits>{OutsecName, C->Type, Flags, Alignment};
}
template <class ELFT>
-void MipsOptionsOutputSection<ELFT>::addSection(InputSectionBase<ELFT> *C) {
- auto *S = cast<MipsOptionsInputSection<ELFT>>(C);
- if (S->Reginfo)
- GprMask |= S->Reginfo->ri_gprmask;
- S->OutSec = this;
-}
-
-template <class ELFT>
-std::pair<OutputSectionBase<ELFT> *, bool>
+std::pair<OutputSectionBase *, bool>
OutputSectionFactory<ELFT>::create(InputSectionBase<ELFT> *C,
StringRef OutsecName) {
SectionKey<ELFT::Is64Bits> Key = createKey(C, OutsecName);
- OutputSectionBase<ELFT> *&Sec = Map[Key];
- if (Sec)
+ return create(Key, C);
+}
+
+template <class ELFT>
+std::pair<OutputSectionBase *, bool>
+OutputSectionFactory<ELFT>::create(const SectionKey<ELFT::Is64Bits> &Key,
+ InputSectionBase<ELFT> *C) {
+ uintX_t Flags = getOutFlags(C);
+ OutputSectionBase *&Sec = Map[Key];
+ if (Sec) {
+ Sec->Flags |= Flags;
return {Sec, false};
+ }
- switch (C->SectionKind) {
+ uint32_t Type = C->Type;
+ switch (C->kind()) {
case InputSectionBase<ELFT>::Regular:
- Sec = new OutputSection<ELFT>(Key.Name, Key.Type, Key.Flags);
+ case InputSectionBase<ELFT>::Synthetic:
+ Sec = make<OutputSection<ELFT>>(Key.Name, Type, Flags);
break;
case InputSectionBase<ELFT>::EHFrame:
return {Out<ELFT>::EhFrame, false};
case InputSectionBase<ELFT>::Merge:
- Sec = new MergeOutputSection<ELFT>(Key.Name, Key.Type, Key.Flags,
- Key.Alignment);
- break;
- case InputSectionBase<ELFT>::MipsReginfo:
- Sec = new MipsReginfoOutputSection<ELFT>();
- break;
- case InputSectionBase<ELFT>::MipsOptions:
- Sec = new MipsOptionsOutputSection<ELFT>();
+ Sec = make<MergeOutputSection<ELFT>>(Key.Name, Type, Flags, Key.Alignment);
break;
}
return {Sec, true};
}
-template <class ELFT>
-OutputSectionBase<ELFT> *OutputSectionFactory<ELFT>::lookup(StringRef Name,
- uint32_t Type,
- uintX_t Flags) {
- return Map.lookup({Name, Type, Flags, 0});
-}
-
-template <class ELFT>
-SectionKey<ELFT::Is64Bits>
-OutputSectionFactory<ELFT>::createKey(InputSectionBase<ELFT> *C,
- StringRef OutsecName) {
- const Elf_Shdr *H = C->getSectionHdr();
- uintX_t Flags = H->sh_flags & ~SHF_GROUP & ~SHF_COMPRESSED;
-
- // For SHF_MERGE we create different output sections for each alignment.
- // This makes each output section simple and keeps a single level mapping from
- // input to output.
- uintX_t Alignment = 0;
- if (isa<MergeInputSection<ELFT>>(C))
- Alignment = std::max(H->sh_addralign, H->sh_entsize);
-
- uint32_t Type = H->sh_type;
- return SectionKey<ELFT::Is64Bits>{OutsecName, Type, Flags, Alignment};
-}
-
template <bool Is64Bits>
typename lld::elf::SectionKey<Is64Bits>
DenseMapInfo<lld::elf::SectionKey<Is64Bits>>::getEmptyKey() {
@@ -1832,55 +622,11 @@ template struct DenseMapInfo<SectionKey<false>>;
namespace lld {
namespace elf {
-template class OutputSectionBase<ELF32LE>;
-template class OutputSectionBase<ELF32BE>;
-template class OutputSectionBase<ELF64LE>;
-template class OutputSectionBase<ELF64BE>;
-
-template class EhFrameHeader<ELF32LE>;
-template class EhFrameHeader<ELF32BE>;
-template class EhFrameHeader<ELF64LE>;
-template class EhFrameHeader<ELF64BE>;
-
-template class GotPltSection<ELF32LE>;
-template class GotPltSection<ELF32BE>;
-template class GotPltSection<ELF64LE>;
-template class GotPltSection<ELF64BE>;
-
-template class GotSection<ELF32LE>;
-template class GotSection<ELF32BE>;
-template class GotSection<ELF64LE>;
-template class GotSection<ELF64BE>;
-
-template class PltSection<ELF32LE>;
-template class PltSection<ELF32BE>;
-template class PltSection<ELF64LE>;
-template class PltSection<ELF64BE>;
-
-template class RelocationSection<ELF32LE>;
-template class RelocationSection<ELF32BE>;
-template class RelocationSection<ELF64LE>;
-template class RelocationSection<ELF64BE>;
-
-template class InterpSection<ELF32LE>;
-template class InterpSection<ELF32BE>;
-template class InterpSection<ELF64LE>;
-template class InterpSection<ELF64BE>;
-
-template class GnuHashTableSection<ELF32LE>;
-template class GnuHashTableSection<ELF32BE>;
-template class GnuHashTableSection<ELF64LE>;
-template class GnuHashTableSection<ELF64BE>;
-
-template class HashTableSection<ELF32LE>;
-template class HashTableSection<ELF32BE>;
-template class HashTableSection<ELF64LE>;
-template class HashTableSection<ELF64BE>;
-
-template class DynamicSection<ELF32LE>;
-template class DynamicSection<ELF32BE>;
-template class DynamicSection<ELF64LE>;
-template class DynamicSection<ELF64BE>;
+
+template void OutputSectionBase::writeHeaderTo<ELF32LE>(ELF32LE::Shdr *Shdr);
+template void OutputSectionBase::writeHeaderTo<ELF32BE>(ELF32BE::Shdr *Shdr);
+template void OutputSectionBase::writeHeaderTo<ELF64LE>(ELF64LE::Shdr *Shdr);
+template void OutputSectionBase::writeHeaderTo<ELF64BE>(ELF64BE::Shdr *Shdr);
template class OutputSection<ELF32LE>;
template class OutputSection<ELF32BE>;
@@ -1892,71 +638,11 @@ template class EhOutputSection<ELF32BE>;
template class EhOutputSection<ELF64LE>;
template class EhOutputSection<ELF64BE>;
-template class MipsReginfoOutputSection<ELF32LE>;
-template class MipsReginfoOutputSection<ELF32BE>;
-template class MipsReginfoOutputSection<ELF64LE>;
-template class MipsReginfoOutputSection<ELF64BE>;
-
-template class MipsOptionsOutputSection<ELF32LE>;
-template class MipsOptionsOutputSection<ELF32BE>;
-template class MipsOptionsOutputSection<ELF64LE>;
-template class MipsOptionsOutputSection<ELF64BE>;
-
template class MergeOutputSection<ELF32LE>;
template class MergeOutputSection<ELF32BE>;
template class MergeOutputSection<ELF64LE>;
template class MergeOutputSection<ELF64BE>;
-template class StringTableSection<ELF32LE>;
-template class StringTableSection<ELF32BE>;
-template class StringTableSection<ELF64LE>;
-template class StringTableSection<ELF64BE>;
-
-template class SymbolTableSection<ELF32LE>;
-template class SymbolTableSection<ELF32BE>;
-template class SymbolTableSection<ELF64LE>;
-template class SymbolTableSection<ELF64BE>;
-
-template class VersionTableSection<ELF32LE>;
-template class VersionTableSection<ELF32BE>;
-template class VersionTableSection<ELF64LE>;
-template class VersionTableSection<ELF64BE>;
-
-template class VersionNeedSection<ELF32LE>;
-template class VersionNeedSection<ELF32BE>;
-template class VersionNeedSection<ELF64LE>;
-template class VersionNeedSection<ELF64BE>;
-
-template class VersionDefinitionSection<ELF32LE>;
-template class VersionDefinitionSection<ELF32BE>;
-template class VersionDefinitionSection<ELF64LE>;
-template class VersionDefinitionSection<ELF64BE>;
-
-template class BuildIdSection<ELF32LE>;
-template class BuildIdSection<ELF32BE>;
-template class BuildIdSection<ELF64LE>;
-template class BuildIdSection<ELF64BE>;
-
-template class BuildIdFnv1<ELF32LE>;
-template class BuildIdFnv1<ELF32BE>;
-template class BuildIdFnv1<ELF64LE>;
-template class BuildIdFnv1<ELF64BE>;
-
-template class BuildIdMd5<ELF32LE>;
-template class BuildIdMd5<ELF32BE>;
-template class BuildIdMd5<ELF64LE>;
-template class BuildIdMd5<ELF64BE>;
-
-template class BuildIdSha1<ELF32LE>;
-template class BuildIdSha1<ELF32BE>;
-template class BuildIdSha1<ELF64LE>;
-template class BuildIdSha1<ELF64BE>;
-
-template class BuildIdHexstring<ELF32LE>;
-template class BuildIdHexstring<ELF32BE>;
-template class BuildIdHexstring<ELF64LE>;
-template class BuildIdHexstring<ELF64BE>;
-
template class OutputSectionFactory<ELF32LE>;
template class OutputSectionFactory<ELF32BE>;
template class OutputSectionFactory<ELF64LE>;
diff --git a/ELF/OutputSections.h b/ELF/OutputSections.h
index 5fdf8de4cb46..978b1f8191ef 100644
--- a/ELF/OutputSections.h
+++ b/ELF/OutputSections.h
@@ -14,25 +14,19 @@
#include "Relocations.h"
#include "lld/Core/LLVM.h"
-#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Object/ELF.h"
-#include "llvm/Support/MD5.h"
-#include "llvm/Support/SHA1.h"
namespace lld {
namespace elf {
+struct PhdrEntry;
class SymbolBody;
-struct SectionPiece;
-template <class ELFT> class SymbolTable;
-template <class ELFT> class SymbolTableSection;
-template <class ELFT> class StringTableSection;
+struct EhSectionPiece;
template <class ELFT> class EhInputSection;
template <class ELFT> class InputSection;
template <class ELFT> class InputSectionBase;
template <class ELFT> class MergeInputSection;
-template <class ELFT> class MipsReginfoInputSection;
template <class ELFT> class OutputSection;
template <class ELFT> class ObjectFile;
template <class ELFT> class SharedFile;
@@ -44,341 +38,126 @@ template <class ELFT> class DefinedRegular;
// input sections, others are created by the linker.
// The writer creates multiple OutputSections and assign them unique,
// non-overlapping file offsets and VAs.
-template <class ELFT> class OutputSectionBase {
+class OutputSectionBase {
public:
- typedef typename ELFT::uint uintX_t;
- typedef typename ELFT::Shdr Elf_Shdr;
+ enum Kind {
+ Base,
+ EHFrame,
+ Merge,
+ Regular,
+ };
- OutputSectionBase(StringRef Name, uint32_t Type, uintX_t Flags);
- void setVA(uintX_t VA) { Header.sh_addr = VA; }
- uintX_t getVA() const { return Header.sh_addr; }
- void setFileOffset(uintX_t Off) { Header.sh_offset = Off; }
- void setSHName(unsigned Val) { Header.sh_name = Val; }
- void writeHeaderTo(Elf_Shdr *SHdr);
- StringRef getName() { return Name; }
+ OutputSectionBase(StringRef Name, uint32_t Type, uint64_t Flags);
+ void setLMAOffset(uint64_t LMAOff) { LMAOffset = LMAOff; }
+ uint64_t getLMA() const { return Addr + LMAOffset; }
+ template <typename ELFT> void writeHeaderTo(typename ELFT::Shdr *SHdr);
+ StringRef getName() const { return Name; }
- virtual void addSection(InputSectionBase<ELFT> *C) {}
+ virtual void addSection(InputSectionData *C) {}
+ virtual Kind getKind() const { return Base; }
+ static bool classof(const OutputSectionBase *B) {
+ return B->getKind() == Base;
+ }
unsigned SectionIndex;
- // Returns the size of the section in the output file.
- uintX_t getSize() const { return Header.sh_size; }
- void setSize(uintX_t Val) { Header.sh_size = Val; }
- uintX_t getFlags() const { return Header.sh_flags; }
- uintX_t getFileOff() const { return Header.sh_offset; }
- uintX_t getAlignment() const { return Header.sh_addralign; }
- uint32_t getType() const { return Header.sh_type; }
-
- void updateAlignment(uintX_t Alignment) {
- if (Alignment > Header.sh_addralign)
- Header.sh_addralign = Alignment;
+ uint32_t getPhdrFlags() const;
+
+ void updateAlignment(uint64_t Alignment) {
+ if (Alignment > Addralign)
+ Addralign = Alignment;
}
// If true, this section will be page aligned on disk.
// Typically the first section of each PT_LOAD segment has this flag.
bool PageAlign = false;
+ // Pointer to the first section in PT_LOAD segment, which this section
+ // also resides in. This field is used to correctly compute file offset
+ // of a section. When two sections share the same load segment, difference
+ // between their file offsets should be equal to difference between their
+ // virtual addresses. To compute some section offset we use the following
+ // formula: Off = Off_first + VA - VA_first.
+ OutputSectionBase *FirstInPtLoad = nullptr;
+
virtual void finalize() {}
- virtual void finalizePieces() {}
virtual void assignOffsets() {}
virtual void writeTo(uint8_t *Buf) {}
virtual ~OutputSectionBase() = default;
-protected:
StringRef Name;
- Elf_Shdr Header;
-};
-template <class ELFT> class GotSection final : public OutputSectionBase<ELFT> {
- typedef OutputSectionBase<ELFT> Base;
- typedef typename ELFT::uint uintX_t;
-
-public:
- GotSection();
- void finalize() override;
- void writeTo(uint8_t *Buf) override;
- void addEntry(SymbolBody &Sym);
- void addMipsEntry(SymbolBody &Sym, uintX_t Addend, RelExpr Expr);
- bool addDynTlsEntry(SymbolBody &Sym);
- bool addTlsIndex();
- bool empty() const { return MipsPageEntries == 0 && Entries.empty(); }
- uintX_t getMipsLocalPageOffset(uintX_t Addr);
- uintX_t getMipsGotOffset(const SymbolBody &B, uintX_t Addend) const;
- uintX_t getGlobalDynAddr(const SymbolBody &B) const;
- uintX_t getGlobalDynOffset(const SymbolBody &B) const;
- uintX_t getNumEntries() const { return Entries.size(); }
-
- // Returns the symbol which corresponds to the first entry of the global part
- // of GOT on MIPS platform. It is required to fill up MIPS-specific dynamic
- // table properties.
- // Returns nullptr if the global part is empty.
- const SymbolBody *getMipsFirstGlobalEntry() const;
-
- // Returns the number of entries in the local part of GOT including
- // the number of reserved entries. This method is MIPS-specific.
- unsigned getMipsLocalEntriesNum() const;
-
- // Returns offset of TLS part of the MIPS GOT table. This part goes
- // after 'local' and 'global' entries.
- uintX_t getMipsTlsOffset();
-
- uintX_t getTlsIndexVA() { return Base::getVA() + TlsIndexOff; }
- uint32_t getTlsIndexOff() { return TlsIndexOff; }
-
- // Flag to force GOT to be in output if we have relocations
- // that relies on its address.
- bool HasGotOffRel = false;
-
-private:
- std::vector<const SymbolBody *> Entries;
- uint32_t TlsIndexOff = -1;
- uint32_t MipsPageEntries = 0;
- // Output sections referenced by MIPS GOT relocations.
- llvm::SmallPtrSet<const OutputSectionBase<ELFT> *, 10> MipsOutSections;
- llvm::DenseMap<uintX_t, size_t> MipsLocalGotPos;
-
- // MIPS ABI requires to create unique GOT entry for each Symbol/Addend
- // pairs. The `MipsGotMap` maps (S,A) pair to the GOT index in the `MipsLocal`
- // or `MipsGlobal` vectors. In general it does not have a sence to take in
- // account addend for preemptible symbols because the corresponding
- // GOT entries should have one-to-one mapping with dynamic symbols table.
- // But we use the same container's types for both kind of GOT entries
- // to handle them uniformly.
- typedef std::pair<const SymbolBody*, uintX_t> MipsGotEntry;
- typedef std::vector<MipsGotEntry> MipsGotEntries;
- llvm::DenseMap<MipsGotEntry, size_t> MipsGotMap;
- MipsGotEntries MipsLocal;
- MipsGotEntries MipsGlobal;
-
- // Write MIPS-specific parts of the GOT.
- void writeMipsGot(uint8_t *&Buf);
+ // The following fields correspond to Elf_Shdr members.
+ uint64_t Size = 0;
+ uint64_t Entsize = 0;
+ uint64_t Addralign = 0;
+ uint64_t Offset = 0;
+ uint64_t Flags = 0;
+ uint64_t LMAOffset = 0;
+ uint64_t Addr = 0;
+ uint32_t ShName = 0;
+ uint32_t Type = 0;
+ uint32_t Info = 0;
+ uint32_t Link = 0;
};
-template <class ELFT>
-class GotPltSection final : public OutputSectionBase<ELFT> {
- typedef typename ELFT::uint uintX_t;
+template <class ELFT> class OutputSection final : public OutputSectionBase {
public:
- GotPltSection();
- void finalize() override;
- void writeTo(uint8_t *Buf) override;
- void addEntry(SymbolBody &Sym);
- bool empty() const;
-
-private:
- std::vector<const SymbolBody *> Entries;
-};
-
-template <class ELFT> class PltSection final : public OutputSectionBase<ELFT> {
- typedef OutputSectionBase<ELFT> Base;
- typedef typename ELFT::uint uintX_t;
-
-public:
- PltSection();
- void finalize() override;
- void writeTo(uint8_t *Buf) override;
- void addEntry(SymbolBody &Sym);
- bool empty() const { return Entries.empty(); }
-
-private:
- std::vector<std::pair<const SymbolBody *, unsigned>> Entries;
-};
-
-template <class ELFT> class DynamicReloc {
- typedef typename ELFT::uint uintX_t;
-
-public:
- DynamicReloc(uint32_t Type, const InputSectionBase<ELFT> *InputSec,
- uintX_t OffsetInSec, bool UseSymVA, SymbolBody *Sym,
- uintX_t Addend)
- : Type(Type), Sym(Sym), InputSec(InputSec), OffsetInSec(OffsetInSec),
- UseSymVA(UseSymVA), Addend(Addend) {}
-
- DynamicReloc(uint32_t Type, const OutputSectionBase<ELFT> *OutputSec,
- uintX_t OffsetInSec, bool UseSymVA, SymbolBody *Sym,
- uintX_t Addend)
- : Type(Type), Sym(Sym), OutputSec(OutputSec), OffsetInSec(OffsetInSec),
- UseSymVA(UseSymVA), Addend(Addend) {}
-
- uintX_t getOffset() const;
- uintX_t getAddend() const;
- uint32_t getSymIndex() const;
- const OutputSectionBase<ELFT> *getOutputSec() const { return OutputSec; }
-
- uint32_t Type;
-
-private:
- SymbolBody *Sym;
- const InputSectionBase<ELFT> *InputSec = nullptr;
- const OutputSectionBase<ELFT> *OutputSec = nullptr;
- uintX_t OffsetInSec;
- bool UseSymVA;
- uintX_t Addend;
-};
-
-template <class ELFT>
-class SymbolTableSection final : public OutputSectionBase<ELFT> {
-public:
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::Sym Elf_Sym;
- typedef typename ELFT::SymRange Elf_Sym_Range;
- typedef typename ELFT::uint uintX_t;
- SymbolTableSection(StringTableSection<ELFT> &StrTabSec);
-
- void finalize() override;
- void writeTo(uint8_t *Buf) override;
- void addSymbol(SymbolBody *Body);
- StringTableSection<ELFT> &getStrTabSec() const { return StrTabSec; }
- unsigned getNumSymbols() const { return NumLocals + Symbols.size() + 1; }
-
- ArrayRef<std::pair<SymbolBody *, size_t>> getSymbols() const {
- return Symbols;
- }
-
- unsigned NumLocals = 0;
- StringTableSection<ELFT> &StrTabSec;
-
-private:
- void writeLocalSymbols(uint8_t *&Buf);
- void writeGlobalSymbols(uint8_t *Buf);
-
- const OutputSectionBase<ELFT> *getOutputSection(SymbolBody *Sym);
-
- // A vector of symbols and their string table offsets.
- std::vector<std::pair<SymbolBody *, size_t>> Symbols;
-};
-
-// For more information about .gnu.version and .gnu.version_r see:
-// https://www.akkadia.org/drepper/symbol-versioning
-
-// The .gnu.version_d section which has a section type of SHT_GNU_verdef shall
-// contain symbol version definitions. The number of entries in this section
-// shall be contained in the DT_VERDEFNUM entry of the .dynamic section.
-// The section shall contain an array of Elf_Verdef structures, optionally
-// followed by an array of Elf_Verdaux structures.
-template <class ELFT>
-class VersionDefinitionSection final : public OutputSectionBase<ELFT> {
- typedef typename ELFT::Verdef Elf_Verdef;
- typedef typename ELFT::Verdaux Elf_Verdaux;
-
-public:
- VersionDefinitionSection();
- void finalize() override;
- void writeTo(uint8_t *Buf) override;
-
-private:
- void writeOne(uint8_t *Buf, uint32_t Index, StringRef Name, size_t NameOff);
-
- unsigned FileDefNameOff;
-};
-
-// The .gnu.version section specifies the required version of each symbol in the
-// dynamic symbol table. It contains one Elf_Versym for each dynamic symbol
-// table entry. An Elf_Versym is just a 16-bit integer that refers to a version
-// identifier defined in the either .gnu.version_r or .gnu.version_d section.
-// The values 0 and 1 are reserved. All other values are used for versions in
-// the own object or in any of the dependencies.
-template <class ELFT>
-class VersionTableSection final : public OutputSectionBase<ELFT> {
- typedef typename ELFT::Versym Elf_Versym;
-
-public:
- VersionTableSection();
- void finalize() override;
- void writeTo(uint8_t *Buf) override;
-};
-
-// The .gnu.version_r section defines the version identifiers used by
-// .gnu.version. It contains a linked list of Elf_Verneed data structures. Each
-// Elf_Verneed specifies the version requirements for a single DSO, and contains
-// a reference to a linked list of Elf_Vernaux data structures which define the
-// mapping from version identifiers to version names.
-template <class ELFT>
-class VersionNeedSection final : public OutputSectionBase<ELFT> {
- typedef typename ELFT::Verneed Elf_Verneed;
- typedef typename ELFT::Vernaux Elf_Vernaux;
-
- // A vector of shared files that need Elf_Verneed data structures and the
- // string table offsets of their sonames.
- std::vector<std::pair<SharedFile<ELFT> *, size_t>> Needed;
-
- // The next available version identifier.
- unsigned NextIndex;
-
-public:
- VersionNeedSection();
- void addSymbol(SharedSymbol<ELFT> *SS);
- void finalize() override;
- void writeTo(uint8_t *Buf) override;
- size_t getNeedNum() const { return Needed.size(); }
-};
-
-template <class ELFT>
-class RelocationSection final : public OutputSectionBase<ELFT> {
- typedef typename ELFT::Rel Elf_Rel;
- typedef typename ELFT::Rela Elf_Rela;
- typedef typename ELFT::uint uintX_t;
-
-public:
- RelocationSection(StringRef Name, bool Sort);
- void addReloc(const DynamicReloc<ELFT> &Reloc);
- unsigned getRelocOffset();
- void finalize() override;
- void writeTo(uint8_t *Buf) override;
- bool hasRelocs() const { return !Relocs.empty(); }
-
- bool Static = false;
-
-private:
- bool Sort;
- std::vector<DynamicReloc<ELFT>> Relocs;
-};
-
-template <class ELFT>
-class OutputSection final : public OutputSectionBase<ELFT> {
-public:
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::Rel Elf_Rel;
typedef typename ELFT::Rela Elf_Rela;
typedef typename ELFT::uint uintX_t;
OutputSection(StringRef Name, uint32_t Type, uintX_t Flags);
- void addSection(InputSectionBase<ELFT> *C) override;
+ void addSection(InputSectionData *C) override;
+ void sort(std::function<int(InputSection<ELFT> *S)> Order);
void sortInitFini();
void sortCtorsDtors();
void writeTo(uint8_t *Buf) override;
void finalize() override;
void assignOffsets() override;
+ Kind getKind() const override { return Regular; }
+ static bool classof(const OutputSectionBase *B) {
+ return B->getKind() == Regular;
+ }
std::vector<InputSection<ELFT> *> Sections;
+
+ // Location in the output buffer.
+ uint8_t *Loc = nullptr;
};
template <class ELFT>
-class MergeOutputSection final : public OutputSectionBase<ELFT> {
+class MergeOutputSection final : public OutputSectionBase {
typedef typename ELFT::uint uintX_t;
public:
MergeOutputSection(StringRef Name, uint32_t Type, uintX_t Flags,
uintX_t Alignment);
- void addSection(InputSectionBase<ELFT> *S) override;
+ void addSection(InputSectionData *S) override;
void writeTo(uint8_t *Buf) override;
- unsigned getOffset(StringRef Val);
void finalize() override;
- void finalizePieces() override;
bool shouldTailMerge() const;
+ Kind getKind() const override { return Merge; }
+ static bool classof(const OutputSectionBase *B) {
+ return B->getKind() == Merge;
+ }
private:
+ void finalizeTailMerge();
+ void finalizeNoTailMerge();
+
llvm::StringTableBuilder Builder;
std::vector<MergeInputSection<ELFT> *> Sections;
};
struct CieRecord {
- SectionPiece *Piece = nullptr;
- std::vector<SectionPiece *> FdePieces;
+ EhSectionPiece *Piece = nullptr;
+ std::vector<EhSectionPiece *> FdePieces;
};
// Output section for .eh_frame.
-template <class ELFT>
-class EhOutputSection final : public OutputSectionBase<ELFT> {
+template <class ELFT> class EhOutputSection final : public OutputSectionBase {
typedef typename ELFT::uint uintX_t;
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Rel Elf_Rel;
@@ -390,7 +169,11 @@ public:
void finalize() override;
bool empty() const { return Sections.empty(); }
- void addSection(InputSectionBase<ELFT> *S) override;
+ void addSection(InputSectionData *S) override;
+ Kind getKind() const override { return EHFrame; }
+ static bool classof(const OutputSectionBase *B) {
+ return B->getKind() == EHFrame;
+ }
size_t NumFdes = 0;
@@ -399,12 +182,10 @@ private:
void addSectionAux(EhInputSection<ELFT> *S, llvm::ArrayRef<RelTy> Rels);
template <class RelTy>
- CieRecord *addCie(SectionPiece &Piece, EhInputSection<ELFT> *Sec,
- ArrayRef<RelTy> &Rels);
+ CieRecord *addCie(EhSectionPiece &Piece, ArrayRef<RelTy> Rels);
template <class RelTy>
- bool isFdeLive(SectionPiece &Piece, EhInputSection<ELFT> *Sec,
- ArrayRef<RelTy> &Rels);
+ bool isFdeLive(EhSectionPiece &Piece, ArrayRef<RelTy> Rels);
uintX_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc);
@@ -415,247 +196,25 @@ private:
llvm::DenseMap<std::pair<ArrayRef<uint8_t>, SymbolBody *>, CieRecord> CieMap;
};
-template <class ELFT>
-class InterpSection final : public OutputSectionBase<ELFT> {
-public:
- InterpSection();
- void writeTo(uint8_t *Buf) override;
-};
-
-template <class ELFT>
-class StringTableSection final : public OutputSectionBase<ELFT> {
-public:
- typedef typename ELFT::uint uintX_t;
- StringTableSection(StringRef Name, bool Dynamic);
- unsigned addString(StringRef S, bool HashIt = true);
- void writeTo(uint8_t *Buf) override;
- unsigned getSize() const { return Size; }
- void finalize() override { this->Header.sh_size = getSize(); }
- bool isDynamic() const { return Dynamic; }
-
-private:
- const bool Dynamic;
- llvm::DenseMap<StringRef, unsigned> StringMap;
- std::vector<StringRef> Strings;
- unsigned Size = 1; // ELF string tables start with a NUL byte, so 1.
-};
-
-template <class ELFT>
-class HashTableSection final : public OutputSectionBase<ELFT> {
- typedef typename ELFT::Word Elf_Word;
-
-public:
- HashTableSection();
- void finalize() override;
- void writeTo(uint8_t *Buf) override;
-};
-
-// Outputs GNU Hash section. For detailed explanation see:
-// https://blogs.oracle.com/ali/entry/gnu_hash_elf_sections
-template <class ELFT>
-class GnuHashTableSection final : public OutputSectionBase<ELFT> {
- typedef typename ELFT::Off Elf_Off;
- typedef typename ELFT::Word Elf_Word;
- typedef typename ELFT::uint uintX_t;
-
-public:
- GnuHashTableSection();
- void finalize() override;
- void writeTo(uint8_t *Buf) override;
-
- // Adds symbols to the hash table.
- // Sorts the input to satisfy GNU hash section requirements.
- void addSymbols(std::vector<std::pair<SymbolBody *, size_t>> &Symbols);
-
-private:
- static unsigned calcNBuckets(unsigned NumHashed);
- static unsigned calcMaskWords(unsigned NumHashed);
-
- void writeHeader(uint8_t *&Buf);
- void writeBloomFilter(uint8_t *&Buf);
- void writeHashTable(uint8_t *Buf);
-
- struct SymbolData {
- SymbolBody *Body;
- size_t STName;
- uint32_t Hash;
- };
-
- std::vector<SymbolData> Symbols;
-
- unsigned MaskWords;
- unsigned NBuckets;
- unsigned Shift2;
-};
-
-template <class ELFT>
-class DynamicSection final : public OutputSectionBase<ELFT> {
- typedef OutputSectionBase<ELFT> Base;
- typedef typename ELFT::Dyn Elf_Dyn;
- typedef typename ELFT::Rel Elf_Rel;
- typedef typename ELFT::Rela Elf_Rela;
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::Sym Elf_Sym;
- typedef typename ELFT::uint uintX_t;
-
- // The .dynamic section contains information for the dynamic linker.
- // The section consists of fixed size entries, which consist of
- // type and value fields. Value are one of plain integers, symbol
- // addresses, or section addresses. This struct represents the entry.
- struct Entry {
- int32_t Tag;
- union {
- OutputSectionBase<ELFT> *OutSec;
- uint64_t Val;
- const SymbolBody *Sym;
- };
- enum KindT { SecAddr, SymAddr, PlainInt } Kind;
- Entry(int32_t Tag, OutputSectionBase<ELFT> *OutSec)
- : Tag(Tag), OutSec(OutSec), Kind(SecAddr) {}
- Entry(int32_t Tag, uint64_t Val) : Tag(Tag), Val(Val), Kind(PlainInt) {}
- Entry(int32_t Tag, const SymbolBody *Sym)
- : Tag(Tag), Sym(Sym), Kind(SymAddr) {}
- };
-
- // finalize() fills this vector with the section contents. finalize()
- // cannot directly create final section contents because when the
- // function is called, symbol or section addresses are not fixed yet.
- std::vector<Entry> Entries;
-
-public:
- explicit DynamicSection();
- void finalize() override;
- void writeTo(uint8_t *Buf) override;
-
- OutputSectionBase<ELFT> *PreInitArraySec = nullptr;
- OutputSectionBase<ELFT> *InitArraySec = nullptr;
- OutputSectionBase<ELFT> *FiniArraySec = nullptr;
-};
-
-template <class ELFT>
-class MipsReginfoOutputSection final : public OutputSectionBase<ELFT> {
- typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo;
-
-public:
- MipsReginfoOutputSection();
- void writeTo(uint8_t *Buf) override;
- void addSection(InputSectionBase<ELFT> *S) override;
-
-private:
- uint32_t GprMask = 0;
-};
-
-template <class ELFT>
-class MipsOptionsOutputSection final : public OutputSectionBase<ELFT> {
- typedef llvm::object::Elf_Mips_Options<ELFT> Elf_Mips_Options;
- typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo;
-
-public:
- MipsOptionsOutputSection();
- void writeTo(uint8_t *Buf) override;
- void addSection(InputSectionBase<ELFT> *S) override;
-
-private:
- uint32_t GprMask = 0;
-};
-
-// --eh-frame-hdr option tells linker to construct a header for all the
-// .eh_frame sections. This header is placed to a section named .eh_frame_hdr
-// and also to a PT_GNU_EH_FRAME segment.
-// At runtime the unwinder then can find all the PT_GNU_EH_FRAME segments by
-// calling dl_iterate_phdr.
-// This section contains a lookup table for quick binary search of FDEs.
-// Detailed info about internals can be found in Ian Lance Taylor's blog:
-// http://www.airs.com/blog/archives/460 (".eh_frame")
-// http://www.airs.com/blog/archives/462 (".eh_frame_hdr")
-template <class ELFT>
-class EhFrameHeader final : public OutputSectionBase<ELFT> {
- typedef typename ELFT::uint uintX_t;
-
-public:
- EhFrameHeader();
- void finalize() override;
- void writeTo(uint8_t *Buf) override;
- void addFde(uint32_t Pc, uint32_t FdeVA);
-
-private:
- struct FdeData {
- uint32_t Pc;
- uint32_t FdeVA;
- };
-
- std::vector<FdeData> Fdes;
-};
-
-template <class ELFT> class BuildIdSection : public OutputSectionBase<ELFT> {
-public:
- void writeTo(uint8_t *Buf) override;
- virtual void writeBuildId(ArrayRef<ArrayRef<uint8_t>> Bufs) = 0;
-
-protected:
- BuildIdSection(size_t HashSize);
- size_t HashSize;
- uint8_t *HashBuf = nullptr;
-};
-
-template <class ELFT> class BuildIdFnv1 final : public BuildIdSection<ELFT> {
-public:
- BuildIdFnv1() : BuildIdSection<ELFT>(8) {}
- void writeBuildId(ArrayRef<ArrayRef<uint8_t>> Bufs) override;
-};
-
-template <class ELFT> class BuildIdMd5 final : public BuildIdSection<ELFT> {
-public:
- BuildIdMd5() : BuildIdSection<ELFT>(16) {}
- void writeBuildId(ArrayRef<ArrayRef<uint8_t>> Bufs) override;
-};
-
-template <class ELFT> class BuildIdSha1 final : public BuildIdSection<ELFT> {
-public:
- BuildIdSha1() : BuildIdSection<ELFT>(20) {}
- void writeBuildId(ArrayRef<ArrayRef<uint8_t>> Bufs) override;
-};
-
-template <class ELFT>
-class BuildIdHexstring final : public BuildIdSection<ELFT> {
-public:
- BuildIdHexstring();
- void writeBuildId(ArrayRef<ArrayRef<uint8_t>> Bufs) override;
-};
-
// All output sections that are hadnled by the linker specially are
// globally accessible. Writer initializes them, so don't use them
// until Writer is initialized.
template <class ELFT> struct Out {
typedef typename ELFT::uint uintX_t;
typedef typename ELFT::Phdr Elf_Phdr;
- static BuildIdSection<ELFT> *BuildId;
- static DynamicSection<ELFT> *Dynamic;
- static EhFrameHeader<ELFT> *EhFrameHdr;
+
+ static uint8_t First;
static EhOutputSection<ELFT> *EhFrame;
- static GnuHashTableSection<ELFT> *GnuHashTab;
- static GotPltSection<ELFT> *GotPlt;
- static GotSection<ELFT> *Got;
- static HashTableSection<ELFT> *HashTab;
- static InterpSection<ELFT> *Interp;
static OutputSection<ELFT> *Bss;
- static OutputSection<ELFT> *MipsRldMap;
- static OutputSectionBase<ELFT> *Opd;
+ static OutputSectionBase *Opd;
static uint8_t *OpdBuf;
- static PltSection<ELFT> *Plt;
- static RelocationSection<ELFT> *RelaDyn;
- static RelocationSection<ELFT> *RelaPlt;
- static StringTableSection<ELFT> *DynStrTab;
- static StringTableSection<ELFT> *ShStrTab;
- static StringTableSection<ELFT> *StrTab;
- static SymbolTableSection<ELFT> *DynSymTab;
- static SymbolTableSection<ELFT> *SymTab;
- static VersionDefinitionSection<ELFT> *VerDef;
- static VersionTableSection<ELFT> *VerSym;
- static VersionNeedSection<ELFT> *VerNeed;
- static Elf_Phdr *TlsPhdr;
- static OutputSectionBase<ELFT> *ElfHeader;
- static OutputSectionBase<ELFT> *ProgramHeaders;
+ static PhdrEntry *TlsPhdr;
+ static OutputSectionBase *DebugInfo;
+ static OutputSectionBase *ElfHeader;
+ static OutputSectionBase *ProgramHeaders;
+ static OutputSectionBase *PreinitArray;
+ static OutputSectionBase *InitArray;
+ static OutputSectionBase *FiniArray;
};
template <bool Is64Bits> struct SectionKey {
@@ -676,45 +235,33 @@ template <class ELFT> class OutputSectionFactory {
typedef typename elf::SectionKey<ELFT::Is64Bits> Key;
public:
- std::pair<OutputSectionBase<ELFT> *, bool> create(InputSectionBase<ELFT> *C,
- StringRef OutsecName);
-
- OutputSectionBase<ELFT> *lookup(StringRef Name, uint32_t Type, uintX_t Flags);
+ std::pair<OutputSectionBase *, bool> create(InputSectionBase<ELFT> *C,
+ StringRef OutsecName);
+ std::pair<OutputSectionBase *, bool>
+ create(const SectionKey<ELFT::Is64Bits> &Key, InputSectionBase<ELFT> *C);
private:
- Key createKey(InputSectionBase<ELFT> *C, StringRef OutsecName);
-
- llvm::SmallDenseMap<Key, OutputSectionBase<ELFT> *> Map;
+ llvm::SmallDenseMap<Key, OutputSectionBase *> Map;
};
-template <class ELFT> BuildIdSection<ELFT> *Out<ELFT>::BuildId;
-template <class ELFT> DynamicSection<ELFT> *Out<ELFT>::Dynamic;
-template <class ELFT> EhFrameHeader<ELFT> *Out<ELFT>::EhFrameHdr;
+template <class ELFT> uint64_t getHeaderSize() {
+ if (Config->OFormatBinary)
+ return 0;
+ return Out<ELFT>::ElfHeader->Size + Out<ELFT>::ProgramHeaders->Size;
+}
+
+template <class ELFT> uint8_t Out<ELFT>::First;
template <class ELFT> EhOutputSection<ELFT> *Out<ELFT>::EhFrame;
-template <class ELFT> GnuHashTableSection<ELFT> *Out<ELFT>::GnuHashTab;
-template <class ELFT> GotPltSection<ELFT> *Out<ELFT>::GotPlt;
-template <class ELFT> GotSection<ELFT> *Out<ELFT>::Got;
-template <class ELFT> HashTableSection<ELFT> *Out<ELFT>::HashTab;
-template <class ELFT> InterpSection<ELFT> *Out<ELFT>::Interp;
template <class ELFT> OutputSection<ELFT> *Out<ELFT>::Bss;
-template <class ELFT> OutputSection<ELFT> *Out<ELFT>::MipsRldMap;
-template <class ELFT> OutputSectionBase<ELFT> *Out<ELFT>::Opd;
+template <class ELFT> OutputSectionBase *Out<ELFT>::Opd;
template <class ELFT> uint8_t *Out<ELFT>::OpdBuf;
-template <class ELFT> PltSection<ELFT> *Out<ELFT>::Plt;
-template <class ELFT> RelocationSection<ELFT> *Out<ELFT>::RelaDyn;
-template <class ELFT> RelocationSection<ELFT> *Out<ELFT>::RelaPlt;
-template <class ELFT> StringTableSection<ELFT> *Out<ELFT>::DynStrTab;
-template <class ELFT> StringTableSection<ELFT> *Out<ELFT>::ShStrTab;
-template <class ELFT> StringTableSection<ELFT> *Out<ELFT>::StrTab;
-template <class ELFT> SymbolTableSection<ELFT> *Out<ELFT>::DynSymTab;
-template <class ELFT> SymbolTableSection<ELFT> *Out<ELFT>::SymTab;
-template <class ELFT> VersionDefinitionSection<ELFT> *Out<ELFT>::VerDef;
-template <class ELFT> VersionTableSection<ELFT> *Out<ELFT>::VerSym;
-template <class ELFT> VersionNeedSection<ELFT> *Out<ELFT>::VerNeed;
-template <class ELFT> typename ELFT::Phdr *Out<ELFT>::TlsPhdr;
-template <class ELFT> OutputSectionBase<ELFT> *Out<ELFT>::ElfHeader;
-template <class ELFT> OutputSectionBase<ELFT> *Out<ELFT>::ProgramHeaders;
-
+template <class ELFT> PhdrEntry *Out<ELFT>::TlsPhdr;
+template <class ELFT> OutputSectionBase *Out<ELFT>::DebugInfo;
+template <class ELFT> OutputSectionBase *Out<ELFT>::ElfHeader;
+template <class ELFT> OutputSectionBase *Out<ELFT>::ProgramHeaders;
+template <class ELFT> OutputSectionBase *Out<ELFT>::PreinitArray;
+template <class ELFT> OutputSectionBase *Out<ELFT>::InitArray;
+template <class ELFT> OutputSectionBase *Out<ELFT>::FiniArray;
} // namespace elf
} // namespace lld
diff --git a/ELF/Relocations.cpp b/ELF/Relocations.cpp
index c09cf6b2b1ef..f7dcc5d24e93 100644
--- a/ELF/Relocations.cpp
+++ b/ELF/Relocations.cpp
@@ -44,7 +44,9 @@
#include "Relocations.h"
#include "Config.h"
#include "OutputSections.h"
+#include "Strings.h"
#include "SymbolTable.h"
+#include "SyntheticSections.h"
#include "Target.h"
#include "Thunks.h"
@@ -60,11 +62,10 @@ namespace lld {
namespace elf {
static bool refersToGotEntry(RelExpr Expr) {
- return Expr == R_GOT || Expr == R_GOT_OFF || Expr == R_MIPS_GOT_LOCAL_PAGE ||
- Expr == R_MIPS_GOT_OFF || Expr == R_MIPS_TLSGD ||
- Expr == R_MIPS_TLSLD || Expr == R_GOT_PAGE_PC || Expr == R_GOT_PC ||
- Expr == R_GOT_FROM_END || Expr == R_TLSGD || Expr == R_TLSGD_PC ||
- Expr == R_TLSDESC || Expr == R_TLSDESC_PAGE;
+ return isRelExprOneOf<R_GOT, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOT_OFF,
+ R_MIPS_GOT_OFF32, R_MIPS_TLSGD, R_MIPS_TLSLD,
+ R_GOT_PAGE_PC, R_GOT_PC, R_GOT_FROM_END, R_TLSGD,
+ R_TLSGD_PC, R_TLSDESC, R_TLSDESC_PAGE>(Expr);
}
static bool isPreemptible(const SymbolBody &Body, uint32_t Type) {
@@ -83,34 +84,46 @@ static bool isPreemptible(const SymbolBody &Body, uint32_t Type) {
return Body.isPreemptible();
}
-// This function is similar to the `handleTlsRelocation`. MIPS does not support
-// any relaxations for TLS relocations so by factoring out MIPS handling into
-// the separate function we can simplify the code and does not pollute
-// `handleTlsRelocation` by MIPS `ifs` statements.
-template <class ELFT>
-static unsigned
-handleMipsTlsRelocation(uint32_t Type, SymbolBody &Body,
- InputSectionBase<ELFT> &C, typename ELFT::uint Offset,
- typename ELFT::uint Addend, RelExpr Expr) {
- if (Expr == R_MIPS_TLSLD) {
- if (Out<ELFT>::Got->addTlsIndex())
- Out<ELFT>::RelaDyn->addReloc({Target->TlsModuleIndexRel, Out<ELFT>::Got,
- Out<ELFT>::Got->getTlsIndexOff(), false,
- nullptr, 0});
- C.Relocations.push_back({Expr, Type, &C, Offset, Addend, &Body});
+// This function is similar to the `handleTlsRelocation`. ARM and MIPS do not
+// support any relaxations for TLS relocations so by factoring out ARM and MIPS
+// handling in to the separate function we can simplify the code and do not
+// pollute `handleTlsRelocation` by ARM and MIPS `ifs` statements.
+template <class ELFT, class GOT>
+static unsigned handleNoRelaxTlsRelocation(
+ GOT *Got, uint32_t Type, SymbolBody &Body, InputSectionBase<ELFT> &C,
+ typename ELFT::uint Offset, typename ELFT::uint Addend, RelExpr Expr) {
+ typedef typename ELFT::uint uintX_t;
+ auto addModuleReloc = [](SymbolBody &Body, GOT *Got, uintX_t Off, bool LD) {
+ // The Dynamic TLS Module Index Relocation can be statically resolved to 1
+ // if we know that we are linking an executable. For ARM we resolve the
+ // relocation when writing the Got. MIPS has a custom Got implementation
+ // that writes the Module index in directly.
+ if (!Body.isPreemptible() && !Config->Pic && Config->EMachine == EM_ARM)
+ Got->Relocations.push_back(
+ {R_ABS, Target->TlsModuleIndexRel, Off, 0, &Body});
+ else {
+ SymbolBody *Dest = LD ? nullptr : &Body;
+ In<ELFT>::RelaDyn->addReloc(
+ {Target->TlsModuleIndexRel, Got, Off, false, Dest, 0});
+ }
+ };
+ if (Expr == R_MIPS_TLSLD || Expr == R_TLSLD_PC) {
+ if (Got->addTlsIndex() && (Config->Pic || Config->EMachine == EM_ARM))
+ addModuleReloc(Body, Got, Got->getTlsIndexOff(), true);
+ C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
return 1;
}
if (Target->isTlsGlobalDynamicRel(Type)) {
- if (Out<ELFT>::Got->addDynTlsEntry(Body)) {
- typedef typename ELFT::uint uintX_t;
- uintX_t Off = Out<ELFT>::Got->getGlobalDynOffset(Body);
- Out<ELFT>::RelaDyn->addReloc(
- {Target->TlsModuleIndexRel, Out<ELFT>::Got, Off, false, &Body, 0});
- Out<ELFT>::RelaDyn->addReloc({Target->TlsOffsetRel, Out<ELFT>::Got,
- Off + (uintX_t)sizeof(uintX_t), false,
- &Body, 0});
+ if (Got->addDynTlsEntry(Body) &&
+ (Body.isPreemptible() || Config->EMachine == EM_ARM)) {
+ uintX_t Off = Got->getGlobalDynOffset(Body);
+ addModuleReloc(Body, Got, Off, false);
+ if (Body.isPreemptible())
+ In<ELFT>::RelaDyn->addReloc({Target->TlsOffsetRel, Got,
+ Off + (uintX_t)sizeof(uintX_t), false,
+ &Body, 0});
}
- C.Relocations.push_back({Expr, Type, &C, Offset, Addend, &Body});
+ C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
return 1;
}
return 0;
@@ -122,7 +135,7 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body,
InputSectionBase<ELFT> &C,
typename ELFT::uint Offset,
typename ELFT::uint Addend, RelExpr Expr) {
- if (!(C.getSectionHdr()->sh_flags & SHF_ALLOC))
+ if (!(C.Flags & SHF_ALLOC))
return 0;
if (!Body.isTls())
@@ -130,18 +143,23 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body,
typedef typename ELFT::uint uintX_t;
+ if (Config->EMachine == EM_ARM)
+ return handleNoRelaxTlsRelocation<ELFT>(In<ELFT>::Got, Type, Body, C,
+ Offset, Addend, Expr);
if (Config->EMachine == EM_MIPS)
- return handleMipsTlsRelocation<ELFT>(Type, Body, C, Offset, Addend, Expr);
+ return handleNoRelaxTlsRelocation<ELFT>(In<ELFT>::MipsGot, Type, Body, C,
+ Offset, Addend, Expr);
- if ((Expr == R_TLSDESC || Expr == R_TLSDESC_PAGE || Expr == R_HINT) &&
+ bool IsPreemptible = isPreemptible(Body, Type);
+ if ((Expr == R_TLSDESC || Expr == R_TLSDESC_PAGE || Expr == R_TLSDESC_CALL) &&
Config->Shared) {
- if (Out<ELFT>::Got->addDynTlsEntry(Body)) {
- uintX_t Off = Out<ELFT>::Got->getGlobalDynOffset(Body);
- Out<ELFT>::RelaDyn->addReloc(
- {Target->TlsDescRel, Out<ELFT>::Got, Off, false, &Body, 0});
+ if (In<ELFT>::Got->addDynTlsEntry(Body)) {
+ uintX_t Off = In<ELFT>::Got->getGlobalDynOffset(Body);
+ In<ELFT>::RelaDyn->addReloc({Target->TlsDescRel, In<ELFT>::Got, Off,
+ !IsPreemptible, &Body, 0});
}
- if (Expr != R_HINT)
- C.Relocations.push_back({Expr, Type, &C, Offset, Addend, &Body});
+ if (Expr != R_TLSDESC_CALL)
+ C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
return 1;
}
@@ -149,69 +167,71 @@ static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body,
// Local-Dynamic relocs can be relaxed to Local-Exec.
if (!Config->Shared) {
C.Relocations.push_back(
- {R_RELAX_TLS_LD_TO_LE, Type, &C, Offset, Addend, &Body});
+ {R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Body});
return 2;
}
- if (Out<ELFT>::Got->addTlsIndex())
- Out<ELFT>::RelaDyn->addReloc({Target->TlsModuleIndexRel, Out<ELFT>::Got,
- Out<ELFT>::Got->getTlsIndexOff(), false,
- nullptr, 0});
- C.Relocations.push_back({Expr, Type, &C, Offset, Addend, &Body});
+ if (In<ELFT>::Got->addTlsIndex())
+ In<ELFT>::RelaDyn->addReloc({Target->TlsModuleIndexRel, In<ELFT>::Got,
+ In<ELFT>::Got->getTlsIndexOff(), false,
+ nullptr, 0});
+ C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
return 1;
}
// Local-Dynamic relocs can be relaxed to Local-Exec.
if (Target->isTlsLocalDynamicRel(Type) && !Config->Shared) {
C.Relocations.push_back(
- {R_RELAX_TLS_LD_TO_LE, Type, &C, Offset, Addend, &Body});
+ {R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Body});
return 1;
}
- if (Expr == R_TLSDESC_PAGE || Expr == R_TLSDESC || Expr == R_HINT ||
+ if (Expr == R_TLSDESC_PAGE || Expr == R_TLSDESC || Expr == R_TLSDESC_CALL ||
Target->isTlsGlobalDynamicRel(Type)) {
if (Config->Shared) {
- if (Out<ELFT>::Got->addDynTlsEntry(Body)) {
- uintX_t Off = Out<ELFT>::Got->getGlobalDynOffset(Body);
- Out<ELFT>::RelaDyn->addReloc(
- {Target->TlsModuleIndexRel, Out<ELFT>::Got, Off, false, &Body, 0});
+ if (In<ELFT>::Got->addDynTlsEntry(Body)) {
+ uintX_t Off = In<ELFT>::Got->getGlobalDynOffset(Body);
+ In<ELFT>::RelaDyn->addReloc(
+ {Target->TlsModuleIndexRel, In<ELFT>::Got, Off, false, &Body, 0});
// If the symbol is preemptible we need the dynamic linker to write
// the offset too.
- if (isPreemptible(Body, Type))
- Out<ELFT>::RelaDyn->addReloc({Target->TlsOffsetRel, Out<ELFT>::Got,
- Off + (uintX_t)sizeof(uintX_t), false,
- &Body, 0});
+ uintX_t OffsetOff = Off + (uintX_t)sizeof(uintX_t);
+ if (IsPreemptible)
+ In<ELFT>::RelaDyn->addReloc({Target->TlsOffsetRel, In<ELFT>::Got,
+ OffsetOff, false, &Body, 0});
+ else
+ In<ELFT>::Got->Relocations.push_back(
+ {R_ABS, Target->TlsOffsetRel, OffsetOff, 0, &Body});
}
- C.Relocations.push_back({Expr, Type, &C, Offset, Addend, &Body});
+ C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
return 1;
}
// Global-Dynamic relocs can be relaxed to Initial-Exec or Local-Exec
// depending on the symbol being locally defined or not.
- if (isPreemptible(Body, Type)) {
+ if (IsPreemptible) {
C.Relocations.push_back(
{Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_IE), Type,
- &C, Offset, Addend, &Body});
+ Offset, Addend, &Body});
if (!Body.isInGot()) {
- Out<ELFT>::Got->addEntry(Body);
- Out<ELFT>::RelaDyn->addReloc({Target->TlsGotRel, Out<ELFT>::Got,
- Body.getGotOffset<ELFT>(), false, &Body,
- 0});
+ In<ELFT>::Got->addEntry(Body);
+ In<ELFT>::RelaDyn->addReloc({Target->TlsGotRel, In<ELFT>::Got,
+ Body.getGotOffset<ELFT>(), false, &Body,
+ 0});
}
return Target->TlsGdRelaxSkip;
}
C.Relocations.push_back(
- {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_LE), Type, &C,
+ {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_LE), Type,
Offset, Addend, &Body});
return Target->TlsGdRelaxSkip;
}
// Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally
// defined.
- if (Target->isTlsInitialExecRel(Type) && !Config->Shared &&
- !isPreemptible(Body, Type)) {
+ if (Target->isTlsInitialExecRel(Type) && !Config->Shared && !IsPreemptible) {
C.Relocations.push_back(
- {R_RELAX_TLS_IE_TO_LE, Type, &C, Offset, Addend, &Body});
+ {R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Body});
return 1;
}
return 0;
@@ -260,8 +280,8 @@ static int32_t findMipsPairedAddend(const uint8_t *Buf, const uint8_t *BufLoc,
return ((read32<E>(BufLoc) & 0xffff) << 16) +
readSignedLo16<E>(Buf + RI->r_offset);
}
- warning("can't find matching " + getRelName(Type) + " relocation for " +
- getRelName(Rel->getType(Config->Mips64EL)));
+ warn("can't find matching " + toString(Type) + " relocation for " +
+ toString(Rel->getType(Config->Mips64EL)));
return 0;
}
@@ -275,27 +295,34 @@ template <class ELFT> static bool isAbsolute(const SymbolBody &Body) {
return false;
}
+template <class ELFT> static bool isAbsoluteValue(const SymbolBody &Body) {
+ return isAbsolute<ELFT>(Body) || Body.isTls();
+}
+
static bool needsPlt(RelExpr Expr) {
- return Expr == R_PLT_PC || Expr == R_PPC_PLT_OPD || Expr == R_PLT ||
- Expr == R_PLT_PAGE_PC || Expr == R_THUNK_PLT_PC;
+ return isRelExprOneOf<R_PLT_PC, R_PPC_PLT_OPD, R_PLT, R_PLT_PAGE_PC,
+ R_THUNK_PLT_PC>(Expr);
}
// True if this expression is of the form Sym - X, where X is a position in the
// file (PC, or GOT for example).
static bool isRelExpr(RelExpr Expr) {
- return Expr == R_PC || Expr == R_GOTREL || Expr == R_PAGE_PC ||
- Expr == R_RELAX_GOT_PC || Expr == R_THUNK_PC || Expr == R_THUNK_PLT_PC;
+ return isRelExprOneOf<R_PC, R_GOTREL, R_GOTREL_FROM_END, R_MIPS_GOTREL,
+ R_PAGE_PC, R_RELAX_GOT_PC, R_THUNK_PC, R_THUNK_PLT_PC>(
+ Expr);
}
template <class ELFT>
static bool isStaticLinkTimeConstant(RelExpr E, uint32_t Type,
- const SymbolBody &Body) {
+ const SymbolBody &Body,
+ InputSectionBase<ELFT> &S,
+ typename ELFT::uint RelOff) {
// These expressions always compute a constant
- if (E == R_SIZE || E == R_GOT_FROM_END || E == R_GOT_OFF ||
- E == R_MIPS_GOT_LOCAL_PAGE || E == R_MIPS_GOT_OFF || E == R_MIPS_TLSGD ||
- E == R_GOT_PAGE_PC || E == R_GOT_PC || E == R_PLT_PC || E == R_TLSGD_PC ||
- E == R_TLSGD || E == R_PPC_PLT_OPD || E == R_TLSDESC_PAGE ||
- E == R_HINT || E == R_THUNK_PC || E == R_THUNK_PLT_PC)
+ if (isRelExprOneOf<R_SIZE, R_GOT_FROM_END, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE,
+ R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_MIPS_TLSGD,
+ R_GOT_PAGE_PC, R_GOT_PC, R_PLT_PC, R_TLSGD_PC, R_TLSGD,
+ R_PPC_PLT_OPD, R_TLSDESC_CALL, R_TLSDESC_PAGE, R_HINT,
+ R_THUNK_PC, R_THUNK_PLT_PC>(E))
return true;
// These never do, except if the entire file is position dependent or if
@@ -309,7 +336,7 @@ static bool isStaticLinkTimeConstant(RelExpr E, uint32_t Type,
if (!Config->Pic)
return true;
- bool AbsVal = isAbsolute<ELFT>(Body) || Body.isTls();
+ bool AbsVal = isAbsoluteValue<ELFT>(Body);
bool RelE = isRelExpr(E);
if (AbsVal && !RelE)
return true;
@@ -321,11 +348,17 @@ static bool isStaticLinkTimeConstant(RelExpr E, uint32_t Type,
// resolve to the image base. This is a little strange, but it allows us to
// link function calls to such symbols. Normally such a call will be guarded
// with a comparison, which will load a zero from the GOT.
+ // Another special case is MIPS _gp_disp symbol which represents offset
+ // between start of a function and '_gp' value and defined as absolute just
+ // to simplify the code.
if (AbsVal && RelE) {
if (Body.isUndefined() && !Body.isLocal() && Body.symbol()->isWeak())
return true;
- error("relocation " + getRelName(Type) +
- " cannot refer to absolute symbol " + Body.getName());
+ if (&Body == ElfSym<ELFT>::MipsGpDisp)
+ return true;
+ error(S.getLocation(RelOff) + ": relocation " + toString(Type) +
+ " cannot refer to absolute symbol '" + toString(Body) +
+ "' defined in " + toString(Body.File));
return true;
}
@@ -374,18 +407,18 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol<ELFT> *SS) {
// Copy relocation against zero-sized symbol doesn't make sense.
uintX_t SymSize = SS->template getSize<ELFT>();
if (SymSize == 0)
- fatal("cannot create a copy relocation for " + SS->getName());
+ fatal("cannot create a copy relocation for symbol " + toString(*SS));
uintX_t Alignment = getAlignment(SS);
- uintX_t Off = alignTo(Out<ELFT>::Bss->getSize(), Alignment);
- Out<ELFT>::Bss->setSize(Off + SymSize);
+ uintX_t Off = alignTo(Out<ELFT>::Bss->Size, Alignment);
+ Out<ELFT>::Bss->Size = Off + SymSize;
Out<ELFT>::Bss->updateAlignment(Alignment);
uintX_t Shndx = SS->Sym.st_shndx;
uintX_t Value = SS->Sym.st_value;
// Look through the DSO's dynamic symbol table for aliases and create a
// dynamic symbol for each one. This causes the copy relocation to correctly
// interpose any aliases.
- for (const Elf_Sym &S : SS->file()->getElfSymbols(true)) {
+ for (const Elf_Sym &S : SS->file()->getGlobalSymbols()) {
if (S.st_shndx != Shndx || S.st_value != Value)
continue;
auto *Alias = dyn_cast_or_null<SharedSymbol<ELFT>>(
@@ -396,38 +429,43 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol<ELFT> *SS) {
Alias->NeedsCopyOrPltAddr = true;
Alias->symbol()->IsUsedInRegularObj = true;
}
- Out<ELFT>::RelaDyn->addReloc(
+ In<ELFT>::RelaDyn->addReloc(
{Target->CopyRel, Out<ELFT>::Bss, SS->OffsetInBss, false, SS, 0});
}
template <class ELFT>
static RelExpr adjustExpr(const elf::ObjectFile<ELFT> &File, SymbolBody &Body,
bool IsWrite, RelExpr Expr, uint32_t Type,
- const uint8_t *Data) {
+ const uint8_t *Data, InputSectionBase<ELFT> &S,
+ typename ELFT::uint RelOff) {
bool Preemptible = isPreemptible(Body, Type);
if (Body.isGnuIFunc()) {
Expr = toPlt(Expr);
} else if (!Preemptible) {
if (needsPlt(Expr))
Expr = fromPlt(Expr);
- if (Expr == R_GOT_PC)
+ if (Expr == R_GOT_PC && !isAbsoluteValue<ELFT>(Body))
Expr = Target->adjustRelaxExpr(Type, Data, Expr);
}
Expr = Target->getThunkExpr(Expr, Type, File, Body);
- if (IsWrite || isStaticLinkTimeConstant<ELFT>(Expr, Type, Body))
+ if (IsWrite || isStaticLinkTimeConstant<ELFT>(Expr, Type, Body, S, RelOff))
return Expr;
// This relocation would require the dynamic linker to write a value to read
// only memory. We can hack around it if we are producing an executable and
// the refered symbol can be preemepted to refer to the executable.
if (Config->Shared || (Config->Pic && !isRelExpr(Expr))) {
- error("can't create dynamic relocation " + getRelName(Type) +
- " against readonly segment");
+ error(S.getLocation(RelOff) + ": can't create dynamic relocation " +
+ toString(Type) + " against " +
+ (Body.getName().empty() ? "local symbol in readonly segment"
+ : "symbol '" + toString(Body) + "'") +
+ " defined in " + toString(Body.File));
return Expr;
}
if (Body.getVisibility() != STV_DEFAULT) {
- error("cannot preempt symbol");
+ error(S.getLocation(RelOff) + ": cannot preempt symbol '" + toString(Body) +
+ "' defined in " + toString(Body.File));
return Expr;
}
if (Body.isObject()) {
@@ -461,7 +499,8 @@ static RelExpr adjustExpr(const elf::ObjectFile<ELFT> &File, SymbolBody &Body,
Body.NeedsCopyOrPltAddr = true;
return toPlt(Expr);
}
- error("symbol is missing type");
+ error("symbol '" + toString(Body) + "' defined in " + toString(Body.File) +
+ " is missing type");
return Expr;
}
@@ -487,17 +526,49 @@ static typename ELFT::uint computeAddend(const elf::ObjectFile<ELFT> &File,
// For details see p. 4-19 at
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
Addend += 4;
- if (Expr == R_GOTREL) {
- Addend -= MipsGPOffset;
- if (Body.isLocal())
- Addend += File.getMipsGp0();
- }
+ if (Expr == R_MIPS_GOTREL && Body.isLocal())
+ Addend += File.MipsGp0;
}
if (Config->Pic && Config->EMachine == EM_PPC64 && Type == R_PPC64_TOC)
Addend += getPPC64TocBase();
return Addend;
}
+template <class ELFT>
+static void reportUndefined(SymbolBody &Sym, InputSectionBase<ELFT> &S,
+ typename ELFT::uint Offset) {
+ if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore)
+ return;
+
+ if (Config->Shared && Sym.symbol()->Visibility == STV_DEFAULT &&
+ Config->UnresolvedSymbols != UnresolvedPolicy::NoUndef)
+ return;
+
+ std::string Msg =
+ S.getLocation(Offset) + ": undefined symbol '" + toString(Sym) + "'";
+
+ if (Config->UnresolvedSymbols == UnresolvedPolicy::Warn)
+ warn(Msg);
+ else
+ error(Msg);
+}
+
+template <class RelTy>
+static std::pair<uint32_t, uint32_t>
+mergeMipsN32RelTypes(uint32_t Type, uint32_t Offset, RelTy *I, RelTy *E) {
+ // MIPS N32 ABI treats series of successive relocations with the same offset
+ // as a single relocation. The similar approach used by N64 ABI, but this ABI
+ // packs all relocations into the single relocation record. Here we emulate
+ // this for the N32 ABI. Iterate over relocation with the same offset and put
+ // theirs types into the single bit-set.
+ uint32_t Processed = 0;
+ for (; I != E && Offset == I->r_offset; ++I) {
+ ++Processed;
+ Type |= I->getType(Config->Mips64EL) << (8 * Processed);
+ }
+ return std::make_pair(Type, Processed);
+}
+
// The reason we have to do this early scan is as follows
// * To mmap the output file, we need to know the size
// * For that, we need to know how many dynamic relocs we will have.
@@ -515,53 +586,90 @@ template <class ELFT, class RelTy>
static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
typedef typename ELFT::uint uintX_t;
- bool IsWrite = C.getSectionHdr()->sh_flags & SHF_WRITE;
+ bool IsWrite = C.Flags & SHF_WRITE;
auto AddDyn = [=](const DynamicReloc<ELFT> &Reloc) {
- Out<ELFT>::RelaDyn->addReloc(Reloc);
+ In<ELFT>::RelaDyn->addReloc(Reloc);
};
- const elf::ObjectFile<ELFT> &File = *C.getFile();
- ArrayRef<uint8_t> SectionData = C.getSectionData();
+ const elf::ObjectFile<ELFT> *File = C.getFile();
+ ArrayRef<uint8_t> SectionData = C.Data;
const uint8_t *Buf = SectionData.begin();
+
+ ArrayRef<EhSectionPiece> Pieces;
+ if (auto *Eh = dyn_cast<EhInputSection<ELFT>>(&C))
+ Pieces = Eh->Pieces;
+
+ ArrayRef<EhSectionPiece>::iterator PieceI = Pieces.begin();
+ ArrayRef<EhSectionPiece>::iterator PieceE = Pieces.end();
+
for (auto I = Rels.begin(), E = Rels.end(); I != E; ++I) {
const RelTy &RI = *I;
- SymbolBody &Body = File.getRelocTargetSym(RI);
+ SymbolBody &Body = File->getRelocTargetSym(RI);
uint32_t Type = RI.getType(Config->Mips64EL);
+ if (Config->MipsN32Abi) {
+ uint32_t Processed;
+ std::tie(Type, Processed) =
+ mergeMipsN32RelTypes(Type, RI.r_offset, I + 1, E);
+ I += Processed;
+ }
+
+ // We only report undefined symbols if they are referenced somewhere in the
+ // code.
+ if (!Body.isLocal() && Body.isUndefined() && !Body.symbol()->isWeak())
+ reportUndefined(Body, C, RI.r_offset);
+
RelExpr Expr = Target->getRelExpr(Type, Body);
bool Preemptible = isPreemptible(Body, Type);
- Expr = adjustExpr(File, Body, IsWrite, Expr, Type, Buf + RI.r_offset);
- if (HasError)
+ Expr = adjustExpr(*File, Body, IsWrite, Expr, Type, Buf + RI.r_offset, C,
+ RI.r_offset);
+ if (ErrorCount)
continue;
// Skip a relocation that points to a dead piece
- // in a mergeable section.
- if (C.getOffset(RI.r_offset) == (uintX_t)-1)
- continue;
+ // in a eh_frame section.
+ while (PieceI != PieceE &&
+ (PieceI->InputOff + PieceI->size() <= RI.r_offset))
+ ++PieceI;
+
+ // Compute the offset of this section in the output section. We do it here
+ // to try to compute it only once.
+ uintX_t Offset;
+ if (PieceI != PieceE) {
+ assert(PieceI->InputOff <= RI.r_offset && "Relocation not in any piece");
+ if (PieceI->OutputOff == -1)
+ continue;
+ Offset = PieceI->OutputOff + RI.r_offset - PieceI->InputOff;
+ } else {
+ Offset = RI.r_offset;
+ }
// This relocation does not require got entry, but it is relative to got and
// needs it to be created. Here we request for that.
- if (Expr == R_GOTONLY_PC || Expr == R_GOTREL || Expr == R_PPC_TOC)
- Out<ELFT>::Got->HasGotOffRel = true;
+ if (Expr == R_GOTONLY_PC || Expr == R_GOTONLY_PC_FROM_END ||
+ Expr == R_GOTREL || Expr == R_GOTREL_FROM_END || Expr == R_PPC_TOC)
+ In<ELFT>::Got->HasGotOffRel = true;
- uintX_t Addend = computeAddend(File, Buf, E, RI, Expr, Body);
+ uintX_t Addend = computeAddend(*File, Buf, E, RI, Expr, Body);
- if (unsigned Processed = handleTlsRelocation<ELFT>(
- Type, Body, C, RI.r_offset, Addend, Expr)) {
+ if (unsigned Processed =
+ handleTlsRelocation<ELFT>(Type, Body, C, Offset, Addend, Expr)) {
I += (Processed - 1);
continue;
}
- // Ignore "hint" relocation because it is for optional code optimization.
- if (Expr == R_HINT)
+ // Ignore "hint" and TLS Descriptor call relocation because they are
+ // only markers for relaxation.
+ if (isRelExprOneOf<R_HINT, R_TLSDESC_CALL>(Expr))
continue;
- if (needsPlt(Expr) || Expr == R_THUNK_ABS || Expr == R_THUNK_PC ||
- Expr == R_THUNK_PLT_PC || refersToGotEntry(Expr) ||
- !isPreemptible(Body, Type)) {
+ if (needsPlt(Expr) ||
+ isRelExprOneOf<R_THUNK_ABS, R_THUNK_PC, R_THUNK_PLT_PC>(Expr) ||
+ refersToGotEntry(Expr) || !isPreemptible(Body, Type)) {
// If the relocation points to something in the file, we can process it.
- bool Constant = isStaticLinkTimeConstant<ELFT>(Expr, Type, Body);
+ bool Constant =
+ isStaticLinkTimeConstant<ELFT>(Expr, Type, Body, C, RI.r_offset);
// If the output being produced is position independent, the final value
// is still not known. In that case we still need some help from the
@@ -569,17 +677,21 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
// relocation. We can process some of it and and just ask the dynamic
// linker to add the load address.
if (!Constant)
- AddDyn({Target->RelativeRel, &C, RI.r_offset, true, &Body, Addend});
+ AddDyn({Target->RelativeRel, &C, Offset, true, &Body, Addend});
// If the produced value is a constant, we just remember to write it
// when outputting this section. We also have to do it if the format
// uses Elf_Rel, since in that case the written value is the addend.
if (Constant || !RelTy::IsRela)
- C.Relocations.push_back({Expr, Type, &C, RI.r_offset, Addend, &Body});
+ C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
} else {
// We don't know anything about the finaly symbol. Just ask the dynamic
// linker to handle the relocation for us.
- AddDyn({Target->getDynRel(Type), &C, RI.r_offset, false, &Body, Addend});
+ if (!Target->isPicRel(Type))
+ error(C.getLocation(Offset) + ": relocation " + toString(Type) +
+ " cannot be used against shared object; recompile with -fPIC.");
+ AddDyn({Target->getDynRel(Type), &C, Offset, false, &Body, Addend});
+
// MIPS ABI turns using of GOT and dynamic relocations inside out.
// While regular ABI uses dynamic relocations to fill up GOT entries
// MIPS ABI requires dynamic linker to fills up GOT entries using
@@ -596,18 +708,10 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
// a dynamic relocation.
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf p.4-19
if (Config->EMachine == EM_MIPS)
- Out<ELFT>::Got->addMipsEntry(Body, Addend, Expr);
+ In<ELFT>::MipsGot->addEntry(Body, Addend, Expr);
continue;
}
- // Some targets might require creation of thunks for relocations.
- // Now we support only MIPS which requires LA25 thunk to call PIC
- // code from non-PIC one, and ARM which requires interworking.
- if (Expr == R_THUNK_ABS || Expr == R_THUNK_PC || Expr == R_THUNK_PLT_PC) {
- auto *Sec = cast<InputSection<ELFT>>(&C);
- addThunk<ELFT>(Type, Body, *Sec);
- }
-
// At this point we are done with the relocated position. Some relocations
// also require us to create a got or plt entry.
@@ -615,90 +719,107 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
if (needsPlt(Expr)) {
if (Body.isInPlt())
continue;
- Out<ELFT>::Plt->addEntry(Body);
-
- uint32_t Rel;
- if (Body.isGnuIFunc() && !Preemptible)
- Rel = Target->IRelativeRel;
- else
- Rel = Target->PltRel;
- Out<ELFT>::GotPlt->addEntry(Body);
- Out<ELFT>::RelaPlt->addReloc({Rel, Out<ELFT>::GotPlt,
- Body.getGotPltOffset<ELFT>(), !Preemptible,
- &Body, 0});
+ if (Body.isGnuIFunc() && !Preemptible) {
+ In<ELFT>::Iplt->addEntry(Body);
+ In<ELFT>::IgotPlt->addEntry(Body);
+ In<ELFT>::RelaIplt->addReloc({Target->IRelativeRel, In<ELFT>::IgotPlt,
+ Body.getGotPltOffset<ELFT>(),
+ !Preemptible, &Body, 0});
+ } else {
+ In<ELFT>::Plt->addEntry(Body);
+ In<ELFT>::GotPlt->addEntry(Body);
+ In<ELFT>::RelaPlt->addReloc({Target->PltRel, In<ELFT>::GotPlt,
+ Body.getGotPltOffset<ELFT>(), !Preemptible,
+ &Body, 0});
+ }
continue;
}
if (refersToGotEntry(Expr)) {
if (Config->EMachine == EM_MIPS) {
- // MIPS ABI has special rules to process GOT entries
- // and doesn't require relocation entries for them.
+ // MIPS ABI has special rules to process GOT entries and doesn't
+ // require relocation entries for them. A special case is TLS
+ // relocations. In that case dynamic loader applies dynamic
+ // relocations to initialize TLS GOT entries.
// See "Global Offset Table" in Chapter 5 in the following document
// for detailed description:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- Out<ELFT>::Got->addMipsEntry(Body, Addend, Expr);
- if (Body.isTls())
- AddDyn({Target->TlsGotRel, Out<ELFT>::Got, Body.getGotOffset<ELFT>(),
- !Preemptible, &Body, 0});
+ In<ELFT>::MipsGot->addEntry(Body, Addend, Expr);
+ if (Body.isTls() && Body.isPreemptible())
+ AddDyn({Target->TlsGotRel, In<ELFT>::MipsGot,
+ Body.getGotOffset<ELFT>(), false, &Body, 0});
continue;
}
if (Body.isInGot())
continue;
- Out<ELFT>::Got->addEntry(Body);
- if (Preemptible || (Config->Pic && !isAbsolute<ELFT>(Body))) {
- uint32_t DynType;
- if (Body.isTls())
- DynType = Target->TlsGotRel;
- else if (Preemptible)
- DynType = Target->GotRel;
- else
- DynType = Target->RelativeRel;
- AddDyn({DynType, Out<ELFT>::Got, Body.getGotOffset<ELFT>(),
- !Preemptible, &Body, 0});
- }
+ In<ELFT>::Got->addEntry(Body);
+ uintX_t Off = Body.getGotOffset<ELFT>();
+ uint32_t DynType;
+ RelExpr GotRE = R_ABS;
+ if (Body.isTls()) {
+ DynType = Target->TlsGotRel;
+ GotRE = R_TLS;
+ } else if (!Preemptible && Config->Pic && !isAbsolute<ELFT>(Body))
+ DynType = Target->RelativeRel;
+ else
+ DynType = Target->GotRel;
+
+ // FIXME: this logic is almost duplicated above.
+ bool Constant = !Preemptible && !(Config->Pic && !isAbsolute<ELFT>(Body));
+ if (!Constant)
+ AddDyn({DynType, In<ELFT>::Got, Off, !Preemptible, &Body, 0});
+ if (Constant || (!RelTy::IsRela && !Preemptible))
+ In<ELFT>::Got->Relocations.push_back({GotRE, DynType, Off, 0, &Body});
continue;
}
}
}
-template <class ELFT> void scanRelocations(InputSection<ELFT> &C) {
- typedef typename ELFT::Shdr Elf_Shdr;
-
- // Scan all relocations. Each relocation goes through a series
- // of tests to determine if it needs special treatment, such as
- // creating GOT, PLT, copy relocations, etc.
- // Note that relocations for non-alloc sections are directly
- // processed by InputSection::relocateNonAlloc.
- if (C.getSectionHdr()->sh_flags & SHF_ALLOC)
- for (const Elf_Shdr *RelSec : C.RelocSections)
- scanRelocations(C, *RelSec);
+template <class ELFT> void scanRelocations(InputSectionBase<ELFT> &S) {
+ if (S.AreRelocsRela)
+ scanRelocs(S, S.relas());
+ else
+ scanRelocs(S, S.rels());
}
-template <class ELFT>
-void scanRelocations(InputSectionBase<ELFT> &S,
- const typename ELFT::Shdr &RelSec) {
- ELFFile<ELFT> &EObj = S.getFile()->getObj();
- if (RelSec.sh_type == SHT_RELA)
- scanRelocs(S, EObj.relas(&RelSec));
+template <class ELFT, class RelTy>
+static void createThunks(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
+ const elf::ObjectFile<ELFT> *File = C.getFile();
+ for (const RelTy &Rel : Rels) {
+ SymbolBody &Body = File->getRelocTargetSym(Rel);
+ uint32_t Type = Rel.getType(Config->Mips64EL);
+ RelExpr Expr = Target->getRelExpr(Type, Body);
+ if (!isPreemptible(Body, Type) && needsPlt(Expr))
+ Expr = fromPlt(Expr);
+ Expr = Target->getThunkExpr(Expr, Type, *File, Body);
+ // Some targets might require creation of thunks for relocations.
+ // Now we support only MIPS which requires LA25 thunk to call PIC
+ // code from non-PIC one, and ARM which requires interworking.
+ if (Expr == R_THUNK_ABS || Expr == R_THUNK_PC || Expr == R_THUNK_PLT_PC) {
+ auto *Sec = cast<InputSection<ELFT>>(&C);
+ addThunk<ELFT>(Type, Body, *Sec);
+ }
+ }
+}
+
+template <class ELFT> void createThunks(InputSectionBase<ELFT> &S) {
+ if (S.AreRelocsRela)
+ createThunks(S, S.relas());
else
- scanRelocs(S, EObj.rels(&RelSec));
+ createThunks(S, S.rels());
}
-template void scanRelocations<ELF32LE>(InputSection<ELF32LE> &);
-template void scanRelocations<ELF32BE>(InputSection<ELF32BE> &);
-template void scanRelocations<ELF64LE>(InputSection<ELF64LE> &);
-template void scanRelocations<ELF64BE>(InputSection<ELF64BE> &);
-
-template void scanRelocations<ELF32LE>(InputSectionBase<ELF32LE> &,
- const ELF32LE::Shdr &);
-template void scanRelocations<ELF32BE>(InputSectionBase<ELF32BE> &,
- const ELF32BE::Shdr &);
-template void scanRelocations<ELF64LE>(InputSectionBase<ELF64LE> &,
- const ELF64LE::Shdr &);
-template void scanRelocations<ELF64BE>(InputSectionBase<ELF64BE> &,
- const ELF64BE::Shdr &);
+template void scanRelocations<ELF32LE>(InputSectionBase<ELF32LE> &);
+template void scanRelocations<ELF32BE>(InputSectionBase<ELF32BE> &);
+template void scanRelocations<ELF64LE>(InputSectionBase<ELF64LE> &);
+template void scanRelocations<ELF64BE>(InputSectionBase<ELF64BE> &);
+
+template void createThunks<ELF32LE>(InputSectionBase<ELF32LE> &);
+template void createThunks<ELF32BE>(InputSectionBase<ELF32BE> &);
+template void createThunks<ELF64LE>(InputSectionBase<ELF64LE> &);
+template void createThunks<ELF64BE>(InputSectionBase<ELF64BE> &);
}
}
diff --git a/ELF/Relocations.h b/ELF/Relocations.h
index 4c1c74efb0da..b5825bdd5e59 100644
--- a/ELF/Relocations.h
+++ b/ELF/Relocations.h
@@ -15,14 +15,20 @@
namespace lld {
namespace elf {
class SymbolBody;
+class InputSectionData;
template <class ELFT> class InputSection;
template <class ELFT> class InputSectionBase;
+// List of target-independent relocation types. Relocations read
+// from files are converted to these types so that the main code
+// doesn't have to know about architecture-specific details.
enum RelExpr {
R_ABS,
R_GOT,
R_GOTONLY_PC,
+ R_GOTONLY_PC_FROM_END,
R_GOTREL,
+ R_GOTREL_FROM_END,
R_GOT_FROM_END,
R_GOT_OFF,
R_GOT_PAGE_PC,
@@ -30,6 +36,8 @@ enum RelExpr {
R_HINT,
R_MIPS_GOT_LOCAL_PAGE,
R_MIPS_GOT_OFF,
+ R_MIPS_GOT_OFF32,
+ R_MIPS_GOTREL,
R_MIPS_TLSGD,
R_MIPS_TLSLD,
R_NEG_TLS,
@@ -58,25 +66,54 @@ enum RelExpr {
R_TLS,
R_TLSDESC,
R_TLSDESC_PAGE,
+ R_TLSDESC_CALL,
R_TLSGD,
R_TLSGD_PC,
R_TLSLD,
- R_TLSLD_PC
+ R_TLSLD_PC,
};
-template <class ELFT> struct Relocation {
+// Build a bitmask with one bit set for each RelExpr.
+//
+// Constexpr function arguments can't be used in static asserts, so we
+// use template arguments to build the mask.
+// But function template partial specializations don't exist (needed
+// for base case of the recursion), so we need a dummy struct.
+template <RelExpr... Exprs> struct RelExprMaskBuilder {
+ static inline uint64_t build() { return 0; }
+};
+
+// Specialization for recursive case.
+template <RelExpr Head, RelExpr... Tail>
+struct RelExprMaskBuilder<Head, Tail...> {
+ static inline uint64_t build() {
+ static_assert(0 <= Head && Head < 64,
+ "RelExpr is too large for 64-bit mask!");
+ return (uint64_t(1) << Head) | RelExprMaskBuilder<Tail...>::build();
+ }
+};
+
+// Return true if `Expr` is one of `Exprs`.
+// There are fewer than 64 RelExpr's, so we can represent any set of
+// RelExpr's as a constant bit mask and test for membership with a
+// couple cheap bitwise operations.
+template <RelExpr... Exprs> bool isRelExprOneOf(RelExpr Expr) {
+ assert(0 <= Expr && (int)Expr < 64 && "RelExpr is too large for 64-bit mask!");
+ return (uint64_t(1) << Expr) & RelExprMaskBuilder<Exprs...>::build();
+}
+
+// Architecture-neutral representation of relocation.
+struct Relocation {
RelExpr Expr;
uint32_t Type;
- InputSectionBase<ELFT> *InputSec;
uint64_t Offset;
uint64_t Addend;
SymbolBody *Sym;
};
-template <class ELFT> void scanRelocations(InputSection<ELFT> &);
+template <class ELFT> void scanRelocations(InputSectionBase<ELFT> &);
-template <class ELFT>
-void scanRelocations(InputSectionBase<ELFT> &, const typename ELFT::Shdr &);
+template <class ELFT> void createThunks(InputSectionBase<ELFT> &);
template <class ELFT>
static inline typename ELFT::uint getAddend(const typename ELFT::Rel &Rel) {
diff --git a/ELF/ScriptParser.cpp b/ELF/ScriptParser.cpp
index 559ec1be0e39..c740685a15a1 100644
--- a/ELF/ScriptParser.cpp
+++ b/ELF/ScriptParser.cpp
@@ -20,69 +20,101 @@ using namespace llvm;
using namespace lld;
using namespace lld::elf;
-// Returns the line that the character S[Pos] is in.
-static StringRef getLine(StringRef S, size_t Pos) {
- size_t Begin = S.rfind('\n', Pos);
- size_t End = S.find('\n', Pos);
- Begin = (Begin == StringRef::npos) ? 0 : Begin + 1;
- if (End == StringRef::npos)
- End = S.size();
- // rtrim for DOS-style newlines.
- return S.substr(Begin, End - Begin).rtrim();
+// Returns a whole line containing the current token.
+StringRef ScriptParserBase::getLine() {
+ StringRef S = getCurrentMB().getBuffer();
+ StringRef Tok = Tokens[Pos - 1];
+
+ size_t Pos = S.rfind('\n', Tok.data() - S.data());
+ if (Pos != StringRef::npos)
+ S = S.substr(Pos + 1);
+ return S.substr(0, S.find_first_of("\r\n"));
+}
+
+// Returns 1-based line number of the current token.
+size_t ScriptParserBase::getLineNumber() {
+ StringRef S = getCurrentMB().getBuffer();
+ StringRef Tok = Tokens[Pos - 1];
+ return S.substr(0, Tok.data() - S.data()).count('\n') + 1;
}
-void ScriptParserBase::printErrorPos() {
- StringRef Tok = Tokens[Pos == 0 ? 0 : Pos - 1];
- StringRef Line = getLine(Input, Tok.data() - Input.data());
- size_t Col = Tok.data() - Line.data();
- error(Line);
- error(std::string(Col, ' ') + "^");
+// Returns 0-based column number of the current token.
+size_t ScriptParserBase::getColumnNumber() {
+ StringRef Tok = Tokens[Pos - 1];
+ return Tok.data() - getLine().data();
}
+std::string ScriptParserBase::getCurrentLocation() {
+ std::string Filename = getCurrentMB().getBufferIdentifier();
+ if (!Pos)
+ return Filename;
+ return (Filename + ":" + Twine(getLineNumber())).str();
+}
+
+ScriptParserBase::ScriptParserBase(MemoryBufferRef MB) { tokenize(MB); }
+
// We don't want to record cascading errors. Keep only the first one.
void ScriptParserBase::setError(const Twine &Msg) {
if (Error)
return;
- if (Input.empty() || Tokens.empty()) {
- error(Msg);
- } else {
- error("line " + Twine(getPos()) + ": " + Msg);
- printErrorPos();
- }
Error = true;
+
+ if (!Pos) {
+ error(getCurrentLocation() + ": " + Msg);
+ return;
+ }
+
+ std::string S = getCurrentLocation() + ": ";
+ error(S + Msg);
+ error(S + getLine());
+ error(S + std::string(getColumnNumber(), ' ') + "^");
}
// Split S into linker script tokens.
-std::vector<StringRef> ScriptParserBase::tokenize(StringRef S) {
- std::vector<StringRef> Ret;
+void ScriptParserBase::tokenize(MemoryBufferRef MB) {
+ std::vector<StringRef> Vec;
+ MBs.push_back(MB);
+ StringRef S = MB.getBuffer();
+ StringRef Begin = S;
+
for (;;) {
S = skipSpace(S);
if (S.empty())
- return Ret;
+ break;
- // Quoted token
+ // Quoted token. Note that double-quote characters are parts of a token
+ // because, in a glob match context, only unquoted tokens are interpreted
+ // as glob patterns. Double-quoted tokens are literal patterns in that
+ // context.
if (S.startswith("\"")) {
size_t E = S.find("\"", 1);
if (E == StringRef::npos) {
- error("unclosed quote");
- return {};
+ StringRef Filename = MB.getBufferIdentifier();
+ size_t Lineno = Begin.substr(0, S.data() - Begin.data()).count('\n');
+ error(Filename + ":" + Twine(Lineno + 1) + ": unclosed quote");
+ return;
}
- Ret.push_back(S.substr(1, E - 1));
+
+ Vec.push_back(S.take_front(E + 1));
S = S.substr(E + 1);
continue;
}
- // Unquoted token
+ // Unquoted token. This is more relaxed than tokens in C-like language,
+ // so that you can write "file-name.cpp" as one bare token, for example.
size_t Pos = S.find_first_not_of(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
- "0123456789_.$/\\~=+[]*?-:!<>");
+ "0123456789_.$/\\~=+[]*?-:!<>^");
+
// A character that cannot start a word (which is usually a
// punctuation) forms a single character token.
if (Pos == 0)
Pos = 1;
- Ret.push_back(S.substr(0, Pos));
+ Vec.push_back(S.substr(0, Pos));
S = S.substr(Pos);
}
+
+ Tokens.insert(Tokens.begin() + Pos, Vec.begin(), Vec.end());
}
// Skip leading whitespace characters or comments.
@@ -132,19 +164,16 @@ StringRef ScriptParserBase::peek() {
return Tok;
}
-bool ScriptParserBase::skip(StringRef Tok) {
- if (Error)
- return false;
- if (atEOF()) {
- setError("unexpected EOF");
- return false;
+bool ScriptParserBase::consume(StringRef Tok) {
+ if (peek() == Tok) {
+ skip();
+ return true;
}
- if (Tokens[Pos] != Tok)
- return false;
- ++Pos;
- return true;
+ return false;
}
+void ScriptParserBase::skip() { (void)next(); }
+
void ScriptParserBase::expect(StringRef Expect) {
if (Error)
return;
@@ -153,11 +182,19 @@ void ScriptParserBase::expect(StringRef Expect) {
setError(Expect + " expected, but got " + Tok);
}
-// Returns the current line number.
-size_t ScriptParserBase::getPos() {
- if (Pos == 0)
- return 1;
- const char *Begin = Input.data();
- const char *Tok = Tokens[Pos - 1].data();
- return StringRef(Begin, Tok - Begin).count('\n') + 1;
+// Returns true if S encloses T.
+static bool encloses(StringRef S, StringRef T) {
+ return S.bytes_begin() <= T.bytes_begin() && T.bytes_end() <= S.bytes_end();
+}
+
+MemoryBufferRef ScriptParserBase::getCurrentMB() {
+ // Find input buffer containing the current token.
+ assert(!MBs.empty());
+ if (!Pos)
+ return MBs[0];
+
+ for (MemoryBufferRef MB : MBs)
+ if (encloses(MB.getBuffer(), Tokens[Pos - 1]))
+ return MB;
+ llvm_unreachable("getCurrentMB: failed to find a token");
}
diff --git a/ELF/ScriptParser.h b/ELF/ScriptParser.h
index 20735f78da81..264c49792337 100644
--- a/ELF/ScriptParser.h
+++ b/ELF/ScriptParser.h
@@ -12,6 +12,7 @@
#include "lld/Core/LLVM.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/MemoryBuffer.h"
#include <utility>
#include <vector>
@@ -20,27 +21,30 @@ namespace elf {
class ScriptParserBase {
public:
- explicit ScriptParserBase(StringRef S) : Input(S), Tokens(tokenize(S)) {}
- explicit ScriptParserBase(std::vector<StringRef> Tokens)
- : Input(""), Tokens(std::move(Tokens)) {}
+ explicit ScriptParserBase(MemoryBufferRef MB);
-protected:
void setError(const Twine &Msg);
- static std::vector<StringRef> tokenize(StringRef S);
+ void tokenize(MemoryBufferRef MB);
static StringRef skipSpace(StringRef S);
bool atEOF();
StringRef next();
StringRef peek();
- bool skip(StringRef Tok);
+ void skip();
+ bool consume(StringRef Tok);
void expect(StringRef Expect);
+ std::string getCurrentLocation();
- size_t getPos();
- void printErrorPos();
-
- StringRef Input;
+ std::vector<MemoryBufferRef> MBs;
std::vector<StringRef> Tokens;
size_t Pos = 0;
bool Error = false;
+
+private:
+ StringRef getLine();
+ size_t getLineNumber();
+ size_t getColumnNumber();
+
+ MemoryBufferRef getCurrentMB();
};
} // namespace elf
diff --git a/ELF/Strings.cpp b/ELF/Strings.cpp
index 0c21e8819d6c..ec3d1f1b2b51 100644
--- a/ELF/Strings.cpp
+++ b/ELF/Strings.cpp
@@ -8,44 +8,59 @@
//===----------------------------------------------------------------------===//
#include "Strings.h"
+#include "Config.h"
#include "Error.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
-#include "llvm/Config/config.h"
+#include "llvm/Demangle/Demangle.h"
#include <algorithm>
-
-#ifdef HAVE_CXXABI_H
-#include <cxxabi.h>
-#endif
+#include <cstring>
using namespace llvm;
using namespace lld;
using namespace lld::elf;
-// Returns true if S matches T. S can contain glob meta-characters.
-// The asterisk ('*') matches zero or more characters, and the question
-// mark ('?') matches one character.
-bool elf::globMatch(StringRef S, StringRef T) {
- for (;;) {
- if (S.empty())
- return T.empty();
- if (S[0] == '*') {
- S = S.substr(1);
- if (S.empty())
- // Fast path. If a pattern is '*', it matches anything.
- return true;
- for (size_t I = 0, E = T.size(); I < E; ++I)
- if (globMatch(S, T.substr(I)))
- return true;
- return false;
- }
- if (T.empty() || (S[0] != T[0] && S[0] != '?'))
- return false;
- S = S.substr(1);
- T = T.substr(1);
+StringMatcher::StringMatcher(ArrayRef<StringRef> Pat) {
+ for (StringRef S : Pat) {
+ Expected<GlobPattern> Pat = GlobPattern::create(S);
+ if (!Pat)
+ error(toString(Pat.takeError()));
+ else
+ Patterns.push_back(*Pat);
}
}
+bool StringMatcher::match(StringRef S) const {
+ for (const GlobPattern &Pat : Patterns)
+ if (Pat.match(S))
+ return true;
+ return false;
+}
+
+// If an input string is in the form of "foo.N" where N is a number,
+// return N. Otherwise, returns 65536, which is one greater than the
+// lowest priority.
+int elf::getPriority(StringRef S) {
+ size_t Pos = S.rfind('.');
+ if (Pos == StringRef::npos)
+ return 65536;
+ int V;
+ if (S.substr(Pos + 1).getAsInteger(10, V))
+ return 65536;
+ return V;
+}
+
+bool elf::hasWildcard(StringRef S) {
+ return S.find_first_of("?*[") != StringRef::npos;
+}
+
+StringRef elf::unquote(StringRef S) {
+ if (!S.startswith("\""))
+ return S;
+ return S.substr(1, S.size() - 2);
+}
+
// Converts a hex string (e.g. "deadbeef") to a vector.
std::vector<uint8_t> elf::parseHex(StringRef S) {
std::vector<uint8_t> Hex;
@@ -75,24 +90,19 @@ bool elf::isValidCIdentifier(StringRef S) {
}
// Returns the demangled C++ symbol name for Name.
-std::string elf::demangle(StringRef Name) {
-#if !defined(HAVE_CXXABI_H)
- return Name;
-#else
+Optional<std::string> elf::demangle(StringRef Name) {
// __cxa_demangle can be used to demangle strings other than symbol
// names which do not necessarily start with "_Z". Name can be
// either a C or C++ symbol. Don't call __cxa_demangle if the name
// does not look like a C++ symbol name to avoid getting unexpected
// result for a C symbol that happens to match a mangled type name.
if (!Name.startswith("_Z"))
- return Name;
+ return None;
- char *Buf =
- abi::__cxa_demangle(Name.str().c_str(), nullptr, nullptr, nullptr);
+ char *Buf = itaniumDemangle(Name.str().c_str(), nullptr, nullptr, nullptr);
if (!Buf)
- return Name;
+ return None;
std::string S(Buf);
free(Buf);
return S;
-#endif
}
diff --git a/ELF/Strings.h b/ELF/Strings.h
index 4948e9dbd56b..934b6427105f 100644
--- a/ELF/Strings.h
+++ b/ELF/Strings.h
@@ -7,22 +7,75 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLD_COFF_STRINGS_H
-#define LLD_COFF_STRINGS_H
+#ifndef LLD_ELF_STRINGS_H
+#define LLD_ELF_STRINGS_H
#include "lld/Core/LLVM.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/GlobPattern.h"
#include <vector>
namespace lld {
namespace elf {
-bool globMatch(StringRef S, StringRef T);
+
+int getPriority(StringRef S);
+bool hasWildcard(StringRef S);
std::vector<uint8_t> parseHex(StringRef S);
bool isValidCIdentifier(StringRef S);
+StringRef unquote(StringRef S);
+
+// This is a lazy version of StringRef. String size is computed lazily
+// when it is needed. It is more efficient than StringRef to instantiate
+// if you have a string whose size is unknown.
+//
+// ELF string tables contain a lot of null-terminated strings.
+// Most of them are not necessary for the linker because they are names
+// of local symbols and the linker doesn't use local symbol names for
+// name resolution. So, we use this class to represents strings read
+// from string tables.
+class StringRefZ {
+public:
+ StringRefZ() : Start(nullptr), Size(0) {}
+ StringRefZ(const char *S, size_t Size) : Start(S), Size(Size) {}
+
+ /*implicit*/ StringRefZ(const char *S) : Start(S), Size(-1) {}
+
+ /*implicit*/ StringRefZ(llvm::StringRef S)
+ : Start(S.data()), Size(S.size()) {}
+
+ operator llvm::StringRef() const {
+ if (Size == (size_t)-1)
+ Size = strlen(Start);
+ return {Start, Size};
+ }
+
+private:
+ const char *Start;
+ mutable size_t Size;
+};
+
+// This class represents multiple glob patterns.
+class StringMatcher {
+public:
+ StringMatcher() = default;
+ explicit StringMatcher(ArrayRef<StringRef> Pat);
+
+ bool match(StringRef S) const;
+
+private:
+ std::vector<llvm::GlobPattern> Patterns;
+};
// Returns a demangled C++ symbol name. If Name is not a mangled
-// name or the system does not provide __cxa_demangle function,
-// it returns an unmodified string.
-std::string demangle(StringRef Name);
+// name, it returns Optional::None.
+llvm::Optional<std::string> demangle(StringRef Name);
+
+inline StringRef toStringRef(ArrayRef<uint8_t> Arr) {
+ return {(const char *)Arr.data(), Arr.size()};
+}
}
}
diff --git a/ELF/SymbolListFile.cpp b/ELF/SymbolListFile.cpp
deleted file mode 100644
index 9e088025c1b7..000000000000
--- a/ELF/SymbolListFile.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-//===- SymbolListFile.cpp -------------------------------------------------===//
-//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file contains the parser/evaluator of the linker script.
-// It does not construct an AST but consume linker script directives directly.
-// Results are written to Driver or Config object.
-//
-//===----------------------------------------------------------------------===//
-
-#include "SymbolListFile.h"
-#include "Config.h"
-#include "ScriptParser.h"
-#include "llvm/Support/MemoryBuffer.h"
-
-using namespace llvm;
-using namespace llvm::ELF;
-
-using namespace lld;
-using namespace lld::elf;
-
-// Parse the --dynamic-list argument. A dynamic list is in the form
-//
-// { symbol1; symbol2; [...]; symbolN };
-//
-// Multiple groups can be defined in the same file, and they are merged
-// into a single group.
-
-class DynamicListParser final : public ScriptParserBase {
-public:
- DynamicListParser(StringRef S) : ScriptParserBase(S) {}
- void run();
-};
-
-void DynamicListParser::run() {
- while (!atEOF()) {
- expect("{");
- while (!Error) {
- Config->DynamicList.push_back(next());
- expect(";");
- if (skip("}"))
- break;
- }
- expect(";");
- }
-}
-
-void elf::parseDynamicList(MemoryBufferRef MB) {
- DynamicListParser(MB.getBuffer()).run();
-}
-
-// Parse the --version-script argument. We currently only accept the following
-// version script syntax:
-//
-// { [ global: symbol1; symbol2; [...]; symbolN; ] local: *; };
-//
-// No wildcards are supported, other than for the local entry. Symbol versioning
-// is also not supported.
-
-class VersionScriptParser final : public ScriptParserBase {
-public:
- VersionScriptParser(StringRef S) : ScriptParserBase(S) {}
-
- void run();
-
-private:
- void parseExtern(std::vector<SymbolVersion> *Globals);
- void parseVersion(StringRef VerStr);
- void parseGlobal(StringRef VerStr);
- void parseLocal();
-};
-
-size_t elf::defineSymbolVersion(StringRef VerStr) {
- // Identifiers start at 2 because 0 and 1 are reserved
- // for VER_NDX_LOCAL and VER_NDX_GLOBAL constants.
- size_t VersionId = Config->VersionDefinitions.size() + 2;
- Config->VersionDefinitions.push_back({VerStr, VersionId});
- return VersionId;
-}
-
-void VersionScriptParser::parseVersion(StringRef VerStr) {
- defineSymbolVersion(VerStr);
-
- if (skip("global:") || peek() != "local:")
- parseGlobal(VerStr);
- if (skip("local:"))
- parseLocal();
- expect("}");
-
- // Each version may have a parent version. For example, "Ver2" defined as
- // "Ver2 { global: foo; local: *; } Ver1;" has "Ver1" as a parent. This
- // version hierarchy is, probably against your instinct, purely for human; the
- // runtime doesn't care about them at all. In LLD, we simply skip the token.
- if (!VerStr.empty() && peek() != ";")
- next();
- expect(";");
-}
-
-void VersionScriptParser::parseLocal() {
- Config->DefaultSymbolVersion = VER_NDX_LOCAL;
- expect("*");
- expect(";");
-}
-
-void VersionScriptParser::parseExtern(std::vector<SymbolVersion> *Globals) {
- expect("C++");
- expect("{");
-
- for (;;) {
- if (peek() == "}" || Error)
- break;
- Globals->push_back({next(), true});
- expect(";");
- }
-
- expect("}");
- expect(";");
-}
-
-void VersionScriptParser::parseGlobal(StringRef VerStr) {
- std::vector<SymbolVersion> *Globals;
- if (VerStr.empty())
- Globals = &Config->VersionScriptGlobals;
- else
- Globals = &Config->VersionDefinitions.back().Globals;
-
- for (;;) {
- if (skip("extern"))
- parseExtern(Globals);
-
- StringRef Cur = peek();
- if (Cur == "}" || Cur == "local:" || Error)
- return;
- next();
- Globals->push_back({Cur, false});
- expect(";");
- }
-}
-
-void VersionScriptParser::run() {
- StringRef Msg = "anonymous version definition is used in "
- "combination with other version definitions";
- if (skip("{")) {
- parseVersion("");
- if (!atEOF())
- setError(Msg);
- return;
- }
-
- while (!atEOF() && !Error) {
- StringRef VerStr = next();
- if (VerStr == "{") {
- setError(Msg);
- return;
- }
- expect("{");
- parseVersion(VerStr);
- }
-}
-
-void elf::parseVersionScript(MemoryBufferRef MB) {
- VersionScriptParser(MB.getBuffer()).run();
-}
diff --git a/ELF/SymbolListFile.h b/ELF/SymbolListFile.h
deleted file mode 100644
index cf3c4c639ea4..000000000000
--- a/ELF/SymbolListFile.h
+++ /dev/null
@@ -1,27 +0,0 @@
-//===- SymbolListFile.h -----------------------------------------*- C++ -*-===//
-//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_ELF_SYMBOL_LIST_FILE_H
-#define LLD_ELF_SYMBOL_LIST_FILE_H
-
-#include "lld/Core/LLVM.h"
-#include "llvm/Support/MemoryBuffer.h"
-
-namespace lld {
-namespace elf {
-
-size_t defineSymbolVersion(StringRef Version);
-
-void parseDynamicList(MemoryBufferRef MB);
-void parseVersionScript(MemoryBufferRef MB);
-
-} // namespace elf
-} // namespace lld
-
-#endif
diff --git a/ELF/SymbolTable.cpp b/ELF/SymbolTable.cpp
index 78c1298df427..79097e176e68 100644
--- a/ELF/SymbolTable.cpp
+++ b/ELF/SymbolTable.cpp
@@ -18,11 +18,9 @@
#include "Config.h"
#include "Error.h"
#include "LinkerScript.h"
-#include "Strings.h"
-#include "SymbolListFile.h"
+#include "Memory.h"
#include "Symbols.h"
-#include "llvm/Bitcode/ReaderWriter.h"
-#include "llvm/Support/StringSaver.h"
+#include "llvm/ADT/STLExtras.h"
using namespace llvm;
using namespace llvm::object;
@@ -37,62 +35,69 @@ using namespace lld::elf;
template <class ELFT> static bool isCompatible(InputFile *F) {
if (!isa<ELFFileBase<ELFT>>(F) && !isa<BitcodeFile>(F))
return true;
- if (F->EKind == Config->EKind && F->EMachine == Config->EMachine)
- return true;
- StringRef A = F->getName();
- StringRef B = Config->Emulation;
- if (B.empty())
- B = Config->FirstElf->getName();
- error(A + " is incompatible with " + B);
+
+ if (F->EKind == Config->EKind && F->EMachine == Config->EMachine) {
+ if (Config->EMachine != EM_MIPS)
+ return true;
+ if (isMipsN32Abi(F) == Config->MipsN32Abi)
+ return true;
+ }
+
+ if (!Config->Emulation.empty())
+ error(toString(F) + " is incompatible with " + Config->Emulation);
+ else
+ error(toString(F) + " is incompatible with " + toString(Config->FirstElf));
return false;
}
// Add symbols in File to the symbol table.
-template <class ELFT>
-void SymbolTable<ELFT>::addFile(std::unique_ptr<InputFile> File) {
- InputFile *FileP = File.get();
- if (!isCompatible<ELFT>(FileP))
+template <class ELFT> void SymbolTable<ELFT>::addFile(InputFile *File) {
+ if (!isCompatible<ELFT>(File))
+ return;
+
+ // Binary file
+ if (auto *F = dyn_cast<BinaryFile>(File)) {
+ BinaryFiles.push_back(F);
+ F->parse<ELFT>();
return;
+ }
// .a file
- if (auto *F = dyn_cast<ArchiveFile>(FileP)) {
- ArchiveFiles.emplace_back(cast<ArchiveFile>(File.release()));
+ if (auto *F = dyn_cast<ArchiveFile>(File)) {
F->parse<ELFT>();
return;
}
// Lazy object file
- if (auto *F = dyn_cast<LazyObjectFile>(FileP)) {
- LazyObjectFiles.emplace_back(cast<LazyObjectFile>(File.release()));
+ if (auto *F = dyn_cast<LazyObjectFile>(File)) {
F->parse<ELFT>();
return;
}
if (Config->Trace)
- outs() << getFilename(FileP) << "\n";
+ outs() << toString(File) << "\n";
// .so file
- if (auto *F = dyn_cast<SharedFile<ELFT>>(FileP)) {
+ if (auto *F = dyn_cast<SharedFile<ELFT>>(File)) {
// DSOs are uniquified not by filename but by soname.
F->parseSoName();
- if (!SoNames.insert(F->getSoName()).second)
+ if (ErrorCount || !SoNames.insert(F->getSoName()).second)
return;
-
- SharedFiles.emplace_back(cast<SharedFile<ELFT>>(File.release()));
+ SharedFiles.push_back(F);
F->parseRest();
return;
}
// LLVM bitcode file
- if (auto *F = dyn_cast<BitcodeFile>(FileP)) {
- BitcodeFiles.emplace_back(cast<BitcodeFile>(File.release()));
+ if (auto *F = dyn_cast<BitcodeFile>(File)) {
+ BitcodeFiles.push_back(F);
F->parse<ELFT>(ComdatGroups);
return;
}
// Regular object file
- auto *F = cast<ObjectFile<ELFT>>(FileP);
- ObjectFiles.emplace_back(cast<ObjectFile<ELFT>>(File.release()));
+ auto *F = cast<ObjectFile<ELFT>>(File);
+ ObjectFiles.push_back(F);
F->parse(ComdatGroups);
}
@@ -103,31 +108,30 @@ void SymbolTable<ELFT>::addFile(std::unique_ptr<InputFile> File) {
// using LLVM functions and replaces bitcode symbols with the results.
// Because all bitcode files that consist of a program are passed
// to the compiler at once, it can do whole-program optimization.
-template <class ELFT> void SymbolTable<ELFT>::addCombinedLtoObject() {
+template <class ELFT> void SymbolTable<ELFT>::addCombinedLTOObject() {
if (BitcodeFiles.empty())
return;
- // Compile bitcode files.
- Lto.reset(new BitcodeCompiler);
- for (const std::unique_ptr<BitcodeFile> &F : BitcodeFiles)
- Lto->add(*F);
- std::vector<std::unique_ptr<InputFile>> IFs = Lto->compile();
+ // Compile bitcode files and replace bitcode symbols.
+ LTO.reset(new BitcodeCompiler);
+ for (BitcodeFile *F : BitcodeFiles)
+ LTO->add(*F);
- // Replace bitcode symbols.
- for (auto &IF : IFs) {
- ObjectFile<ELFT> *Obj = cast<ObjectFile<ELFT>>(IF.release());
-
- DenseSet<StringRef> DummyGroups;
+ for (InputFile *File : LTO->compile()) {
+ ObjectFile<ELFT> *Obj = cast<ObjectFile<ELFT>>(File);
+ DenseSet<CachedHashStringRef> DummyGroups;
Obj->parse(DummyGroups);
- ObjectFiles.emplace_back(Obj);
+ ObjectFiles.push_back(Obj);
}
}
template <class ELFT>
DefinedRegular<ELFT> *SymbolTable<ELFT>::addAbsolute(StringRef Name,
- uint8_t Visibility) {
- return cast<DefinedRegular<ELFT>>(
- addRegular(Name, STB_GLOBAL, Visibility)->body());
+ uint8_t Visibility,
+ uint8_t Binding) {
+ Symbol *Sym =
+ addRegular(Name, Visibility, STT_NOTYPE, 0, 0, Binding, nullptr, nullptr);
+ return cast<DefinedRegular<ELFT>>(Sym->body());
}
// Add Name as an "ignored" symbol. An ignored symbol is a regular
@@ -135,7 +139,8 @@ DefinedRegular<ELFT> *SymbolTable<ELFT>::addAbsolute(StringRef Name,
template <class ELFT>
DefinedRegular<ELFT> *SymbolTable<ELFT>::addIgnored(StringRef Name,
uint8_t Visibility) {
- if (!find(Name))
+ SymbolBody *S = find(Name);
+ if (!S || !S->isUndefined())
return nullptr;
return addAbsolute(Name, Visibility);
}
@@ -143,7 +148,7 @@ DefinedRegular<ELFT> *SymbolTable<ELFT>::addIgnored(StringRef Name,
// Set a flag for --trace-symbol so that we can print out a log message
// if a new symbol with the same name is inserted into the symbol table.
template <class ELFT> void SymbolTable<ELFT>::trace(StringRef Name) {
- Symtab.insert({Name, {-1, true}});
+ Symtab.insert({CachedHashStringRef(Name), {-1, true}});
}
// Rename SYM as __wrap_SYM. The original symbol is preserved as __real_SYM.
@@ -152,10 +157,10 @@ template <class ELFT> void SymbolTable<ELFT>::wrap(StringRef Name) {
SymbolBody *B = find(Name);
if (!B)
return;
- StringSaver Saver(Alloc);
Symbol *Sym = B->symbol();
Symbol *Real = addUndefined(Saver.save("__real_" + Name));
Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name));
+
// We rename symbols by replacing the old symbol's SymbolBody with the new
// symbol's SymbolBody. This causes all SymbolBody pointers referring to the
// old symbol to instead refer to the new symbol.
@@ -174,24 +179,26 @@ static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) {
// Find an existing symbol or create and insert a new one.
template <class ELFT>
std::pair<Symbol *, bool> SymbolTable<ELFT>::insert(StringRef Name) {
- auto P = Symtab.insert({Name, {(int)SymVector.size(), false}});
+ auto P = Symtab.insert(
+ {CachedHashStringRef(Name), SymIndex((int)SymVector.size(), false)});
SymIndex &V = P.first->second;
bool IsNew = P.second;
if (V.Idx == -1) {
IsNew = true;
- V = {(int)SymVector.size(), true};
+ V = SymIndex((int)SymVector.size(), true);
}
Symbol *Sym;
if (IsNew) {
- Sym = new (Alloc) Symbol;
+ Sym = new (BAlloc) Symbol;
+ Sym->InVersionScript = false;
Sym->Binding = STB_WEAK;
Sym->Visibility = STV_DEFAULT;
Sym->IsUsedInRegularObj = false;
Sym->ExportDynamic = false;
- Sym->VersionId = Config->DefaultSymbolVersion;
Sym->Traced = V.Traced;
+ Sym->VersionId = Config->DefaultSymbolVersion;
SymVector.push_back(Sym);
} else {
Sym = SymVector[V.Idx];
@@ -199,13 +206,20 @@ std::pair<Symbol *, bool> SymbolTable<ELFT>::insert(StringRef Name) {
return {Sym, IsNew};
}
+// Construct a string in the form of "Sym in File1 and File2".
+// Used to construct an error message.
+static std::string conflictMsg(SymbolBody *Existing, InputFile *NewFile) {
+ return "'" + toString(*Existing) + "' in " + toString(Existing->File) +
+ " and " + toString(NewFile);
+}
+
// Find an existing symbol or create and insert a new one, then apply the given
// attributes.
template <class ELFT>
std::pair<Symbol *, bool>
SymbolTable<ELFT>::insert(StringRef Name, uint8_t Type, uint8_t Visibility,
- bool CanOmitFromDynSym, bool IsUsedInRegularObj,
- InputFile *File) {
+ bool CanOmitFromDynSym, InputFile *File) {
+ bool IsUsedInRegularObj = !File || File->kind() == InputFile::ObjectKind;
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
@@ -218,42 +232,31 @@ SymbolTable<ELFT>::insert(StringRef Name, uint8_t Type, uint8_t Visibility,
S->IsUsedInRegularObj = true;
if (!WasInserted && S->body()->Type != SymbolBody::UnknownType &&
((Type == STT_TLS) != S->body()->isTls()))
- error("TLS attribute mismatch for symbol: " +
- conflictMsg(S->body(), File));
+ error("TLS attribute mismatch for symbol " + conflictMsg(S->body(), File));
return {S, WasInserted};
}
-// Construct a string in the form of "Sym in File1 and File2".
-// Used to construct an error message.
-template <typename ELFT>
-std::string SymbolTable<ELFT>::conflictMsg(SymbolBody *Existing,
- InputFile *NewFile) {
- std::string Sym = Existing->getName();
- if (Config->Demangle)
- Sym = demangle(Sym);
- return Sym + " in " + getFilename(Existing->File) + " and " +
- getFilename(NewFile);
-}
-
template <class ELFT> Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name) {
- return addUndefined(Name, STB_GLOBAL, STV_DEFAULT, /*Type*/ 0,
+ return addUndefined(Name, /*IsLocal=*/false, STB_GLOBAL, STV_DEFAULT,
+ /*Type*/ 0,
/*CanOmitFromDynSym*/ false, /*File*/ nullptr);
}
+static uint8_t getVisibility(uint8_t StOther) { return StOther & 3; }
+
template <class ELFT>
-Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, uint8_t Binding,
- uint8_t StOther, uint8_t Type,
- bool CanOmitFromDynSym,
+Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, bool IsLocal,
+ uint8_t Binding, uint8_t StOther,
+ uint8_t Type, bool CanOmitFromDynSym,
InputFile *File) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) =
- insert(Name, Type, StOther & 3, CanOmitFromDynSym,
- /*IsUsedInRegularObj*/ !File || !isa<BitcodeFile>(File), File);
+ insert(Name, Type, getVisibility(StOther), CanOmitFromDynSym, File);
if (WasInserted) {
S->Binding = Binding;
- replaceBody<Undefined>(S, Name, StOther, Type, File);
+ replaceBody<Undefined>(S, Name, IsLocal, StOther, Type, File);
return S;
}
if (Binding != STB_WEAK) {
@@ -267,8 +270,8 @@ Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, uint8_t Binding,
// its type. See also comment in addLazyArchive.
if (S->isWeak())
L->Type = Type;
- else if (auto F = L->fetch())
- addFile(std::move(F));
+ else if (InputFile *F = L->fetch())
+ addFile(F);
}
return S;
}
@@ -292,17 +295,24 @@ static int compareDefined(Symbol *S, bool WasInserted, uint8_t Binding) {
// We have a new non-common defined symbol with the specified binding. Return 1
// if the new symbol should win, -1 if the new symbol should lose, or 0 if there
// is a conflict. If the new symbol wins, also update the binding.
-static int compareDefinedNonCommon(Symbol *S, bool WasInserted, uint8_t Binding) {
+template <typename ELFT>
+static int compareDefinedNonCommon(Symbol *S, bool WasInserted, uint8_t Binding,
+ bool IsAbsolute, typename ELFT::uint Value) {
if (int Cmp = compareDefined(S, WasInserted, Binding)) {
if (Cmp > 0)
S->Binding = Binding;
return Cmp;
}
- if (isa<DefinedCommon>(S->body())) {
+ SymbolBody *B = S->body();
+ if (isa<DefinedCommon>(B)) {
// Non-common symbols take precedence over common symbols.
if (Config->WarnCommon)
- warning("common " + S->body()->getName() + " is overridden");
+ warn("common " + S->body()->getName() + " is overridden");
return 1;
+ } else if (auto *R = dyn_cast<DefinedRegular<ELFT>>(B)) {
+ if (R->Section == nullptr && Binding == STB_GLOBAL && IsAbsolute &&
+ R->Value == Value)
+ return -1;
}
return 0;
}
@@ -314,9 +324,8 @@ Symbol *SymbolTable<ELFT>::addCommon(StringRef N, uint64_t Size,
InputFile *File) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) =
- insert(N, Type, StOther & 3, /*CanOmitFromDynSym*/ false,
- /*IsUsedInRegularObj*/ true, File);
+ std::tie(S, WasInserted) = insert(N, Type, getVisibility(StOther),
+ /*CanOmitFromDynSym*/ false, File);
int Cmp = compareDefined(S, WasInserted, Binding);
if (Cmp > 0) {
S->Binding = Binding;
@@ -326,74 +335,80 @@ Symbol *SymbolTable<ELFT>::addCommon(StringRef N, uint64_t Size,
if (!C) {
// Non-common symbols take precedence over common symbols.
if (Config->WarnCommon)
- warning("common " + S->body()->getName() + " is overridden");
+ warn("common " + S->body()->getName() + " is overridden");
return S;
}
if (Config->WarnCommon)
- warning("multiple common of " + S->body()->getName());
+ warn("multiple common of " + S->body()->getName());
- C->Size = std::max(C->Size, Size);
- C->Alignment = std::max(C->Alignment, Alignment);
+ Alignment = C->Alignment = std::max(C->Alignment, Alignment);
+ if (Size > C->Size)
+ replaceBody<DefinedCommon>(S, N, Size, Alignment, StOther, Type, File);
}
return S;
}
-template <class ELFT>
-void SymbolTable<ELFT>::reportDuplicate(SymbolBody *Existing,
- InputFile *NewFile) {
- std::string Msg = "duplicate symbol: " + conflictMsg(Existing, NewFile);
+static void print(const Twine &Msg) {
if (Config->AllowMultipleDefinition)
- warning(Msg);
+ warn(Msg);
else
error(Msg);
}
-template <typename ELFT>
-Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, const Elf_Sym &Sym,
- InputSectionBase<ELFT> *Section) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) =
- insert(Name, Sym.getType(), Sym.getVisibility(),
- /*CanOmitFromDynSym*/ false, /*IsUsedInRegularObj*/ true,
- Section ? Section->getFile() : nullptr);
- int Cmp = compareDefinedNonCommon(S, WasInserted, Sym.getBinding());
- if (Cmp > 0)
- replaceBody<DefinedRegular<ELFT>>(S, Name, Sym, Section);
- else if (Cmp == 0)
- reportDuplicate(S->body(), Section->getFile());
- return S;
+static void reportDuplicate(SymbolBody *Existing, InputFile *NewFile) {
+ print("duplicate symbol " + conflictMsg(Existing, NewFile));
+}
+
+template <class ELFT>
+static void reportDuplicate(SymbolBody *Existing,
+ InputSectionBase<ELFT> *ErrSec,
+ typename ELFT::uint ErrOffset) {
+ DefinedRegular<ELFT> *D = dyn_cast<DefinedRegular<ELFT>>(Existing);
+ if (!D || !D->Section || !ErrSec) {
+ reportDuplicate(Existing, ErrSec ? ErrSec->getFile() : nullptr);
+ return;
+ }
+
+ std::string OldLoc = D->Section->getLocation(D->Value);
+ std::string NewLoc = ErrSec->getLocation(ErrOffset);
+
+ print(NewLoc + ": duplicate symbol '" + toString(*Existing) + "'");
+ print(OldLoc + ": previous definition was here");
}
template <typename ELFT>
-Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, uint8_t Binding,
- uint8_t StOther) {
+Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, uint8_t StOther,
+ uint8_t Type, uintX_t Value, uintX_t Size,
+ uint8_t Binding,
+ InputSectionBase<ELFT> *Section,
+ InputFile *File) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) =
- insert(Name, STT_NOTYPE, StOther & 3, /*CanOmitFromDynSym*/ false,
- /*IsUsedInRegularObj*/ true, nullptr);
- int Cmp = compareDefinedNonCommon(S, WasInserted, Binding);
+ std::tie(S, WasInserted) = insert(Name, Type, getVisibility(StOther),
+ /*CanOmitFromDynSym*/ false, File);
+ int Cmp = compareDefinedNonCommon<ELFT>(S, WasInserted, Binding,
+ Section == nullptr, Value);
if (Cmp > 0)
- replaceBody<DefinedRegular<ELFT>>(S, Name, StOther);
+ replaceBody<DefinedRegular<ELFT>>(S, Name, /*IsLocal=*/false, StOther, Type,
+ Value, Size, Section, File);
else if (Cmp == 0)
- reportDuplicate(S->body(), nullptr);
+ reportDuplicate(S->body(), Section, Value);
return S;
}
template <typename ELFT>
Symbol *SymbolTable<ELFT>::addSynthetic(StringRef N,
- OutputSectionBase<ELFT> *Section,
- uintX_t Value) {
+ const OutputSectionBase *Section,
+ uintX_t Value, uint8_t StOther) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) =
- insert(N, STT_NOTYPE, STV_HIDDEN, /*CanOmitFromDynSym*/ false,
- /*IsUsedInRegularObj*/ true, nullptr);
- int Cmp = compareDefinedNonCommon(S, WasInserted, STB_GLOBAL);
+ std::tie(S, WasInserted) = insert(N, STT_NOTYPE, getVisibility(StOther),
+ /*CanOmitFromDynSym*/ false, nullptr);
+ int Cmp = compareDefinedNonCommon<ELFT>(S, WasInserted, STB_GLOBAL,
+ /*IsAbsolute*/ false, /*Value*/ 0);
if (Cmp > 0)
- replaceBody<DefinedSynthetic<ELFT>>(S, N, Value, Section);
+ replaceBody<DefinedSynthetic>(S, N, Value, Section);
else if (Cmp == 0)
reportDuplicate(S->body(), nullptr);
return S;
@@ -409,11 +424,14 @@ void SymbolTable<ELFT>::addShared(SharedFile<ELFT> *F, StringRef Name,
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) =
- insert(Name, Sym.getType(), STV_DEFAULT, /*CanOmitFromDynSym*/ true,
- /*IsUsedInRegularObj*/ false, F);
+ insert(Name, Sym.getType(), STV_DEFAULT, /*CanOmitFromDynSym*/ true, F);
// Make sure we preempt DSO symbols with default visibility.
- if (Sym.getVisibility() == STV_DEFAULT)
+ if (Sym.getVisibility() == STV_DEFAULT) {
S->ExportDynamic = true;
+ // Exporting preempting symbols takes precedence over linker scripts.
+ if (S->VersionId == VER_NDX_LOCAL)
+ S->VersionId = VER_NDX_GLOBAL;
+ }
if (WasInserted || isa<Undefined>(S->body())) {
replaceBody<SharedSymbol<ELFT>>(S, F, Name, Sym, Verdef);
if (!S->isWeak())
@@ -422,24 +440,25 @@ void SymbolTable<ELFT>::addShared(SharedFile<ELFT> *F, StringRef Name,
}
template <class ELFT>
-Symbol *SymbolTable<ELFT>::addBitcode(StringRef Name, bool IsWeak,
+Symbol *SymbolTable<ELFT>::addBitcode(StringRef Name, uint8_t Binding,
uint8_t StOther, uint8_t Type,
bool CanOmitFromDynSym, BitcodeFile *F) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) = insert(Name, Type, StOther & 3, CanOmitFromDynSym,
- /*IsUsedInRegularObj*/ false, F);
- int Cmp =
- compareDefinedNonCommon(S, WasInserted, IsWeak ? STB_WEAK : STB_GLOBAL);
+ std::tie(S, WasInserted) =
+ insert(Name, Type, getVisibility(StOther), CanOmitFromDynSym, F);
+ int Cmp = compareDefinedNonCommon<ELFT>(S, WasInserted, Binding,
+ /*IsAbs*/ false, /*Value*/ 0);
if (Cmp > 0)
- replaceBody<DefinedBitcode>(S, Name, StOther, Type, F);
+ replaceBody<DefinedRegular<ELFT>>(S, Name, /*IsLocal=*/false, StOther, Type,
+ 0, 0, nullptr, F);
else if (Cmp == 0)
reportDuplicate(S->body(), F);
return S;
}
template <class ELFT> SymbolBody *SymbolTable<ELFT>::find(StringRef Name) {
- auto It = Symtab.find(Name);
+ auto It = Symtab.find(CachedHashStringRef(Name));
if (It == Symtab.end())
return nullptr;
SymIndex V = It->second;
@@ -448,24 +467,13 @@ template <class ELFT> SymbolBody *SymbolTable<ELFT>::find(StringRef Name) {
return SymVector[V.Idx]->body();
}
-// Returns a list of defined symbols that match with a given glob pattern.
-template <class ELFT>
-std::vector<SymbolBody *> SymbolTable<ELFT>::findAll(StringRef Pattern) {
- std::vector<SymbolBody *> Res;
- for (Symbol *Sym : SymVector) {
- SymbolBody *B = Sym->body();
- if (!B->isUndefined() && globMatch(Pattern, B->getName()))
- Res.push_back(B);
- }
- return Res;
-}
-
template <class ELFT>
void SymbolTable<ELFT>::addLazyArchive(ArchiveFile *F,
const object::Archive::Symbol Sym) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) = insert(Sym.getName());
+ StringRef Name = Sym.getName();
+ std::tie(S, WasInserted) = insert(Name);
if (WasInserted) {
replaceBody<LazyArchive>(S, *F, Sym, SymbolBody::UnknownType);
return;
@@ -484,9 +492,9 @@ void SymbolTable<ELFT>::addLazyArchive(ArchiveFile *F,
replaceBody<LazyArchive>(S, *F, Sym, S->body()->Type);
return;
}
- MemoryBufferRef MBRef = F->getMember(&Sym);
- if (!MBRef.getBuffer().empty())
- addFile(createObjectFile(MBRef, F->getName()));
+ std::pair<MemoryBufferRef, uint64_t> MBInfo = F->getMember(&Sym);
+ if (!MBInfo.first.getBuffer().empty())
+ addFile(createObjectFile(MBInfo.first, F->getName(), MBInfo.second));
}
template <class ELFT>
@@ -515,8 +523,8 @@ void SymbolTable<ELFT>::addLazyObject(StringRef Name, LazyObjectFile &Obj) {
template <class ELFT> void SymbolTable<ELFT>::scanUndefinedFlags() {
for (StringRef S : Config->Undefined)
if (auto *L = dyn_cast_or_null<Lazy>(find(S)))
- if (std::unique_ptr<InputFile> File = L->fetch())
- addFile(std::move(File));
+ if (InputFile *File = L->fetch())
+ addFile(File);
}
// This function takes care of the case in which shared libraries depend on
@@ -527,184 +535,173 @@ template <class ELFT> void SymbolTable<ELFT>::scanUndefinedFlags() {
// shared libraries can find them.
// Except this, we ignore undefined symbols in DSOs.
template <class ELFT> void SymbolTable<ELFT>::scanShlibUndefined() {
- for (std::unique_ptr<SharedFile<ELFT>> &File : SharedFiles)
+ for (SharedFile<ELFT> *File : SharedFiles)
for (StringRef U : File->getUndefinedSymbols())
if (SymbolBody *Sym = find(U))
if (Sym->isDefined())
Sym->symbol()->ExportDynamic = true;
}
-// This function process the dynamic list option by marking all the symbols
-// to be exported in the dynamic table.
-template <class ELFT> void SymbolTable<ELFT>::scanDynamicList() {
- for (StringRef S : Config->DynamicList)
- if (SymbolBody *B = find(S))
- B->symbol()->ExportDynamic = true;
+// Initialize DemangledSyms with a map from demangled symbols to symbol
+// objects. Used to handle "extern C++" directive in version scripts.
+//
+// The map will contain all demangled symbols. That can be very large,
+// and in LLD we generally want to avoid do anything for each symbol.
+// Then, why are we doing this? Here's why.
+//
+// Users can use "extern C++ {}" directive to match against demangled
+// C++ symbols. For example, you can write a pattern such as
+// "llvm::*::foo(int, ?)". Obviously, there's no way to handle this
+// other than trying to match a pattern against all demangled symbols.
+// So, if "extern C++" feature is used, we need to demangle all known
+// symbols.
+template <class ELFT>
+StringMap<std::vector<SymbolBody *>> &SymbolTable<ELFT>::getDemangledSyms() {
+ if (!DemangledSyms) {
+ DemangledSyms.emplace();
+ for (Symbol *Sym : SymVector) {
+ SymbolBody *B = Sym->body();
+ if (B->isUndefined())
+ continue;
+ if (Optional<std::string> S = demangle(B->getName()))
+ (*DemangledSyms)[*S].push_back(B);
+ else
+ (*DemangledSyms)[B->getName()].push_back(B);
+ }
+ }
+ return *DemangledSyms;
}
-static bool hasWildcard(StringRef S) {
- return S.find_first_of("?*") != StringRef::npos;
+template <class ELFT>
+std::vector<SymbolBody *> SymbolTable<ELFT>::findByVersion(SymbolVersion Ver) {
+ if (Ver.IsExternCpp)
+ return getDemangledSyms().lookup(Ver.Name);
+ if (SymbolBody *B = find(Ver.Name))
+ if (!B->isUndefined())
+ return {B};
+ return {};
}
-static void setVersionId(SymbolBody *Body, StringRef VersionName,
- StringRef Name, uint16_t Version) {
- if (!Body || Body->isUndefined()) {
- if (Config->NoUndefinedVersion)
- error("version script assignment of " + VersionName + " to symbol " +
- Name + " failed: symbol not defined");
- return;
- }
+template <class ELFT>
+std::vector<SymbolBody *>
+SymbolTable<ELFT>::findAllByVersion(SymbolVersion Ver) {
+ std::vector<SymbolBody *> Res;
+ StringMatcher M(Ver.Name);
- Symbol *Sym = Body->symbol();
- if (Sym->VersionId != Config->DefaultSymbolVersion)
- warning("duplicate symbol " + Name + " in version script");
- Sym->VersionId = Version;
-}
+ if (Ver.IsExternCpp) {
+ for (auto &P : getDemangledSyms())
+ if (M.match(P.first()))
+ Res.insert(Res.end(), P.second.begin(), P.second.end());
+ return Res;
+ }
-template <class ELFT>
-std::map<std::string, SymbolBody *> SymbolTable<ELFT>::getDemangledSyms() {
- std::map<std::string, SymbolBody *> Result;
for (Symbol *Sym : SymVector) {
SymbolBody *B = Sym->body();
- Result[demangle(B->getName())] = B;
+ if (!B->isUndefined() && M.match(B->getName()))
+ Res.push_back(B);
}
- return Result;
-}
-
-static bool hasExternCpp() {
- for (VersionDefinition &V : Config->VersionDefinitions)
- for (SymbolVersion Sym : V.Globals)
- if (Sym.IsExternCpp)
- return true;
- return false;
+ return Res;
}
-// This function processes the --version-script option by marking all global
-// symbols with the VersionScriptGlobal flag, which acts as a filter on the
-// dynamic symbol table.
-template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
- // If version script does not contain versions declarations,
- // we just should mark global symbols.
- if (!Config->VersionScriptGlobals.empty()) {
- for (SymbolVersion &Sym : Config->VersionScriptGlobals)
- if (SymbolBody *B = find(Sym.Name))
+// If there's only one anonymous version definition in a version
+// script file, the script does not actually define any symbol version,
+// but just specifies symbols visibilities. We assume that the script was
+// in the form of { global: foo; bar; local *; }. So, local is default.
+// In this function, we make specified symbols global.
+template <class ELFT> void SymbolTable<ELFT>::handleAnonymousVersion() {
+ for (SymbolVersion &Ver : Config->VersionScriptGlobals) {
+ if (Ver.HasWildcard) {
+ for (SymbolBody *B : findAllByVersion(Ver))
B->symbol()->VersionId = VER_NDX_GLOBAL;
- return;
+ continue;
+ }
+ for (SymbolBody *B : findByVersion(Ver))
+ B->symbol()->VersionId = VER_NDX_GLOBAL;
}
+}
- if (Config->VersionDefinitions.empty())
+// Set symbol versions to symbols. This function handles patterns
+// containing no wildcard characters.
+template <class ELFT>
+void SymbolTable<ELFT>::assignExactVersion(SymbolVersion Ver, uint16_t VersionId,
+ StringRef VersionName) {
+ if (Ver.HasWildcard)
return;
- // If we have symbols version declarations, we should
- // assign version references for each symbol.
- // Current rules are:
- // * If there is an exact match for the mangled name or we have extern C++
- // exact match, then we use it.
- // * Otherwise, we look through the wildcard patterns. We look through the
- // version tags in reverse order. We use the first match we find (the last
- // matching version tag in the file).
- // Handle exact matches and build a map of demangled externs for
- // quick search during next step.
- std::map<std::string, SymbolBody *> Demangled;
- if (hasExternCpp())
- Demangled = getDemangledSyms();
-
- for (VersionDefinition &V : Config->VersionDefinitions) {
- for (SymbolVersion Sym : V.Globals) {
- if (hasWildcard(Sym.Name))
- continue;
- SymbolBody *B = Sym.IsExternCpp ? Demangled[Sym.Name] : find(Sym.Name);
- setVersionId(B, V.Name, Sym.Name, V.Id);
- }
+ // Get a list of symbols which we need to assign the version to.
+ std::vector<SymbolBody *> Syms = findByVersion(Ver);
+ if (Syms.empty()) {
+ if (Config->NoUndefinedVersion)
+ error("version script assignment of '" + VersionName + "' to symbol '" +
+ Ver.Name + "' failed: symbol not defined");
+ return;
}
- // Handle wildcards.
- for (size_t I = Config->VersionDefinitions.size() - 1; I != (size_t)-1; --I) {
- VersionDefinition &V = Config->VersionDefinitions[I];
- for (SymbolVersion &Sym : V.Globals)
- if (hasWildcard(Sym.Name))
- for (SymbolBody *B : findAll(Sym.Name))
- if (B->symbol()->VersionId == Config->DefaultSymbolVersion)
- B->symbol()->VersionId = V.Id;
+ // Assign the version.
+ for (SymbolBody *B : Syms) {
+ Symbol *Sym = B->symbol();
+ if (Sym->InVersionScript)
+ warn("duplicate symbol '" + Ver.Name + "' in version script");
+ Sym->VersionId = VersionId;
+ Sym->InVersionScript = true;
}
}
-// Returns the size of the longest version name.
-static int getMaxVersionLen() {
- size_t Len = 0;
- for (VersionDefinition &V : Config->VersionDefinitions)
- Len = std::max(Len, V.Name.size());
- return Len;
-}
-
-// Parses a symbol name in the form of <name>@<version> or <name>@@<version>.
-static std::pair<StringRef, uint16_t>
-getSymbolVersion(SymbolBody *B, int MaxVersionLen) {
- StringRef S = B->getName();
-
- // MaxVersionLen was passed so that we don't need to scan
- // all characters in a symbol name. It is effective because
- // versions are usually short and symbol names can be very long.
- size_t Pos = S.find('@', std::max(0, int(S.size()) - MaxVersionLen - 2));
- if (Pos == 0 || Pos == StringRef::npos)
- return {"", 0};
-
- StringRef Name = S.substr(0, Pos);
- StringRef Verstr = S.substr(Pos + 1);
- if (Verstr.empty())
- return {"", 0};
-
- // '@@' in a symbol name means the default version.
- // It is usually the most recent one.
- bool IsDefault = (Verstr[0] == '@');
- if (IsDefault)
- Verstr = Verstr.substr(1);
-
- for (VersionDefinition &V : Config->VersionDefinitions) {
- if (V.Name == Verstr)
- return {Name, IsDefault ? V.Id : (V.Id | VERSYM_HIDDEN)};
- }
+template <class ELFT>
+void SymbolTable<ELFT>::assignWildcardVersion(SymbolVersion Ver,
+ uint16_t VersionId) {
+ if (!Ver.HasWildcard)
+ return;
+ std::vector<SymbolBody *> Syms = findAllByVersion(Ver);
- // It is an error if the specified version was not defined.
- error("symbol " + S + " has undefined version " + Verstr);
- return {"", 0};
+ // Exact matching takes precendence over fuzzy matching,
+ // so we set a version to a symbol only if no version has been assigned
+ // to the symbol. This behavior is compatible with GNU.
+ for (SymbolBody *B : Syms)
+ if (B->symbol()->VersionId == Config->DefaultSymbolVersion)
+ B->symbol()->VersionId = VersionId;
}
-// Versions are usually assigned to symbols using version scripts,
-// but there's another way to assign versions to symbols.
-// If a symbol name contains '@', the string after it is not
-// actually a part of the symbol name but specifies a version.
-// This function takes care of it.
-template <class ELFT> void SymbolTable<ELFT>::scanSymbolVersions() {
- if (Config->VersionDefinitions.empty())
+// This function processes version scripts by updating VersionId
+// member of symbols.
+template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
+ // Symbol themselves might know their versions because symbols
+ // can contain versions in the form of <name>@<version>.
+ // Let them parse their names.
+ if (!Config->VersionDefinitions.empty())
+ for (Symbol *Sym : SymVector)
+ Sym->body()->parseSymbolVersion();
+
+ // Handle edge cases first.
+ if (!Config->VersionScriptGlobals.empty()) {
+ handleAnonymousVersion();
return;
+ }
- int MaxVersionLen = getMaxVersionLen();
-
- // Unfortunately there's no way other than iterating over all
- // symbols to look for '@' characters in symbol names.
- // So this is inherently slow. A good news is that we do this
- // only when versions have been defined.
- for (Symbol *Sym : SymVector) {
- // Symbol versions for exported symbols are by nature
- // only for defined global symbols.
- SymbolBody *B = Sym->body();
- if (!B->isDefined())
- continue;
- uint8_t Visibility = B->getVisibility();
- if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED)
- continue;
+ if (Config->VersionDefinitions.empty())
+ return;
- // Look for '@' in the symbol name.
- StringRef Name;
- uint16_t Version;
- std::tie(Name, Version) = getSymbolVersion(B, MaxVersionLen);
- if (Name.empty())
- continue;
+ // Now we have version definitions, so we need to set version ids to symbols.
+ // Each version definition has a glob pattern, and all symbols that match
+ // with the pattern get that version.
- B->setName(Name);
- Sym->VersionId = Version;
- }
+ // First, we assign versions to exact matching symbols,
+ // i.e. version definitions not containing any glob meta-characters.
+ for (SymbolVersion &Ver : Config->VersionScriptLocals)
+ assignExactVersion(Ver, VER_NDX_LOCAL, "local");
+ for (VersionDefinition &V : Config->VersionDefinitions)
+ for (SymbolVersion &Ver : V.Globals)
+ assignExactVersion(Ver, V.Id, V.Name);
+
+ // Next, we assign versions to fuzzy matching symbols,
+ // i.e. version definitions containing glob meta-characters.
+ // Note that because the last match takes precedence over previous matches,
+ // we iterate over the definitions in the reverse order.
+ for (SymbolVersion &Ver : Config->VersionScriptLocals)
+ assignWildcardVersion(Ver, VER_NDX_LOCAL);
+ for (VersionDefinition &V : llvm::reverse(Config->VersionDefinitions))
+ for (SymbolVersion &Ver : V.Globals)
+ assignWildcardVersion(Ver, V.Id);
}
template class elf::SymbolTable<ELF32LE>;
diff --git a/ELF/SymbolTable.h b/ELF/SymbolTable.h
index 40415b645a44..1e5a335acc16 100644
--- a/ELF/SymbolTable.h
+++ b/ELF/SymbolTable.h
@@ -12,16 +12,16 @@
#include "InputFiles.h"
#include "LTO.h"
+#include "Strings.h"
+#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseMap.h"
namespace lld {
namespace elf {
class Lazy;
-template <class ELFT> class OutputSectionBase;
+class OutputSectionBase;
struct Symbol;
-typedef llvm::CachedHash<StringRef> SymName;
-
// SymbolTable is a bucket of all known symbols, including defined,
// undefined, or lazy symbols (the last one is symbols in archive
// files whose archive members are not yet loaded).
@@ -39,40 +39,39 @@ template <class ELFT> class SymbolTable {
typedef typename ELFT::uint uintX_t;
public:
- void addFile(std::unique_ptr<InputFile> File);
- void addCombinedLtoObject();
-
- llvm::ArrayRef<Symbol *> getSymbols() const { return SymVector; }
+ void addFile(InputFile *File);
+ void addCombinedLTOObject();
- const std::vector<std::unique_ptr<ObjectFile<ELFT>>> &getObjectFiles() const {
- return ObjectFiles;
- }
-
- const std::vector<std::unique_ptr<SharedFile<ELFT>>> &getSharedFiles() const {
- return SharedFiles;
- }
+ ArrayRef<Symbol *> getSymbols() const { return SymVector; }
+ ArrayRef<ObjectFile<ELFT> *> getObjectFiles() const { return ObjectFiles; }
+ ArrayRef<BinaryFile *> getBinaryFiles() const { return BinaryFiles; }
+ ArrayRef<SharedFile<ELFT> *> getSharedFiles() const { return SharedFiles; }
DefinedRegular<ELFT> *addAbsolute(StringRef Name,
- uint8_t Visibility = llvm::ELF::STV_HIDDEN);
+ uint8_t Visibility = llvm::ELF::STV_HIDDEN,
+ uint8_t Binding = llvm::ELF::STB_GLOBAL);
DefinedRegular<ELFT> *addIgnored(StringRef Name,
uint8_t Visibility = llvm::ELF::STV_HIDDEN);
Symbol *addUndefined(StringRef Name);
- Symbol *addUndefined(StringRef Name, uint8_t Binding, uint8_t StOther,
- uint8_t Type, bool CanOmitFromDynSym, InputFile *File);
-
- Symbol *addRegular(StringRef Name, const Elf_Sym &Sym,
- InputSectionBase<ELFT> *Section);
- Symbol *addRegular(StringRef Name, uint8_t Binding, uint8_t StOther);
- Symbol *addSynthetic(StringRef N, OutputSectionBase<ELFT> *Section,
- uintX_t Value);
+ Symbol *addUndefined(StringRef Name, bool IsLocal, uint8_t Binding,
+ uint8_t StOther, uint8_t Type, bool CanOmitFromDynSym,
+ InputFile *File);
+
+ Symbol *addRegular(StringRef Name, uint8_t StOther, uint8_t Type,
+ uintX_t Value, uintX_t Size, uint8_t Binding,
+ InputSectionBase<ELFT> *Section, InputFile *File);
+
+ Symbol *addSynthetic(StringRef N, const OutputSectionBase *Section,
+ uintX_t Value, uint8_t StOther);
+
void addShared(SharedFile<ELFT> *F, StringRef Name, const Elf_Sym &Sym,
const typename ELFT::Verdef *Verdef);
void addLazyArchive(ArchiveFile *F, const llvm::object::Archive::Symbol S);
void addLazyObject(StringRef Name, LazyObjectFile &Obj);
- Symbol *addBitcode(StringRef Name, bool IsWeak, uint8_t StOther, uint8_t Type,
- bool CanOmitFromDynSym, BitcodeFile *File);
+ Symbol *addBitcode(StringRef Name, uint8_t Binding, uint8_t StOther,
+ uint8_t Type, bool CanOmitFromDynSym, BitcodeFile *File);
Symbol *addCommon(StringRef N, uint64_t Size, uint64_t Alignment,
uint8_t Binding, uint8_t StOther, uint8_t Type,
@@ -80,28 +79,32 @@ public:
void scanUndefinedFlags();
void scanShlibUndefined();
- void scanDynamicList();
void scanVersionScript();
- void scanSymbolVersions();
SymbolBody *find(StringRef Name);
void trace(StringRef Name);
void wrap(StringRef Name);
+ std::vector<InputSectionBase<ELFT> *> Sections;
+
private:
- std::vector<SymbolBody *> findAll(StringRef Pattern);
std::pair<Symbol *, bool> insert(StringRef Name);
std::pair<Symbol *, bool> insert(StringRef Name, uint8_t Type,
uint8_t Visibility, bool CanOmitFromDynSym,
- bool IsUsedInRegularObj, InputFile *File);
+ InputFile *File);
- std::string conflictMsg(SymbolBody *Existing, InputFile *NewFile);
- void reportDuplicate(SymbolBody *Existing, InputFile *NewFile);
+ std::vector<SymbolBody *> findByVersion(SymbolVersion Ver);
+ std::vector<SymbolBody *> findAllByVersion(SymbolVersion Ver);
- std::map<std::string, SymbolBody *> getDemangledSyms();
+ llvm::StringMap<std::vector<SymbolBody *>> &getDemangledSyms();
+ void handleAnonymousVersion();
+ void assignExactVersion(SymbolVersion Ver, uint16_t VersionId,
+ StringRef VersionName);
+ void assignWildcardVersion(SymbolVersion Ver, uint16_t VersionId);
struct SymIndex {
+ SymIndex(int Idx, bool Traced) : Idx(Idx), Traced(Traced) {}
int Idx : 31;
unsigned Traced : 1;
};
@@ -113,26 +116,30 @@ private:
// but a bit inefficient.
// FIXME: Experiment with passing in a custom hashing or sorting the symbols
// once symbol resolution is finished.
- llvm::DenseMap<SymName, SymIndex> Symtab;
+ llvm::DenseMap<llvm::CachedHashStringRef, SymIndex> Symtab;
std::vector<Symbol *> SymVector;
- llvm::BumpPtrAllocator Alloc;
// Comdat groups define "link once" sections. If two comdat groups have the
// same name, only one of them is linked, and the other is ignored. This set
// is used to uniquify them.
- llvm::DenseSet<StringRef> ComdatGroups;
+ llvm::DenseSet<llvm::CachedHashStringRef> ComdatGroups;
- // The symbol table owns all file objects.
- std::vector<std::unique_ptr<ArchiveFile>> ArchiveFiles;
- std::vector<std::unique_ptr<ObjectFile<ELFT>>> ObjectFiles;
- std::vector<std::unique_ptr<LazyObjectFile>> LazyObjectFiles;
- std::vector<std::unique_ptr<SharedFile<ELFT>>> SharedFiles;
- std::vector<std::unique_ptr<BitcodeFile>> BitcodeFiles;
+ std::vector<ObjectFile<ELFT> *> ObjectFiles;
+ std::vector<SharedFile<ELFT> *> SharedFiles;
+ std::vector<BitcodeFile *> BitcodeFiles;
+ std::vector<BinaryFile *> BinaryFiles;
// Set of .so files to not link the same shared object file more than once.
llvm::DenseSet<StringRef> SoNames;
- std::unique_ptr<BitcodeCompiler> Lto;
+ // A map from demangled symbol names to their symbol objects.
+ // This mapping is 1:N because two symbols with different versions
+ // can have the same name. We use this map to handle "extern C++ {}"
+ // directive in version scripts.
+ llvm::Optional<llvm::StringMap<std::vector<SymbolBody *>>> DemangledSyms;
+
+ // For LTO.
+ std::unique_ptr<BitcodeCompiler> LTO;
};
template <class ELFT> struct Symtab { static SymbolTable<ELFT> *X; };
diff --git a/ELF/Symbols.cpp b/ELF/Symbols.cpp
index d6a605d11183..a2133f411c20 100644
--- a/ELF/Symbols.cpp
+++ b/ELF/Symbols.cpp
@@ -12,9 +12,14 @@
#include "InputFiles.h"
#include "InputSection.h"
#include "OutputSections.h"
+#include "Strings.h"
+#include "SyntheticSections.h"
#include "Target.h"
+#include "Writer.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Path.h"
+#include <cstring>
using namespace llvm;
using namespace llvm::object;
@@ -30,27 +35,27 @@ static typename ELFT::uint getSymVA(const SymbolBody &Body,
switch (Body.kind()) {
case SymbolBody::DefinedSyntheticKind: {
- auto &D = cast<DefinedSynthetic<ELFT>>(Body);
- const OutputSectionBase<ELFT> *Sec = D.Section;
+ auto &D = cast<DefinedSynthetic>(Body);
+ const OutputSectionBase *Sec = D.Section;
if (!Sec)
return D.Value;
- if (D.Value == DefinedSynthetic<ELFT>::SectionEnd)
- return Sec->getVA() + Sec->getSize();
- return Sec->getVA() + D.Value;
+ if (D.Value == uintX_t(-1))
+ return Sec->Addr + Sec->Size;
+ return Sec->Addr + D.Value;
}
case SymbolBody::DefinedRegularKind: {
auto &D = cast<DefinedRegular<ELFT>>(Body);
- InputSectionBase<ELFT> *SC = D.Section;
+ InputSectionBase<ELFT> *IS = D.Section;
// According to the ELF spec reference to a local symbol from outside
// the group are not allowed. Unfortunately .eh_frame breaks that rule
// and must be treated specially. For now we just replace the symbol with
// 0.
- if (SC == &InputSection<ELFT>::Discarded)
+ if (IS == &InputSection<ELFT>::Discarded)
return 0;
// This is an absolute symbol.
- if (!SC)
+ if (!IS)
return D.Value;
uintX_t Offset = D.Value;
@@ -58,20 +63,25 @@ static typename ELFT::uint getSymVA(const SymbolBody &Body,
Offset += Addend;
Addend = 0;
}
- uintX_t VA = SC->OutSec->getVA() + SC->getOffset(Offset);
- if (D.isTls())
+ uintX_t VA = (IS->OutSec ? IS->OutSec->Addr : 0) + IS->getOffset(Offset);
+ if (D.isTls() && !Config->Relocatable) {
+ if (!Out<ELFT>::TlsPhdr)
+ fatal(toString(D.File) +
+ " has a STT_TLS symbol but doesn't have a PT_TLS section");
return VA - Out<ELFT>::TlsPhdr->p_vaddr;
+ }
return VA;
}
case SymbolBody::DefinedCommonKind:
- return Out<ELFT>::Bss->getVA() + cast<DefinedCommon>(Body).OffsetInBss;
+ return In<ELFT>::Common->OutSec->Addr + In<ELFT>::Common->OutSecOff +
+ cast<DefinedCommon>(Body).Offset;
case SymbolBody::SharedKind: {
auto &SS = cast<SharedSymbol<ELFT>>(Body);
if (!SS.NeedsCopyOrPltAddr)
return 0;
if (SS.isFunc())
return Body.getPltVA<ELFT>();
- return Out<ELFT>::Bss->getVA() + SS.OffsetInBss;
+ return Out<ELFT>::Bss->Addr + SS.OffsetInBss;
}
case SymbolBody::UndefinedKind:
return 0;
@@ -79,32 +89,15 @@ static typename ELFT::uint getSymVA(const SymbolBody &Body,
case SymbolBody::LazyObjectKind:
assert(Body.symbol()->IsUsedInRegularObj && "lazy symbol reached writer");
return 0;
- case SymbolBody::DefinedBitcodeKind:
- llvm_unreachable("should have been replaced");
}
llvm_unreachable("invalid symbol kind");
}
-SymbolBody::SymbolBody(Kind K, uint32_t NameOffset, uint8_t StOther,
+SymbolBody::SymbolBody(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
uint8_t Type)
- : SymbolKind(K), NeedsCopyOrPltAddr(false), IsLocal(true),
- IsInGlobalMipsGot(false), Type(Type), StOther(StOther),
- NameOffset(NameOffset) {}
-
-SymbolBody::SymbolBody(Kind K, StringRef Name, uint8_t StOther, uint8_t Type)
- : SymbolKind(K), NeedsCopyOrPltAddr(false), IsLocal(false),
- IsInGlobalMipsGot(false), Type(Type), StOther(StOther),
- Name({Name.data(), Name.size()}) {}
-
-StringRef SymbolBody::getName() const {
- assert(!isLocal());
- return StringRef(Name.S, Name.Len);
-}
-
-void SymbolBody::setName(StringRef S) {
- Name.S = S.data();
- Name.Len = S.size();
-}
+ : SymbolKind(K), NeedsCopyOrPltAddr(false), IsLocal(IsLocal),
+ IsInGlobalMipsGot(false), Is32BitMipsGot(false), IsInIplt(false),
+ IsInIgot(false), Type(Type), StOther(StOther), Name(Name) {}
// Returns true if a symbol can be replaced at load-time by a symbol
// with the same name defined in other ELF executable or DSO.
@@ -151,7 +144,7 @@ typename ELFT::uint SymbolBody::getVA(typename ELFT::uint Addend) const {
}
template <class ELFT> typename ELFT::uint SymbolBody::getGotVA() const {
- return Out<ELFT>::Got->getVA() + getGotOffset<ELFT>();
+ return In<ELFT>::Got->getVA() + getGotOffset<ELFT>();
}
template <class ELFT> typename ELFT::uint SymbolBody::getGotOffset() const {
@@ -159,7 +152,9 @@ template <class ELFT> typename ELFT::uint SymbolBody::getGotOffset() const {
}
template <class ELFT> typename ELFT::uint SymbolBody::getGotPltVA() const {
- return Out<ELFT>::GotPlt->getVA() + getGotPltOffset<ELFT>();
+ if (this->IsInIgot)
+ return In<ELFT>::IgotPlt->getVA() + getGotPltOffset<ELFT>();
+ return In<ELFT>::GotPlt->getVA() + getGotPltOffset<ELFT>();
}
template <class ELFT> typename ELFT::uint SymbolBody::getGotPltOffset() const {
@@ -167,7 +162,9 @@ template <class ELFT> typename ELFT::uint SymbolBody::getGotPltOffset() const {
}
template <class ELFT> typename ELFT::uint SymbolBody::getPltVA() const {
- return Out<ELFT>::Plt->getVA() + Target->PltHeaderSize +
+ if (this->IsInIplt)
+ return In<ELFT>::Iplt->getVA() + PltIndex * Target->PltEntrySize;
+ return In<ELFT>::Plt->getVA() + Target->PltHeaderSize +
PltIndex * Target->PltEntrySize;
}
@@ -189,48 +186,67 @@ template <class ELFT> typename ELFT::uint SymbolBody::getSize() const {
return 0;
}
-Defined::Defined(Kind K, StringRef Name, uint8_t StOther, uint8_t Type)
- : SymbolBody(K, Name, StOther, Type) {}
-
-Defined::Defined(Kind K, uint32_t NameOffset, uint8_t StOther, uint8_t Type)
- : SymbolBody(K, NameOffset, StOther, Type) {}
+// If a symbol name contains '@', the characters after that is
+// a symbol version name. This function parses that.
+void SymbolBody::parseSymbolVersion() {
+ StringRef S = getName();
+ size_t Pos = S.find('@');
+ if (Pos == 0 || Pos == StringRef::npos)
+ return;
+ StringRef Verstr = S.substr(Pos + 1);
+ if (Verstr.empty())
+ return;
+
+ // Truncate the symbol name so that it doesn't include the version string.
+ Name = {S.data(), Pos};
+
+ // '@@' in a symbol name means the default version.
+ // It is usually the most recent one.
+ bool IsDefault = (Verstr[0] == '@');
+ if (IsDefault)
+ Verstr = Verstr.substr(1);
+
+ for (VersionDefinition &Ver : Config->VersionDefinitions) {
+ if (Ver.Name != Verstr)
+ continue;
+
+ if (IsDefault)
+ symbol()->VersionId = Ver.Id;
+ else
+ symbol()->VersionId = Ver.Id | VERSYM_HIDDEN;
+ return;
+ }
-DefinedBitcode::DefinedBitcode(StringRef Name, uint8_t StOther, uint8_t Type,
- BitcodeFile *F)
- : Defined(DefinedBitcodeKind, Name, StOther, Type) {
- this->File = F;
+ // It is an error if the specified version is not defined.
+ error(toString(File) + ": symbol " + S + " has undefined version " + Verstr);
}
-bool DefinedBitcode::classof(const SymbolBody *S) {
- return S->kind() == DefinedBitcodeKind;
-}
+Defined::Defined(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
+ uint8_t Type)
+ : SymbolBody(K, Name, IsLocal, StOther, Type) {}
-Undefined::Undefined(StringRef Name, uint8_t StOther, uint8_t Type,
- InputFile *File)
- : SymbolBody(SymbolBody::UndefinedKind, Name, StOther, Type) {
- this->File = File;
+template <class ELFT> bool DefinedRegular<ELFT>::isMipsPIC() const {
+ if (!Section || !isFunc())
+ return false;
+ return (this->StOther & STO_MIPS_MIPS16) == STO_MIPS_PIC ||
+ (Section->getFile()->getObj().getHeader()->e_flags & EF_MIPS_PIC);
}
-Undefined::Undefined(uint32_t NameOffset, uint8_t StOther, uint8_t Type,
- InputFile *File)
- : SymbolBody(SymbolBody::UndefinedKind, NameOffset, StOther, Type) {
+Undefined::Undefined(StringRefZ Name, bool IsLocal, uint8_t StOther,
+ uint8_t Type, InputFile *File)
+ : SymbolBody(SymbolBody::UndefinedKind, Name, IsLocal, StOther, Type) {
this->File = File;
}
-template <typename ELFT>
-DefinedSynthetic<ELFT>::DefinedSynthetic(StringRef N, uintX_t Value,
- OutputSectionBase<ELFT> *Section)
- : Defined(SymbolBody::DefinedSyntheticKind, N, STV_HIDDEN, 0 /* Type */),
- Value(Value), Section(Section) {}
-
-DefinedCommon::DefinedCommon(StringRef N, uint64_t Size, uint64_t Alignment,
+DefinedCommon::DefinedCommon(StringRef Name, uint64_t Size, uint64_t Alignment,
uint8_t StOther, uint8_t Type, InputFile *File)
- : Defined(SymbolBody::DefinedCommonKind, N, StOther, Type),
+ : Defined(SymbolBody::DefinedCommonKind, Name, /*IsLocal=*/false, StOther,
+ Type),
Alignment(Alignment), Size(Size) {
this->File = File;
}
-std::unique_ptr<InputFile> Lazy::fetch() {
+InputFile *Lazy::fetch() {
if (auto *S = dyn_cast<LazyArchive>(this))
return S->fetch();
return cast<LazyObject>(this)->fetch();
@@ -247,20 +263,20 @@ LazyObject::LazyObject(StringRef Name, LazyObjectFile &File, uint8_t Type)
this->File = &File;
}
-std::unique_ptr<InputFile> LazyArchive::fetch() {
- MemoryBufferRef MBRef = file()->getMember(&Sym);
+InputFile *LazyArchive::fetch() {
+ std::pair<MemoryBufferRef, uint64_t> MBInfo = file()->getMember(&Sym);
// getMember returns an empty buffer if the member was already
// read from the library.
- if (MBRef.getBuffer().empty())
- return std::unique_ptr<InputFile>(nullptr);
- return createObjectFile(MBRef, file()->getName());
+ if (MBInfo.first.getBuffer().empty())
+ return nullptr;
+ return createObjectFile(MBInfo.first, file()->getName(), MBInfo.second);
}
-std::unique_ptr<InputFile> LazyObject::fetch() {
+InputFile *LazyObject::fetch() {
MemoryBufferRef MBRef = file()->getBuffer();
if (MBRef.getBuffer().empty())
- return std::unique_ptr<InputFile>(nullptr);
+ return nullptr;
return createObjectFile(MBRef);
}
@@ -274,7 +290,7 @@ bool Symbol::includeInDynsym() const {
// Print out a log message for --trace-symbol.
void elf::printTraceSymbol(Symbol *Sym) {
SymbolBody *B = Sym->body();
- outs() << getFilename(B->File);
+ outs() << toString(B->File);
if (B->isUndefined())
outs() << ": reference to ";
@@ -285,6 +301,14 @@ void elf::printTraceSymbol(Symbol *Sym) {
outs() << B->getName() << "\n";
}
+// Returns a symbol for an error message.
+std::string elf::toString(const SymbolBody &B) {
+ if (Config->Demangle)
+ if (Optional<std::string> S = demangle(B.getName()))
+ return *S;
+ return B.getName();
+}
+
template bool SymbolBody::hasThunk<ELF32LE>() const;
template bool SymbolBody::hasThunk<ELF32BE>() const;
template bool SymbolBody::hasThunk<ELF64LE>() const;
@@ -330,7 +354,7 @@ template uint32_t SymbolBody::template getSize<ELF32BE>() const;
template uint64_t SymbolBody::template getSize<ELF64LE>() const;
template uint64_t SymbolBody::template getSize<ELF64BE>() const;
-template class elf::DefinedSynthetic<ELF32LE>;
-template class elf::DefinedSynthetic<ELF32BE>;
-template class elf::DefinedSynthetic<ELF64LE>;
-template class elf::DefinedSynthetic<ELF64BE>;
+template class elf::DefinedRegular<ELF32LE>;
+template class elf::DefinedRegular<ELF32BE>;
+template class elf::DefinedRegular<ELF64LE>;
+template class elf::DefinedRegular<ELF64BE>;
diff --git a/ELF/Symbols.h b/ELF/Symbols.h
index aa9a87d3b4f7..c95241a5293e 100644
--- a/ELF/Symbols.h
+++ b/ELF/Symbols.h
@@ -16,11 +16,11 @@
#define LLD_ELF_SYMBOLS_H
#include "InputSection.h"
+#include "Strings.h"
#include "lld/Core/LLVM.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
-#include "llvm/Support/AlignOf.h"
namespace lld {
namespace elf {
@@ -29,10 +29,9 @@ class ArchiveFile;
class BitcodeFile;
class InputFile;
class LazyObjectFile;
-class SymbolBody;
template <class ELFT> class ObjectFile;
template <class ELFT> class OutputSection;
-template <class ELFT> class OutputSectionBase;
+class OutputSectionBase;
template <class ELFT> class SharedFile;
struct Symbol;
@@ -45,7 +44,6 @@ public:
DefinedRegularKind = DefinedFirst,
SharedKind,
DefinedCommonKind,
- DefinedBitcodeKind,
DefinedSyntheticKind,
DefinedLast = DefinedSyntheticKind,
UndefinedKind,
@@ -71,22 +69,10 @@ public:
bool isShared() const { return SymbolKind == SharedKind; }
bool isLocal() const { return IsLocal; }
bool isPreemptible() const;
-
- StringRef getName() const;
- void setName(StringRef S);
-
- uint32_t getNameOffset() const {
- assert(isLocal());
- return NameOffset;
- }
-
+ StringRef getName() const { return Name; }
uint8_t getVisibility() const { return StOther & 0x3; }
+ void parseSymbolVersion();
- unsigned DynsymIndex = 0;
- uint32_t GotIndex = -1;
- uint32_t GotPltIndex = -1;
- uint32_t PltIndex = -1;
- uint32_t GlobalDynIndex = -1;
bool isInGot() const { return GotIndex != -1U; }
bool isInPlt() const { return PltIndex != -1U; }
template <class ELFT> bool hasThunk() const;
@@ -105,10 +91,15 @@ public:
// The file from which this symbol was created.
InputFile *File = nullptr;
-protected:
- SymbolBody(Kind K, StringRef Name, uint8_t StOther, uint8_t Type);
+ uint32_t DynsymIndex = 0;
+ uint32_t GotIndex = -1;
+ uint32_t GotPltIndex = -1;
+ uint32_t PltIndex = -1;
+ uint32_t GlobalDynIndex = -1;
- SymbolBody(Kind K, uint32_t NameOffset, uint8_t StOther, uint8_t Type);
+protected:
+ SymbolBody(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
+ uint8_t Type);
const unsigned SymbolKind : 8;
@@ -123,6 +114,15 @@ public:
// True if this symbol has an entry in the global part of MIPS GOT.
unsigned IsInGlobalMipsGot : 1;
+ // True if this symbol is referenced by 32-bit GOT relocations.
+ unsigned Is32BitMipsGot : 1;
+
+ // True if this symbol is in the Iplt sub-section of the Plt.
+ unsigned IsInIplt : 1;
+
+ // True if this symbol is in the Igot sub-section of the .got.plt or .got.
+ unsigned IsInIgot : 1;
+
// The following fields have the same meaning as the ELF symbol attributes.
uint8_t Type; // symbol type
uint8_t StOther; // st_other field value
@@ -142,32 +142,16 @@ public:
bool isFile() const { return Type == llvm::ELF::STT_FILE; }
protected:
- struct Str {
- const char *S;
- size_t Len;
- };
- union {
- Str Name;
- uint32_t NameOffset;
- };
+ StringRefZ Name;
};
// The base class for any defined symbols.
class Defined : public SymbolBody {
public:
- Defined(Kind K, StringRef Name, uint8_t StOther, uint8_t Type);
- Defined(Kind K, uint32_t NameOffset, uint8_t StOther, uint8_t Type);
+ Defined(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type);
static bool classof(const SymbolBody *S) { return S->isDefined(); }
};
-// The defined symbol in LLVM bitcode files.
-class DefinedBitcode : public Defined {
-public:
- DefinedBitcode(StringRef Name, uint8_t StOther, uint8_t Type, BitcodeFile *F);
- static bool classof(const SymbolBody *S);
- BitcodeFile *file() { return (BitcodeFile *)this->File; }
-};
-
class DefinedCommon : public Defined {
public:
DefinedCommon(StringRef N, uint64_t Size, uint64_t Alignment, uint8_t StOther,
@@ -179,7 +163,7 @@ public:
// The output offset of this common symbol in the output bss. Computed by the
// writer.
- uint64_t OffsetInBss;
+ uint64_t Offset;
// The maximum alignment we have seen for this symbol.
uint64_t Alignment;
@@ -193,30 +177,17 @@ template <class ELFT> class DefinedRegular : public Defined {
typedef typename ELFT::uint uintX_t;
public:
- DefinedRegular(StringRef Name, const Elf_Sym &Sym,
- InputSectionBase<ELFT> *Section)
- : Defined(SymbolBody::DefinedRegularKind, Name, Sym.st_other,
- Sym.getType()),
- Value(Sym.st_value), Size(Sym.st_size),
+ DefinedRegular(StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type,
+ uintX_t Value, uintX_t Size, InputSectionBase<ELFT> *Section,
+ InputFile *File)
+ : Defined(SymbolBody::DefinedRegularKind, Name, IsLocal, StOther, Type),
+ Value(Value), Size(Size),
Section(Section ? Section->Repl : NullInputSection) {
- if (Section)
- this->File = Section->getFile();
+ this->File = File;
}
- DefinedRegular(const Elf_Sym &Sym, InputSectionBase<ELFT> *Section)
- : Defined(SymbolBody::DefinedRegularKind, Sym.st_name, Sym.st_other,
- Sym.getType()),
- Value(Sym.st_value), Size(Sym.st_size),
- Section(Section ? Section->Repl : NullInputSection) {
- assert(isLocal());
- if (Section)
- this->File = Section->getFile();
- }
-
- DefinedRegular(StringRef Name, uint8_t StOther)
- : Defined(SymbolBody::DefinedRegularKind, Name, StOther,
- llvm::ELF::STT_NOTYPE),
- Value(0), Size(0), Section(NullInputSection) {}
+ // Return true if the symbol is a PIC function.
+ bool isMipsPIC() const;
static bool classof(const SymbolBody *S) {
return S->kind() == SymbolBody::DefinedRegularKind;
@@ -249,28 +220,26 @@ InputSectionBase<ELFT> *DefinedRegular<ELFT>::NullInputSection;
// don't belong to any input files or sections. Thus, its constructor
// takes an output section to calculate output VA, etc.
// If Section is null, this symbol is relative to the image base.
-template <class ELFT> class DefinedSynthetic : public Defined {
+class DefinedSynthetic : public Defined {
public:
- typedef typename ELFT::uint uintX_t;
- DefinedSynthetic(StringRef N, uintX_t Value,
- OutputSectionBase<ELFT> *Section);
+ DefinedSynthetic(StringRef Name, uint64_t Value,
+ const OutputSectionBase *Section)
+ : Defined(SymbolBody::DefinedSyntheticKind, Name, /*IsLocal=*/false,
+ llvm::ELF::STV_HIDDEN, 0 /* Type */),
+ Value(Value), Section(Section) {}
static bool classof(const SymbolBody *S) {
return S->kind() == SymbolBody::DefinedSyntheticKind;
}
- // Special value designates that the symbol 'points'
- // to the end of the section.
- static const uintX_t SectionEnd = uintX_t(-1);
-
- uintX_t Value;
- const OutputSectionBase<ELFT> *Section;
+ uint64_t Value;
+ const OutputSectionBase *Section;
};
class Undefined : public SymbolBody {
public:
- Undefined(StringRef Name, uint8_t StOther, uint8_t Type, InputFile *F);
- Undefined(uint32_t NameOffset, uint8_t StOther, uint8_t Type, InputFile *F);
+ Undefined(StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type,
+ InputFile *F);
static bool classof(const SymbolBody *S) {
return S->kind() == UndefinedKind;
@@ -291,7 +260,8 @@ public:
SharedSymbol(SharedFile<ELFT> *F, StringRef Name, const Elf_Sym &Sym,
const Elf_Verdef *Verdef)
- : Defined(SymbolBody::SharedKind, Name, Sym.st_other, Sym.getType()),
+ : Defined(SymbolBody::SharedKind, Name, /*IsLocal=*/false, Sym.st_other,
+ Sym.getType()),
Sym(Sym), Verdef(Verdef) {
// IFuncs defined in DSOs are treated as functions by the static linker.
if (isGnuIFunc())
@@ -326,11 +296,11 @@ public:
// Returns an object file for this symbol, or a nullptr if the file
// was already returned.
- std::unique_ptr<InputFile> fetch();
+ InputFile *fetch();
protected:
Lazy(SymbolBody::Kind K, StringRef Name, uint8_t Type)
- : SymbolBody(K, Name, llvm::ELF::STV_DEFAULT, Type) {}
+ : SymbolBody(K, Name, /*IsLocal=*/false, llvm::ELF::STV_DEFAULT, Type) {}
};
// LazyArchive symbols represents symbols in archive files.
@@ -344,7 +314,7 @@ public:
}
ArchiveFile *file() { return (ArchiveFile *)this->File; }
- std::unique_ptr<InputFile> fetch();
+ InputFile *fetch();
private:
const llvm::object::Archive::Symbol Sym;
@@ -361,12 +331,15 @@ public:
}
LazyObjectFile *file() { return (LazyObjectFile *)this->File; }
- std::unique_ptr<InputFile> fetch();
+ InputFile *fetch();
};
// Some linker-generated symbols need to be created as
// DefinedRegular symbols.
template <class ELFT> struct ElfSym {
+ // The content for __ehdr_start symbol.
+ static DefinedRegular<ELFT> *EhdrStart;
+
// The content for _etext and etext symbols.
static DefinedRegular<ELFT> *Etext;
static DefinedRegular<ELFT> *Etext2;
@@ -379,17 +352,22 @@ template <class ELFT> struct ElfSym {
static DefinedRegular<ELFT> *End;
static DefinedRegular<ELFT> *End2;
- // The content for _gp_disp symbol for MIPS target.
- static SymbolBody *MipsGpDisp;
+ // The content for _gp_disp/__gnu_local_gp symbols for MIPS target.
+ static DefinedRegular<ELFT> *MipsGpDisp;
+ static DefinedRegular<ELFT> *MipsLocalGp;
+ static DefinedRegular<ELFT> *MipsGp;
};
+template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::EhdrStart;
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::Etext;
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::Etext2;
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::Edata;
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::Edata2;
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::End;
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::End2;
-template <class ELFT> SymbolBody *ElfSym<ELFT>::MipsGpDisp;
+template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::MipsGpDisp;
+template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::MipsLocalGp;
+template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::MipsGp;
// A real symbol object, SymbolBody, is usually stored within a Symbol. There's
// always one Symbol for each symbol name. The resolver updates the SymbolBody
@@ -425,6 +403,9 @@ struct Symbol {
// True if this symbol is specified by --trace-symbol option.
unsigned Traced : 1;
+ // This symbol version was found in a version script.
+ unsigned InVersionScript : 1;
+
bool includeInDynsym() const;
bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
@@ -434,9 +415,8 @@ struct Symbol {
// assume that the size and alignment of ELF64LE symbols is sufficient for any
// ELFT, and we verify this with the static_asserts in replaceBody.
llvm::AlignedCharArrayUnion<
- DefinedBitcode, DefinedCommon, DefinedRegular<llvm::object::ELF64LE>,
- DefinedSynthetic<llvm::object::ELF64LE>, Undefined,
- SharedSymbol<llvm::object::ELF64LE>, LazyArchive, LazyObject>
+ DefinedCommon, DefinedRegular<llvm::object::ELF64LE>, DefinedSynthetic,
+ Undefined, SharedSymbol<llvm::object::ELF64LE>, LazyArchive, LazyObject>
Body;
SymbolBody *body() { return reinterpret_cast<SymbolBody *>(Body.buffer); }
@@ -448,8 +428,7 @@ void printTraceSymbol(Symbol *Sym);
template <typename T, typename... ArgT>
void replaceBody(Symbol *S, ArgT &&... Arg) {
static_assert(sizeof(T) <= sizeof(S->Body), "Body too small");
- static_assert(llvm::AlignOf<T>::Alignment <=
- llvm::AlignOf<decltype(S->Body)>::Alignment,
+ static_assert(alignof(T) <= alignof(decltype(S->Body)),
"Body not aligned enough");
assert(static_cast<SymbolBody *>(static_cast<T *>(nullptr)) == nullptr &&
"Not a SymbolBody");
@@ -468,6 +447,8 @@ inline Symbol *SymbolBody::symbol() {
offsetof(Symbol, Body));
}
+std::string toString(const SymbolBody &B);
+
} // namespace elf
} // namespace lld
diff --git a/ELF/SyntheticSections.cpp b/ELF/SyntheticSections.cpp
new file mode 100644
index 000000000000..3c8a439ba308
--- /dev/null
+++ b/ELF/SyntheticSections.cpp
@@ -0,0 +1,1990 @@
+//===- SyntheticSections.cpp ----------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains linker-synthesized sections. Currently,
+// synthetic sections are created either output sections or input sections,
+// but we are rewriting code so that all synthetic sections are created as
+// input sections.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SyntheticSections.h"
+#include "Config.h"
+#include "Error.h"
+#include "InputFiles.h"
+#include "LinkerScript.h"
+#include "Memory.h"
+#include "OutputSections.h"
+#include "Strings.h"
+#include "SymbolTable.h"
+#include "Target.h"
+#include "Threads.h"
+#include "Writer.h"
+#include "lld/Config/Version.h"
+#include "llvm/Support/Dwarf.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/MD5.h"
+#include "llvm/Support/RandomNumberGenerator.h"
+#include "llvm/Support/SHA1.h"
+#include "llvm/Support/xxhash.h"
+#include <cstdlib>
+
+using namespace llvm;
+using namespace llvm::dwarf;
+using namespace llvm::ELF;
+using namespace llvm::object;
+using namespace llvm::support;
+using namespace llvm::support::endian;
+
+using namespace lld;
+using namespace lld::elf;
+
+template <class ELFT> static std::vector<DefinedCommon *> getCommonSymbols() {
+ std::vector<DefinedCommon *> V;
+ for (Symbol *S : Symtab<ELFT>::X->getSymbols())
+ if (auto *B = dyn_cast<DefinedCommon>(S->body()))
+ V.push_back(B);
+ return V;
+}
+
+// Find all common symbols and allocate space for them.
+template <class ELFT> InputSection<ELFT> *elf::createCommonSection() {
+ auto *Ret = make<InputSection<ELFT>>(SHF_ALLOC | SHF_WRITE, SHT_NOBITS, 1,
+ ArrayRef<uint8_t>(), "COMMON");
+ Ret->Live = true;
+
+ // Sort the common symbols by alignment as an heuristic to pack them better.
+ std::vector<DefinedCommon *> Syms = getCommonSymbols<ELFT>();
+ std::stable_sort(Syms.begin(), Syms.end(),
+ [](const DefinedCommon *A, const DefinedCommon *B) {
+ return A->Alignment > B->Alignment;
+ });
+
+ // Assign offsets to symbols.
+ size_t Size = 0;
+ size_t Alignment = 1;
+ for (DefinedCommon *Sym : Syms) {
+ Alignment = std::max<size_t>(Alignment, Sym->Alignment);
+ Size = alignTo(Size, Sym->Alignment);
+
+ // Compute symbol offset relative to beginning of input section.
+ Sym->Offset = Size;
+ Size += Sym->Size;
+ }
+ Ret->Alignment = Alignment;
+ Ret->Data = makeArrayRef<uint8_t>(nullptr, Size);
+ return Ret;
+}
+
+// Returns an LLD version string.
+static ArrayRef<uint8_t> getVersion() {
+ // Check LLD_VERSION first for ease of testing.
+ // You can get consitent output by using the environment variable.
+ // This is only for testing.
+ StringRef S = getenv("LLD_VERSION");
+ if (S.empty())
+ S = Saver.save(Twine("Linker: ") + getLLDVersion());
+
+ // +1 to include the terminating '\0'.
+ return {(const uint8_t *)S.data(), S.size() + 1};
+}
+
+// Creates a .comment section containing LLD version info.
+// With this feature, you can identify LLD-generated binaries easily
+// by "objdump -s -j .comment <file>".
+// The returned object is a mergeable string section.
+template <class ELFT> MergeInputSection<ELFT> *elf::createCommentSection() {
+ typename ELFT::Shdr Hdr = {};
+ Hdr.sh_flags = SHF_MERGE | SHF_STRINGS;
+ Hdr.sh_type = SHT_PROGBITS;
+ Hdr.sh_entsize = 1;
+ Hdr.sh_addralign = 1;
+
+ auto *Ret = make<MergeInputSection<ELFT>>(/*file=*/nullptr, &Hdr, ".comment");
+ Ret->Data = getVersion();
+ Ret->splitIntoPieces();
+ return Ret;
+}
+
+// .MIPS.abiflags section.
+template <class ELFT>
+MipsAbiFlagsSection<ELFT>::MipsAbiFlagsSection(Elf_Mips_ABIFlags Flags)
+ : SyntheticSection<ELFT>(SHF_ALLOC, SHT_MIPS_ABIFLAGS, 8, ".MIPS.abiflags"),
+ Flags(Flags) {}
+
+template <class ELFT> void MipsAbiFlagsSection<ELFT>::writeTo(uint8_t *Buf) {
+ memcpy(Buf, &Flags, sizeof(Flags));
+}
+
+template <class ELFT>
+MipsAbiFlagsSection<ELFT> *MipsAbiFlagsSection<ELFT>::create() {
+ Elf_Mips_ABIFlags Flags = {};
+ bool Create = false;
+
+ for (InputSectionBase<ELFT> *Sec : Symtab<ELFT>::X->Sections) {
+ if (!Sec->Live || Sec->Type != SHT_MIPS_ABIFLAGS)
+ continue;
+ Sec->Live = false;
+ Create = true;
+
+ std::string Filename = toString(Sec->getFile());
+ const size_t Size = Sec->Data.size();
+ // Older version of BFD (such as the default FreeBSD linker) concatenate
+ // .MIPS.abiflags instead of merging. To allow for this case (or potential
+ // zero padding) we ignore everything after the first Elf_Mips_ABIFlags
+ if (Size < sizeof(Elf_Mips_ABIFlags)) {
+ error(Filename + ": invalid size of .MIPS.abiflags section: got " +
+ Twine(Size) + " instead of " + Twine(sizeof(Elf_Mips_ABIFlags)));
+ return nullptr;
+ }
+ auto *S = reinterpret_cast<const Elf_Mips_ABIFlags *>(Sec->Data.data());
+ if (S->version != 0) {
+ error(Filename + ": unexpected .MIPS.abiflags version " +
+ Twine(S->version));
+ return nullptr;
+ }
+
+ // LLD checks ISA compatibility in getMipsEFlags(). Here we just
+ // select the highest number of ISA/Rev/Ext.
+ Flags.isa_level = std::max(Flags.isa_level, S->isa_level);
+ Flags.isa_rev = std::max(Flags.isa_rev, S->isa_rev);
+ Flags.isa_ext = std::max(Flags.isa_ext, S->isa_ext);
+ Flags.gpr_size = std::max(Flags.gpr_size, S->gpr_size);
+ Flags.cpr1_size = std::max(Flags.cpr1_size, S->cpr1_size);
+ Flags.cpr2_size = std::max(Flags.cpr2_size, S->cpr2_size);
+ Flags.ases |= S->ases;
+ Flags.flags1 |= S->flags1;
+ Flags.flags2 |= S->flags2;
+ Flags.fp_abi = elf::getMipsFpAbiFlag(Flags.fp_abi, S->fp_abi, Filename);
+ };
+
+ if (Create)
+ return make<MipsAbiFlagsSection<ELFT>>(Flags);
+ return nullptr;
+}
+
+// .MIPS.options section.
+template <class ELFT>
+MipsOptionsSection<ELFT>::MipsOptionsSection(Elf_Mips_RegInfo Reginfo)
+ : SyntheticSection<ELFT>(SHF_ALLOC, SHT_MIPS_OPTIONS, 8, ".MIPS.options"),
+ Reginfo(Reginfo) {}
+
+template <class ELFT> void MipsOptionsSection<ELFT>::writeTo(uint8_t *Buf) {
+ auto *Options = reinterpret_cast<Elf_Mips_Options *>(Buf);
+ Options->kind = ODK_REGINFO;
+ Options->size = getSize();
+
+ if (!Config->Relocatable)
+ Reginfo.ri_gp_value = In<ELFT>::MipsGot->getGp();
+ memcpy(Buf + sizeof(Elf_Mips_Options), &Reginfo, sizeof(Reginfo));
+}
+
+template <class ELFT>
+MipsOptionsSection<ELFT> *MipsOptionsSection<ELFT>::create() {
+ // N64 ABI only.
+ if (!ELFT::Is64Bits)
+ return nullptr;
+
+ Elf_Mips_RegInfo Reginfo = {};
+ bool Create = false;
+
+ for (InputSectionBase<ELFT> *Sec : Symtab<ELFT>::X->Sections) {
+ if (!Sec->Live || Sec->Type != SHT_MIPS_OPTIONS)
+ continue;
+ Sec->Live = false;
+ Create = true;
+
+ std::string Filename = toString(Sec->getFile());
+ ArrayRef<uint8_t> D = Sec->Data;
+
+ while (!D.empty()) {
+ if (D.size() < sizeof(Elf_Mips_Options)) {
+ error(Filename + ": invalid size of .MIPS.options section");
+ break;
+ }
+
+ auto *Opt = reinterpret_cast<const Elf_Mips_Options *>(D.data());
+ if (Opt->kind == ODK_REGINFO) {
+ if (Config->Relocatable && Opt->getRegInfo().ri_gp_value)
+ error(Filename + ": unsupported non-zero ri_gp_value");
+ Reginfo.ri_gprmask |= Opt->getRegInfo().ri_gprmask;
+ Sec->getFile()->MipsGp0 = Opt->getRegInfo().ri_gp_value;
+ break;
+ }
+
+ if (!Opt->size)
+ fatal(Filename + ": zero option descriptor size");
+ D = D.slice(Opt->size);
+ }
+ };
+
+ if (Create)
+ return make<MipsOptionsSection<ELFT>>(Reginfo);
+ return nullptr;
+}
+
+// MIPS .reginfo section.
+template <class ELFT>
+MipsReginfoSection<ELFT>::MipsReginfoSection(Elf_Mips_RegInfo Reginfo)
+ : SyntheticSection<ELFT>(SHF_ALLOC, SHT_MIPS_REGINFO, 4, ".reginfo"),
+ Reginfo(Reginfo) {}
+
+template <class ELFT> void MipsReginfoSection<ELFT>::writeTo(uint8_t *Buf) {
+ if (!Config->Relocatable)
+ Reginfo.ri_gp_value = In<ELFT>::MipsGot->getGp();
+ memcpy(Buf, &Reginfo, sizeof(Reginfo));
+}
+
+template <class ELFT>
+MipsReginfoSection<ELFT> *MipsReginfoSection<ELFT>::create() {
+ // Section should be alive for O32 and N32 ABIs only.
+ if (ELFT::Is64Bits)
+ return nullptr;
+
+ Elf_Mips_RegInfo Reginfo = {};
+ bool Create = false;
+
+ for (InputSectionBase<ELFT> *Sec : Symtab<ELFT>::X->Sections) {
+ if (!Sec->Live || Sec->Type != SHT_MIPS_REGINFO)
+ continue;
+ Sec->Live = false;
+ Create = true;
+
+ if (Sec->Data.size() != sizeof(Elf_Mips_RegInfo)) {
+ error(toString(Sec->getFile()) + ": invalid size of .reginfo section");
+ return nullptr;
+ }
+ auto *R = reinterpret_cast<const Elf_Mips_RegInfo *>(Sec->Data.data());
+ if (Config->Relocatable && R->ri_gp_value)
+ error(toString(Sec->getFile()) + ": unsupported non-zero ri_gp_value");
+
+ Reginfo.ri_gprmask |= R->ri_gprmask;
+ Sec->getFile()->MipsGp0 = R->ri_gp_value;
+ };
+
+ if (Create)
+ return make<MipsReginfoSection<ELFT>>(Reginfo);
+ return nullptr;
+}
+
+template <class ELFT> InputSection<ELFT> *elf::createInterpSection() {
+ auto *Ret = make<InputSection<ELFT>>(SHF_ALLOC, SHT_PROGBITS, 1,
+ ArrayRef<uint8_t>(), ".interp");
+ Ret->Live = true;
+
+ // StringSaver guarantees that the returned string ends with '\0'.
+ StringRef S = Saver.save(Config->DynamicLinker);
+ Ret->Data = {(const uint8_t *)S.data(), S.size() + 1};
+ return Ret;
+}
+
+static size_t getHashSize() {
+ switch (Config->BuildId) {
+ case BuildIdKind::Fast:
+ return 8;
+ case BuildIdKind::Md5:
+ case BuildIdKind::Uuid:
+ return 16;
+ case BuildIdKind::Sha1:
+ return 20;
+ case BuildIdKind::Hexstring:
+ return Config->BuildIdVector.size();
+ default:
+ llvm_unreachable("unknown BuildIdKind");
+ }
+}
+
+template <class ELFT>
+BuildIdSection<ELFT>::BuildIdSection()
+ : SyntheticSection<ELFT>(SHF_ALLOC, SHT_NOTE, 1, ".note.gnu.build-id"),
+ HashSize(getHashSize()) {}
+
+template <class ELFT> void BuildIdSection<ELFT>::writeTo(uint8_t *Buf) {
+ const endianness E = ELFT::TargetEndianness;
+ write32<E>(Buf, 4); // Name size
+ write32<E>(Buf + 4, HashSize); // Content size
+ write32<E>(Buf + 8, NT_GNU_BUILD_ID); // Type
+ memcpy(Buf + 12, "GNU", 4); // Name string
+ HashBuf = Buf + 16;
+}
+
+// Split one uint8 array into small pieces of uint8 arrays.
+static std::vector<ArrayRef<uint8_t>> split(ArrayRef<uint8_t> Arr,
+ size_t ChunkSize) {
+ std::vector<ArrayRef<uint8_t>> Ret;
+ while (Arr.size() > ChunkSize) {
+ Ret.push_back(Arr.take_front(ChunkSize));
+ Arr = Arr.drop_front(ChunkSize);
+ }
+ if (!Arr.empty())
+ Ret.push_back(Arr);
+ return Ret;
+}
+
+// Computes a hash value of Data using a given hash function.
+// In order to utilize multiple cores, we first split data into 1MB
+// chunks, compute a hash for each chunk, and then compute a hash value
+// of the hash values.
+template <class ELFT>
+void BuildIdSection<ELFT>::computeHash(
+ llvm::ArrayRef<uint8_t> Data,
+ std::function<void(uint8_t *Dest, ArrayRef<uint8_t> Arr)> HashFn) {
+ std::vector<ArrayRef<uint8_t>> Chunks = split(Data, 1024 * 1024);
+ std::vector<uint8_t> Hashes(Chunks.size() * HashSize);
+
+ // Compute hash values.
+ forLoop(0, Chunks.size(),
+ [&](size_t I) { HashFn(Hashes.data() + I * HashSize, Chunks[I]); });
+
+ // Write to the final output buffer.
+ HashFn(HashBuf, Hashes);
+}
+
+template <class ELFT>
+void BuildIdSection<ELFT>::writeBuildId(ArrayRef<uint8_t> Buf) {
+ switch (Config->BuildId) {
+ case BuildIdKind::Fast:
+ computeHash(Buf, [](uint8_t *Dest, ArrayRef<uint8_t> Arr) {
+ write64le(Dest, xxHash64(toStringRef(Arr)));
+ });
+ break;
+ case BuildIdKind::Md5:
+ computeHash(Buf, [](uint8_t *Dest, ArrayRef<uint8_t> Arr) {
+ memcpy(Dest, MD5::hash(Arr).data(), 16);
+ });
+ break;
+ case BuildIdKind::Sha1:
+ computeHash(Buf, [](uint8_t *Dest, ArrayRef<uint8_t> Arr) {
+ memcpy(Dest, SHA1::hash(Arr).data(), 20);
+ });
+ break;
+ case BuildIdKind::Uuid:
+ if (getRandomBytes(HashBuf, HashSize))
+ error("entropy source failure");
+ break;
+ case BuildIdKind::Hexstring:
+ memcpy(HashBuf, Config->BuildIdVector.data(), Config->BuildIdVector.size());
+ break;
+ default:
+ llvm_unreachable("unknown BuildIdKind");
+ }
+}
+
+template <class ELFT>
+GotSection<ELFT>::GotSection()
+ : SyntheticSection<ELFT>(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
+ Target->GotEntrySize, ".got") {}
+
+template <class ELFT> void GotSection<ELFT>::addEntry(SymbolBody &Sym) {
+ Sym.GotIndex = NumEntries;
+ ++NumEntries;
+}
+
+template <class ELFT> bool GotSection<ELFT>::addDynTlsEntry(SymbolBody &Sym) {
+ if (Sym.GlobalDynIndex != -1U)
+ return false;
+ Sym.GlobalDynIndex = NumEntries;
+ // Global Dynamic TLS entries take two GOT slots.
+ NumEntries += 2;
+ return true;
+}
+
+// Reserves TLS entries for a TLS module ID and a TLS block offset.
+// In total it takes two GOT slots.
+template <class ELFT> bool GotSection<ELFT>::addTlsIndex() {
+ if (TlsIndexOff != uint32_t(-1))
+ return false;
+ TlsIndexOff = NumEntries * sizeof(uintX_t);
+ NumEntries += 2;
+ return true;
+}
+
+template <class ELFT>
+typename GotSection<ELFT>::uintX_t
+GotSection<ELFT>::getGlobalDynAddr(const SymbolBody &B) const {
+ return this->getVA() + B.GlobalDynIndex * sizeof(uintX_t);
+}
+
+template <class ELFT>
+typename GotSection<ELFT>::uintX_t
+GotSection<ELFT>::getGlobalDynOffset(const SymbolBody &B) const {
+ return B.GlobalDynIndex * sizeof(uintX_t);
+}
+
+template <class ELFT> void GotSection<ELFT>::finalize() {
+ Size = NumEntries * sizeof(uintX_t);
+}
+
+template <class ELFT> bool GotSection<ELFT>::empty() const {
+ // If we have a relocation that is relative to GOT (such as GOTOFFREL),
+ // we need to emit a GOT even if it's empty.
+ return NumEntries == 0 && !HasGotOffRel;
+}
+
+template <class ELFT> void GotSection<ELFT>::writeTo(uint8_t *Buf) {
+ this->relocate(Buf, Buf + Size);
+}
+
+template <class ELFT>
+MipsGotSection<ELFT>::MipsGotSection()
+ : SyntheticSection<ELFT>(SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL,
+ SHT_PROGBITS, Target->GotEntrySize, ".got") {}
+
+template <class ELFT>
+void MipsGotSection<ELFT>::addEntry(SymbolBody &Sym, uintX_t Addend,
+ RelExpr Expr) {
+ // For "true" local symbols which can be referenced from the same module
+ // only compiler creates two instructions for address loading:
+ //
+ // lw $8, 0($gp) # R_MIPS_GOT16
+ // addi $8, $8, 0 # R_MIPS_LO16
+ //
+ // The first instruction loads high 16 bits of the symbol address while
+ // the second adds an offset. That allows to reduce number of required
+ // GOT entries because only one global offset table entry is necessary
+ // for every 64 KBytes of local data. So for local symbols we need to
+ // allocate number of GOT entries to hold all required "page" addresses.
+ //
+ // All global symbols (hidden and regular) considered by compiler uniformly.
+ // It always generates a single `lw` instruction and R_MIPS_GOT16 relocation
+ // to load address of the symbol. So for each such symbol we need to
+ // allocate dedicated GOT entry to store its address.
+ //
+ // If a symbol is preemptible we need help of dynamic linker to get its
+ // final address. The corresponding GOT entries are allocated in the
+ // "global" part of GOT. Entries for non preemptible global symbol allocated
+ // in the "local" part of GOT.
+ //
+ // See "Global Offset Table" in Chapter 5:
+ // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
+ if (Expr == R_MIPS_GOT_LOCAL_PAGE) {
+ // At this point we do not know final symbol value so to reduce number
+ // of allocated GOT entries do the following trick. Save all output
+ // sections referenced by GOT relocations. Then later in the `finalize`
+ // method calculate number of "pages" required to cover all saved output
+ // section and allocate appropriate number of GOT entries.
+ PageIndexMap.insert({cast<DefinedRegular<ELFT>>(&Sym)->Section->OutSec, 0});
+ return;
+ }
+ if (Sym.isTls()) {
+ // GOT entries created for MIPS TLS relocations behave like
+ // almost GOT entries from other ABIs. They go to the end
+ // of the global offset table.
+ Sym.GotIndex = TlsEntries.size();
+ TlsEntries.push_back(&Sym);
+ return;
+ }
+ auto AddEntry = [&](SymbolBody &S, uintX_t A, GotEntries &Items) {
+ if (S.isInGot() && !A)
+ return;
+ size_t NewIndex = Items.size();
+ if (!EntryIndexMap.insert({{&S, A}, NewIndex}).second)
+ return;
+ Items.emplace_back(&S, A);
+ if (!A)
+ S.GotIndex = NewIndex;
+ };
+ if (Sym.isPreemptible()) {
+ // Ignore addends for preemptible symbols. They got single GOT entry anyway.
+ AddEntry(Sym, 0, GlobalEntries);
+ Sym.IsInGlobalMipsGot = true;
+ } else if (Expr == R_MIPS_GOT_OFF32) {
+ AddEntry(Sym, Addend, LocalEntries32);
+ Sym.Is32BitMipsGot = true;
+ } else {
+ // Hold local GOT entries accessed via a 16-bit index separately.
+ // That allows to write them in the beginning of the GOT and keep
+ // their indexes as less as possible to escape relocation's overflow.
+ AddEntry(Sym, Addend, LocalEntries);
+ }
+}
+
+template <class ELFT>
+bool MipsGotSection<ELFT>::addDynTlsEntry(SymbolBody &Sym) {
+ if (Sym.GlobalDynIndex != -1U)
+ return false;
+ Sym.GlobalDynIndex = TlsEntries.size();
+ // Global Dynamic TLS entries take two GOT slots.
+ TlsEntries.push_back(nullptr);
+ TlsEntries.push_back(&Sym);
+ return true;
+}
+
+// Reserves TLS entries for a TLS module ID and a TLS block offset.
+// In total it takes two GOT slots.
+template <class ELFT> bool MipsGotSection<ELFT>::addTlsIndex() {
+ if (TlsIndexOff != uint32_t(-1))
+ return false;
+ TlsIndexOff = TlsEntries.size() * sizeof(uintX_t);
+ TlsEntries.push_back(nullptr);
+ TlsEntries.push_back(nullptr);
+ return true;
+}
+
+static uint64_t getMipsPageAddr(uint64_t Addr) {
+ return (Addr + 0x8000) & ~0xffff;
+}
+
+static uint64_t getMipsPageCount(uint64_t Size) {
+ return (Size + 0xfffe) / 0xffff + 1;
+}
+
+template <class ELFT>
+typename MipsGotSection<ELFT>::uintX_t
+MipsGotSection<ELFT>::getPageEntryOffset(const SymbolBody &B,
+ uintX_t Addend) const {
+ const OutputSectionBase *OutSec =
+ cast<DefinedRegular<ELFT>>(&B)->Section->OutSec;
+ uintX_t SecAddr = getMipsPageAddr(OutSec->Addr);
+ uintX_t SymAddr = getMipsPageAddr(B.getVA<ELFT>(Addend));
+ uintX_t Index = PageIndexMap.lookup(OutSec) + (SymAddr - SecAddr) / 0xffff;
+ assert(Index < PageEntriesNum);
+ return (HeaderEntriesNum + Index) * sizeof(uintX_t);
+}
+
+template <class ELFT>
+typename MipsGotSection<ELFT>::uintX_t
+MipsGotSection<ELFT>::getBodyEntryOffset(const SymbolBody &B,
+ uintX_t Addend) const {
+ // Calculate offset of the GOT entries block: TLS, global, local.
+ uintX_t Index = HeaderEntriesNum + PageEntriesNum;
+ if (B.isTls())
+ Index += LocalEntries.size() + LocalEntries32.size() + GlobalEntries.size();
+ else if (B.IsInGlobalMipsGot)
+ Index += LocalEntries.size() + LocalEntries32.size();
+ else if (B.Is32BitMipsGot)
+ Index += LocalEntries.size();
+ // Calculate offset of the GOT entry in the block.
+ if (B.isInGot())
+ Index += B.GotIndex;
+ else {
+ auto It = EntryIndexMap.find({&B, Addend});
+ assert(It != EntryIndexMap.end());
+ Index += It->second;
+ }
+ return Index * sizeof(uintX_t);
+}
+
+template <class ELFT>
+typename MipsGotSection<ELFT>::uintX_t
+MipsGotSection<ELFT>::getTlsOffset() const {
+ return (getLocalEntriesNum() + GlobalEntries.size()) * sizeof(uintX_t);
+}
+
+template <class ELFT>
+typename MipsGotSection<ELFT>::uintX_t
+MipsGotSection<ELFT>::getGlobalDynOffset(const SymbolBody &B) const {
+ return B.GlobalDynIndex * sizeof(uintX_t);
+}
+
+template <class ELFT>
+const SymbolBody *MipsGotSection<ELFT>::getFirstGlobalEntry() const {
+ return GlobalEntries.empty() ? nullptr : GlobalEntries.front().first;
+}
+
+template <class ELFT>
+unsigned MipsGotSection<ELFT>::getLocalEntriesNum() const {
+ return HeaderEntriesNum + PageEntriesNum + LocalEntries.size() +
+ LocalEntries32.size();
+}
+
+template <class ELFT> void MipsGotSection<ELFT>::finalize() {
+ PageEntriesNum = 0;
+ for (std::pair<const OutputSectionBase *, size_t> &P : PageIndexMap) {
+ // For each output section referenced by GOT page relocations calculate
+ // and save into PageIndexMap an upper bound of MIPS GOT entries required
+ // to store page addresses of local symbols. We assume the worst case -
+ // each 64kb page of the output section has at least one GOT relocation
+ // against it. And take in account the case when the section intersects
+ // page boundaries.
+ P.second = PageEntriesNum;
+ PageEntriesNum += getMipsPageCount(P.first->Size);
+ }
+ Size = (getLocalEntriesNum() + GlobalEntries.size() + TlsEntries.size()) *
+ sizeof(uintX_t);
+}
+
+template <class ELFT> bool MipsGotSection<ELFT>::empty() const {
+ // We add the .got section to the result for dynamic MIPS target because
+ // its address and properties are mentioned in the .dynamic section.
+ return Config->Relocatable;
+}
+
+template <class ELFT>
+typename MipsGotSection<ELFT>::uintX_t MipsGotSection<ELFT>::getGp() const {
+ return ElfSym<ELFT>::MipsGp->template getVA<ELFT>(0);
+}
+
+template <class ELFT>
+static void writeUint(uint8_t *Buf, typename ELFT::uint Val) {
+ typedef typename ELFT::uint uintX_t;
+ write<uintX_t, ELFT::TargetEndianness, sizeof(uintX_t)>(Buf, Val);
+}
+
+template <class ELFT> void MipsGotSection<ELFT>::writeTo(uint8_t *Buf) {
+ // Set the MSB of the second GOT slot. This is not required by any
+ // MIPS ABI documentation, though.
+ //
+ // There is a comment in glibc saying that "The MSB of got[1] of a
+ // gnu object is set to identify gnu objects," and in GNU gold it
+ // says "the second entry will be used by some runtime loaders".
+ // But how this field is being used is unclear.
+ //
+ // We are not really willing to mimic other linkers behaviors
+ // without understanding why they do that, but because all files
+ // generated by GNU tools have this special GOT value, and because
+ // we've been doing this for years, it is probably a safe bet to
+ // keep doing this for now. We really need to revisit this to see
+ // if we had to do this.
+ auto *P = reinterpret_cast<typename ELFT::Off *>(Buf);
+ P[1] = uintX_t(1) << (ELFT::Is64Bits ? 63 : 31);
+ Buf += HeaderEntriesNum * sizeof(uintX_t);
+ // Write 'page address' entries to the local part of the GOT.
+ for (std::pair<const OutputSectionBase *, size_t> &L : PageIndexMap) {
+ size_t PageCount = getMipsPageCount(L.first->Size);
+ uintX_t FirstPageAddr = getMipsPageAddr(L.first->Addr);
+ for (size_t PI = 0; PI < PageCount; ++PI) {
+ uint8_t *Entry = Buf + (L.second + PI) * sizeof(uintX_t);
+ writeUint<ELFT>(Entry, FirstPageAddr + PI * 0x10000);
+ }
+ }
+ Buf += PageEntriesNum * sizeof(uintX_t);
+ auto AddEntry = [&](const GotEntry &SA) {
+ uint8_t *Entry = Buf;
+ Buf += sizeof(uintX_t);
+ const SymbolBody *Body = SA.first;
+ uintX_t VA = Body->template getVA<ELFT>(SA.second);
+ writeUint<ELFT>(Entry, VA);
+ };
+ std::for_each(std::begin(LocalEntries), std::end(LocalEntries), AddEntry);
+ std::for_each(std::begin(LocalEntries32), std::end(LocalEntries32), AddEntry);
+ std::for_each(std::begin(GlobalEntries), std::end(GlobalEntries), AddEntry);
+ // Initialize TLS-related GOT entries. If the entry has a corresponding
+ // dynamic relocations, leave it initialized by zero. Write down adjusted
+ // TLS symbol's values otherwise. To calculate the adjustments use offsets
+ // for thread-local storage.
+ // https://www.linux-mips.org/wiki/NPTL
+ if (TlsIndexOff != -1U && !Config->Pic)
+ writeUint<ELFT>(Buf + TlsIndexOff, 1);
+ for (const SymbolBody *B : TlsEntries) {
+ if (!B || B->isPreemptible())
+ continue;
+ uintX_t VA = B->getVA<ELFT>();
+ if (B->GotIndex != -1U) {
+ uint8_t *Entry = Buf + B->GotIndex * sizeof(uintX_t);
+ writeUint<ELFT>(Entry, VA - 0x7000);
+ }
+ if (B->GlobalDynIndex != -1U) {
+ uint8_t *Entry = Buf + B->GlobalDynIndex * sizeof(uintX_t);
+ writeUint<ELFT>(Entry, 1);
+ Entry += sizeof(uintX_t);
+ writeUint<ELFT>(Entry, VA - 0x8000);
+ }
+ }
+}
+
+template <class ELFT>
+GotPltSection<ELFT>::GotPltSection()
+ : SyntheticSection<ELFT>(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
+ Target->GotPltEntrySize, ".got.plt") {}
+
+template <class ELFT> void GotPltSection<ELFT>::addEntry(SymbolBody &Sym) {
+ Sym.GotPltIndex = Target->GotPltHeaderEntriesNum + Entries.size();
+ Entries.push_back(&Sym);
+}
+
+template <class ELFT> size_t GotPltSection<ELFT>::getSize() const {
+ return (Target->GotPltHeaderEntriesNum + Entries.size()) *
+ Target->GotPltEntrySize;
+}
+
+template <class ELFT> void GotPltSection<ELFT>::writeTo(uint8_t *Buf) {
+ Target->writeGotPltHeader(Buf);
+ Buf += Target->GotPltHeaderEntriesNum * Target->GotPltEntrySize;
+ for (const SymbolBody *B : Entries) {
+ Target->writeGotPlt(Buf, *B);
+ Buf += sizeof(uintX_t);
+ }
+}
+
+// On ARM the IgotPltSection is part of the GotSection, on other Targets it is
+// part of the .got.plt
+template <class ELFT>
+IgotPltSection<ELFT>::IgotPltSection()
+ : SyntheticSection<ELFT>(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
+ Target->GotPltEntrySize,
+ Config->EMachine == EM_ARM ? ".got" : ".got.plt") {
+}
+
+template <class ELFT> void IgotPltSection<ELFT>::addEntry(SymbolBody &Sym) {
+ Sym.IsInIgot = true;
+ Sym.GotPltIndex = Entries.size();
+ Entries.push_back(&Sym);
+}
+
+template <class ELFT> size_t IgotPltSection<ELFT>::getSize() const {
+ return Entries.size() * Target->GotPltEntrySize;
+}
+
+template <class ELFT> void IgotPltSection<ELFT>::writeTo(uint8_t *Buf) {
+ for (const SymbolBody *B : Entries) {
+ Target->writeIgotPlt(Buf, *B);
+ Buf += sizeof(uintX_t);
+ }
+}
+
+template <class ELFT>
+StringTableSection<ELFT>::StringTableSection(StringRef Name, bool Dynamic)
+ : SyntheticSection<ELFT>(Dynamic ? (uintX_t)SHF_ALLOC : 0, SHT_STRTAB, 1,
+ Name),
+ Dynamic(Dynamic) {}
+
+// Adds a string to the string table. If HashIt is true we hash and check for
+// duplicates. It is optional because the name of global symbols are already
+// uniqued and hashing them again has a big cost for a small value: uniquing
+// them with some other string that happens to be the same.
+template <class ELFT>
+unsigned StringTableSection<ELFT>::addString(StringRef S, bool HashIt) {
+ if (HashIt) {
+ auto R = StringMap.insert(std::make_pair(S, this->Size));
+ if (!R.second)
+ return R.first->second;
+ }
+ unsigned Ret = this->Size;
+ this->Size = this->Size + S.size() + 1;
+ Strings.push_back(S);
+ return Ret;
+}
+
+template <class ELFT> void StringTableSection<ELFT>::writeTo(uint8_t *Buf) {
+ // ELF string tables start with NUL byte, so advance the pointer by one.
+ ++Buf;
+ for (StringRef S : Strings) {
+ memcpy(Buf, S.data(), S.size());
+ Buf += S.size() + 1;
+ }
+}
+
+// Returns the number of version definition entries. Because the first entry
+// is for the version definition itself, it is the number of versioned symbols
+// plus one. Note that we don't support multiple versions yet.
+static unsigned getVerDefNum() { return Config->VersionDefinitions.size() + 1; }
+
+template <class ELFT>
+DynamicSection<ELFT>::DynamicSection()
+ : SyntheticSection<ELFT>(SHF_ALLOC | SHF_WRITE, SHT_DYNAMIC,
+ sizeof(uintX_t), ".dynamic") {
+ this->Entsize = ELFT::Is64Bits ? 16 : 8;
+ // .dynamic section is not writable on MIPS.
+ // See "Special Section" in Chapter 4 in the following document:
+ // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
+ if (Config->EMachine == EM_MIPS)
+ this->Flags = SHF_ALLOC;
+
+ addEntries();
+}
+
+// There are some dynamic entries that don't depend on other sections.
+// Such entries can be set early.
+template <class ELFT> void DynamicSection<ELFT>::addEntries() {
+ // Add strings to .dynstr early so that .dynstr's size will be
+ // fixed early.
+ for (StringRef S : Config->AuxiliaryList)
+ add({DT_AUXILIARY, In<ELFT>::DynStrTab->addString(S)});
+ if (!Config->RPath.empty())
+ add({Config->EnableNewDtags ? DT_RUNPATH : DT_RPATH,
+ In<ELFT>::DynStrTab->addString(Config->RPath)});
+ for (SharedFile<ELFT> *F : Symtab<ELFT>::X->getSharedFiles())
+ if (F->isNeeded())
+ add({DT_NEEDED, In<ELFT>::DynStrTab->addString(F->getSoName())});
+ if (!Config->SoName.empty())
+ add({DT_SONAME, In<ELFT>::DynStrTab->addString(Config->SoName)});
+
+ // Set DT_FLAGS and DT_FLAGS_1.
+ uint32_t DtFlags = 0;
+ uint32_t DtFlags1 = 0;
+ if (Config->Bsymbolic)
+ DtFlags |= DF_SYMBOLIC;
+ if (Config->ZNodelete)
+ DtFlags1 |= DF_1_NODELETE;
+ if (Config->ZNow) {
+ DtFlags |= DF_BIND_NOW;
+ DtFlags1 |= DF_1_NOW;
+ }
+ if (Config->ZOrigin) {
+ DtFlags |= DF_ORIGIN;
+ DtFlags1 |= DF_1_ORIGIN;
+ }
+
+ if (DtFlags)
+ add({DT_FLAGS, DtFlags});
+ if (DtFlags1)
+ add({DT_FLAGS_1, DtFlags1});
+
+ if (!Config->Shared && !Config->Relocatable)
+ add({DT_DEBUG, (uint64_t)0});
+}
+
+// Add remaining entries to complete .dynamic contents.
+template <class ELFT> void DynamicSection<ELFT>::finalize() {
+ if (this->Size)
+ return; // Already finalized.
+
+ this->Link = In<ELFT>::DynStrTab->OutSec->SectionIndex;
+ if (In<ELFT>::RelaDyn->OutSec->Size > 0) {
+ bool IsRela = Config->Rela;
+ add({IsRela ? DT_RELA : DT_REL, In<ELFT>::RelaDyn});
+ add({IsRela ? DT_RELASZ : DT_RELSZ, In<ELFT>::RelaDyn->OutSec->Size});
+ add({IsRela ? DT_RELAENT : DT_RELENT,
+ uintX_t(IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel))});
+
+ // MIPS dynamic loader does not support RELCOUNT tag.
+ // The problem is in the tight relation between dynamic
+ // relocations and GOT. So do not emit this tag on MIPS.
+ if (Config->EMachine != EM_MIPS) {
+ size_t NumRelativeRels = In<ELFT>::RelaDyn->getRelativeRelocCount();
+ if (Config->ZCombreloc && NumRelativeRels)
+ add({IsRela ? DT_RELACOUNT : DT_RELCOUNT, NumRelativeRels});
+ }
+ }
+ if (In<ELFT>::RelaPlt->OutSec->Size > 0) {
+ add({DT_JMPREL, In<ELFT>::RelaPlt});
+ add({DT_PLTRELSZ, In<ELFT>::RelaPlt->OutSec->Size});
+ add({Config->EMachine == EM_MIPS ? DT_MIPS_PLTGOT : DT_PLTGOT,
+ In<ELFT>::GotPlt});
+ add({DT_PLTREL, uint64_t(Config->Rela ? DT_RELA : DT_REL)});
+ }
+
+ add({DT_SYMTAB, In<ELFT>::DynSymTab});
+ add({DT_SYMENT, sizeof(Elf_Sym)});
+ add({DT_STRTAB, In<ELFT>::DynStrTab});
+ add({DT_STRSZ, In<ELFT>::DynStrTab->getSize()});
+ if (In<ELFT>::GnuHashTab)
+ add({DT_GNU_HASH, In<ELFT>::GnuHashTab});
+ if (In<ELFT>::HashTab)
+ add({DT_HASH, In<ELFT>::HashTab});
+
+ if (Out<ELFT>::PreinitArray) {
+ add({DT_PREINIT_ARRAY, Out<ELFT>::PreinitArray});
+ add({DT_PREINIT_ARRAYSZ, Out<ELFT>::PreinitArray, Entry::SecSize});
+ }
+ if (Out<ELFT>::InitArray) {
+ add({DT_INIT_ARRAY, Out<ELFT>::InitArray});
+ add({DT_INIT_ARRAYSZ, Out<ELFT>::InitArray, Entry::SecSize});
+ }
+ if (Out<ELFT>::FiniArray) {
+ add({DT_FINI_ARRAY, Out<ELFT>::FiniArray});
+ add({DT_FINI_ARRAYSZ, Out<ELFT>::FiniArray, Entry::SecSize});
+ }
+
+ if (SymbolBody *B = Symtab<ELFT>::X->find(Config->Init))
+ add({DT_INIT, B});
+ if (SymbolBody *B = Symtab<ELFT>::X->find(Config->Fini))
+ add({DT_FINI, B});
+
+ bool HasVerNeed = In<ELFT>::VerNeed->getNeedNum() != 0;
+ if (HasVerNeed || In<ELFT>::VerDef)
+ add({DT_VERSYM, In<ELFT>::VerSym});
+ if (In<ELFT>::VerDef) {
+ add({DT_VERDEF, In<ELFT>::VerDef});
+ add({DT_VERDEFNUM, getVerDefNum()});
+ }
+ if (HasVerNeed) {
+ add({DT_VERNEED, In<ELFT>::VerNeed});
+ add({DT_VERNEEDNUM, In<ELFT>::VerNeed->getNeedNum()});
+ }
+
+ if (Config->EMachine == EM_MIPS) {
+ add({DT_MIPS_RLD_VERSION, 1});
+ add({DT_MIPS_FLAGS, RHF_NOTPOT});
+ add({DT_MIPS_BASE_ADDRESS, Config->ImageBase});
+ add({DT_MIPS_SYMTABNO, In<ELFT>::DynSymTab->getNumSymbols()});
+ add({DT_MIPS_LOCAL_GOTNO, In<ELFT>::MipsGot->getLocalEntriesNum()});
+ if (const SymbolBody *B = In<ELFT>::MipsGot->getFirstGlobalEntry())
+ add({DT_MIPS_GOTSYM, B->DynsymIndex});
+ else
+ add({DT_MIPS_GOTSYM, In<ELFT>::DynSymTab->getNumSymbols()});
+ add({DT_PLTGOT, In<ELFT>::MipsGot});
+ if (In<ELFT>::MipsRldMap)
+ add({DT_MIPS_RLD_MAP, In<ELFT>::MipsRldMap});
+ }
+
+ this->OutSec->Entsize = this->Entsize;
+ this->OutSec->Link = this->Link;
+
+ // +1 for DT_NULL
+ this->Size = (Entries.size() + 1) * this->Entsize;
+}
+
+template <class ELFT> void DynamicSection<ELFT>::writeTo(uint8_t *Buf) {
+ auto *P = reinterpret_cast<Elf_Dyn *>(Buf);
+
+ for (const Entry &E : Entries) {
+ P->d_tag = E.Tag;
+ switch (E.Kind) {
+ case Entry::SecAddr:
+ P->d_un.d_ptr = E.OutSec->Addr;
+ break;
+ case Entry::InSecAddr:
+ P->d_un.d_ptr = E.InSec->OutSec->Addr + E.InSec->OutSecOff;
+ break;
+ case Entry::SecSize:
+ P->d_un.d_val = E.OutSec->Size;
+ break;
+ case Entry::SymAddr:
+ P->d_un.d_ptr = E.Sym->template getVA<ELFT>();
+ break;
+ case Entry::PlainInt:
+ P->d_un.d_val = E.Val;
+ break;
+ }
+ ++P;
+ }
+}
+
+template <class ELFT>
+typename ELFT::uint DynamicReloc<ELFT>::getOffset() const {
+ if (OutputSec)
+ return OutputSec->Addr + OffsetInSec;
+ return InputSec->OutSec->Addr + InputSec->getOffset(OffsetInSec);
+}
+
+template <class ELFT>
+typename ELFT::uint DynamicReloc<ELFT>::getAddend() const {
+ if (UseSymVA)
+ return Sym->getVA<ELFT>(Addend);
+ return Addend;
+}
+
+template <class ELFT> uint32_t DynamicReloc<ELFT>::getSymIndex() const {
+ if (Sym && !UseSymVA)
+ return Sym->DynsymIndex;
+ return 0;
+}
+
+template <class ELFT>
+RelocationSection<ELFT>::RelocationSection(StringRef Name, bool Sort)
+ : SyntheticSection<ELFT>(SHF_ALLOC, Config->Rela ? SHT_RELA : SHT_REL,
+ sizeof(uintX_t), Name),
+ Sort(Sort) {
+ this->Entsize = Config->Rela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
+}
+
+template <class ELFT>
+void RelocationSection<ELFT>::addReloc(const DynamicReloc<ELFT> &Reloc) {
+ if (Reloc.Type == Target->RelativeRel)
+ ++NumRelativeRelocs;
+ Relocs.push_back(Reloc);
+}
+
+template <class ELFT, class RelTy>
+static bool compRelocations(const RelTy &A, const RelTy &B) {
+ bool AIsRel = A.getType(Config->Mips64EL) == Target->RelativeRel;
+ bool BIsRel = B.getType(Config->Mips64EL) == Target->RelativeRel;
+ if (AIsRel != BIsRel)
+ return AIsRel;
+
+ return A.getSymbol(Config->Mips64EL) < B.getSymbol(Config->Mips64EL);
+}
+
+template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *Buf) {
+ uint8_t *BufBegin = Buf;
+ for (const DynamicReloc<ELFT> &Rel : Relocs) {
+ auto *P = reinterpret_cast<Elf_Rela *>(Buf);
+ Buf += Config->Rela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
+
+ if (Config->Rela)
+ P->r_addend = Rel.getAddend();
+ P->r_offset = Rel.getOffset();
+ if (Config->EMachine == EM_MIPS && Rel.getInputSec() == In<ELFT>::MipsGot)
+ // Dynamic relocation against MIPS GOT section make deal TLS entries
+ // allocated in the end of the GOT. We need to adjust the offset to take
+ // in account 'local' and 'global' GOT entries.
+ P->r_offset += In<ELFT>::MipsGot->getTlsOffset();
+ P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->Mips64EL);
+ }
+
+ if (Sort) {
+ if (Config->Rela)
+ std::stable_sort((Elf_Rela *)BufBegin,
+ (Elf_Rela *)BufBegin + Relocs.size(),
+ compRelocations<ELFT, Elf_Rela>);
+ else
+ std::stable_sort((Elf_Rel *)BufBegin, (Elf_Rel *)BufBegin + Relocs.size(),
+ compRelocations<ELFT, Elf_Rel>);
+ }
+}
+
+template <class ELFT> unsigned RelocationSection<ELFT>::getRelocOffset() {
+ return this->Entsize * Relocs.size();
+}
+
+template <class ELFT> void RelocationSection<ELFT>::finalize() {
+ this->Link = In<ELFT>::DynSymTab ? In<ELFT>::DynSymTab->OutSec->SectionIndex
+ : In<ELFT>::SymTab->OutSec->SectionIndex;
+
+ // Set required output section properties.
+ this->OutSec->Link = this->Link;
+ this->OutSec->Entsize = this->Entsize;
+}
+
+template <class ELFT>
+SymbolTableSection<ELFT>::SymbolTableSection(
+ StringTableSection<ELFT> &StrTabSec)
+ : SyntheticSection<ELFT>(StrTabSec.isDynamic() ? (uintX_t)SHF_ALLOC : 0,
+ StrTabSec.isDynamic() ? SHT_DYNSYM : SHT_SYMTAB,
+ sizeof(uintX_t),
+ StrTabSec.isDynamic() ? ".dynsym" : ".symtab"),
+ StrTabSec(StrTabSec) {
+ this->Entsize = sizeof(Elf_Sym);
+}
+
+// Orders symbols according to their positions in the GOT,
+// in compliance with MIPS ABI rules.
+// See "Global Offset Table" in Chapter 5 in the following document
+// for detailed description:
+// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
+static bool sortMipsSymbols(const SymbolBody *L, const SymbolBody *R) {
+ // Sort entries related to non-local preemptible symbols by GOT indexes.
+ // All other entries go to the first part of GOT in arbitrary order.
+ bool LIsInLocalGot = !L->IsInGlobalMipsGot;
+ bool RIsInLocalGot = !R->IsInGlobalMipsGot;
+ if (LIsInLocalGot || RIsInLocalGot)
+ return !RIsInLocalGot;
+ return L->GotIndex < R->GotIndex;
+}
+
+static uint8_t getSymbolBinding(SymbolBody *Body) {
+ Symbol *S = Body->symbol();
+ if (Config->Relocatable)
+ return S->Binding;
+ uint8_t Visibility = S->Visibility;
+ if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED)
+ return STB_LOCAL;
+ if (Config->NoGnuUnique && S->Binding == STB_GNU_UNIQUE)
+ return STB_GLOBAL;
+ return S->Binding;
+}
+
+template <class ELFT> void SymbolTableSection<ELFT>::finalize() {
+ this->OutSec->Link = this->Link = StrTabSec.OutSec->SectionIndex;
+ this->OutSec->Info = this->Info = NumLocals + 1;
+ this->OutSec->Entsize = this->Entsize;
+
+ if (Config->Relocatable) {
+ size_t I = NumLocals;
+ for (const SymbolTableEntry &S : Symbols)
+ S.Symbol->DynsymIndex = ++I;
+ return;
+ }
+
+ if (!StrTabSec.isDynamic()) {
+ std::stable_sort(Symbols.begin(), Symbols.end(),
+ [](const SymbolTableEntry &L, const SymbolTableEntry &R) {
+ return getSymbolBinding(L.Symbol) == STB_LOCAL &&
+ getSymbolBinding(R.Symbol) != STB_LOCAL;
+ });
+ return;
+ }
+ if (In<ELFT>::GnuHashTab)
+ // NB: It also sorts Symbols to meet the GNU hash table requirements.
+ In<ELFT>::GnuHashTab->addSymbols(Symbols);
+ else if (Config->EMachine == EM_MIPS)
+ std::stable_sort(Symbols.begin(), Symbols.end(),
+ [](const SymbolTableEntry &L, const SymbolTableEntry &R) {
+ return sortMipsSymbols(L.Symbol, R.Symbol);
+ });
+ size_t I = 0;
+ for (const SymbolTableEntry &S : Symbols)
+ S.Symbol->DynsymIndex = ++I;
+}
+
+template <class ELFT> void SymbolTableSection<ELFT>::addSymbol(SymbolBody *B) {
+ Symbols.push_back({B, StrTabSec.addString(B->getName(), false)});
+}
+
+template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) {
+ Buf += sizeof(Elf_Sym);
+
+ // All symbols with STB_LOCAL binding precede the weak and global symbols.
+ // .dynsym only contains global symbols.
+ if (Config->Discard != DiscardPolicy::All && !StrTabSec.isDynamic())
+ writeLocalSymbols(Buf);
+
+ writeGlobalSymbols(Buf);
+}
+
+template <class ELFT>
+void SymbolTableSection<ELFT>::writeLocalSymbols(uint8_t *&Buf) {
+ // Iterate over all input object files to copy their local symbols
+ // to the output symbol table pointed by Buf.
+ for (ObjectFile<ELFT> *File : Symtab<ELFT>::X->getObjectFiles()) {
+ for (const std::pair<const DefinedRegular<ELFT> *, size_t> &P :
+ File->KeptLocalSyms) {
+ const DefinedRegular<ELFT> &Body = *P.first;
+ InputSectionBase<ELFT> *Section = Body.Section;
+ auto *ESym = reinterpret_cast<Elf_Sym *>(Buf);
+
+ if (!Section) {
+ ESym->st_shndx = SHN_ABS;
+ ESym->st_value = Body.Value;
+ } else {
+ const OutputSectionBase *OutSec = Section->OutSec;
+ ESym->st_shndx = OutSec->SectionIndex;
+ ESym->st_value = OutSec->Addr + Section->getOffset(Body);
+ }
+ ESym->st_name = P.second;
+ ESym->st_size = Body.template getSize<ELFT>();
+ ESym->setBindingAndType(STB_LOCAL, Body.Type);
+ Buf += sizeof(*ESym);
+ }
+ }
+}
+
+template <class ELFT>
+void SymbolTableSection<ELFT>::writeGlobalSymbols(uint8_t *Buf) {
+ // Write the internal symbol table contents to the output symbol table
+ // pointed by Buf.
+ auto *ESym = reinterpret_cast<Elf_Sym *>(Buf);
+ for (const SymbolTableEntry &S : Symbols) {
+ SymbolBody *Body = S.Symbol;
+ size_t StrOff = S.StrTabOffset;
+
+ uint8_t Type = Body->Type;
+ uintX_t Size = Body->getSize<ELFT>();
+
+ ESym->setBindingAndType(getSymbolBinding(Body), Type);
+ ESym->st_size = Size;
+ ESym->st_name = StrOff;
+ ESym->setVisibility(Body->symbol()->Visibility);
+ ESym->st_value = Body->getVA<ELFT>();
+
+ if (const OutputSectionBase *OutSec = getOutputSection(Body))
+ ESym->st_shndx = OutSec->SectionIndex;
+ else if (isa<DefinedRegular<ELFT>>(Body))
+ ESym->st_shndx = SHN_ABS;
+
+ if (Config->EMachine == EM_MIPS) {
+ // On MIPS we need to mark symbol which has a PLT entry and requires
+ // pointer equality by STO_MIPS_PLT flag. That is necessary to help
+ // dynamic linker distinguish such symbols and MIPS lazy-binding stubs.
+ // https://sourceware.org/ml/binutils/2008-07/txt00000.txt
+ if (Body->isInPlt() && Body->NeedsCopyOrPltAddr)
+ ESym->st_other |= STO_MIPS_PLT;
+ if (Config->Relocatable) {
+ auto *D = dyn_cast<DefinedRegular<ELFT>>(Body);
+ if (D && D->isMipsPIC())
+ ESym->st_other |= STO_MIPS_PIC;
+ }
+ }
+ ++ESym;
+ }
+}
+
+template <class ELFT>
+const OutputSectionBase *
+SymbolTableSection<ELFT>::getOutputSection(SymbolBody *Sym) {
+ switch (Sym->kind()) {
+ case SymbolBody::DefinedSyntheticKind:
+ return cast<DefinedSynthetic>(Sym)->Section;
+ case SymbolBody::DefinedRegularKind: {
+ auto &D = cast<DefinedRegular<ELFT>>(*Sym);
+ if (D.Section)
+ return D.Section->OutSec;
+ break;
+ }
+ case SymbolBody::DefinedCommonKind:
+ return In<ELFT>::Common->OutSec;
+ case SymbolBody::SharedKind:
+ if (cast<SharedSymbol<ELFT>>(Sym)->needsCopy())
+ return Out<ELFT>::Bss;
+ break;
+ case SymbolBody::UndefinedKind:
+ case SymbolBody::LazyArchiveKind:
+ case SymbolBody::LazyObjectKind:
+ break;
+ }
+ return nullptr;
+}
+
+template <class ELFT>
+GnuHashTableSection<ELFT>::GnuHashTableSection()
+ : SyntheticSection<ELFT>(SHF_ALLOC, SHT_GNU_HASH, sizeof(uintX_t),
+ ".gnu.hash") {
+ this->Entsize = ELFT::Is64Bits ? 0 : 4;
+}
+
+template <class ELFT>
+unsigned GnuHashTableSection<ELFT>::calcNBuckets(unsigned NumHashed) {
+ if (!NumHashed)
+ return 0;
+
+ // These values are prime numbers which are not greater than 2^(N-1) + 1.
+ // In result, for any particular NumHashed we return a prime number
+ // which is not greater than NumHashed.
+ static const unsigned Primes[] = {
+ 1, 1, 3, 3, 7, 13, 31, 61, 127, 251,
+ 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071};
+
+ return Primes[std::min<unsigned>(Log2_32_Ceil(NumHashed),
+ array_lengthof(Primes) - 1)];
+}
+
+// Bloom filter estimation: at least 8 bits for each hashed symbol.
+// GNU Hash table requirement: it should be a power of 2,
+// the minimum value is 1, even for an empty table.
+// Expected results for a 32-bit target:
+// calcMaskWords(0..4) = 1
+// calcMaskWords(5..8) = 2
+// calcMaskWords(9..16) = 4
+// For a 64-bit target:
+// calcMaskWords(0..8) = 1
+// calcMaskWords(9..16) = 2
+// calcMaskWords(17..32) = 4
+template <class ELFT>
+unsigned GnuHashTableSection<ELFT>::calcMaskWords(unsigned NumHashed) {
+ if (!NumHashed)
+ return 1;
+ return NextPowerOf2((NumHashed - 1) / sizeof(Elf_Off));
+}
+
+template <class ELFT> void GnuHashTableSection<ELFT>::finalize() {
+ unsigned NumHashed = Symbols.size();
+ NBuckets = calcNBuckets(NumHashed);
+ MaskWords = calcMaskWords(NumHashed);
+ // Second hash shift estimation: just predefined values.
+ Shift2 = ELFT::Is64Bits ? 6 : 5;
+
+ this->OutSec->Entsize = this->Entsize;
+ this->OutSec->Link = this->Link = In<ELFT>::DynSymTab->OutSec->SectionIndex;
+ this->Size = sizeof(Elf_Word) * 4 // Header
+ + sizeof(Elf_Off) * MaskWords // Bloom Filter
+ + sizeof(Elf_Word) * NBuckets // Hash Buckets
+ + sizeof(Elf_Word) * NumHashed; // Hash Values
+}
+
+template <class ELFT> void GnuHashTableSection<ELFT>::writeTo(uint8_t *Buf) {
+ writeHeader(Buf);
+ if (Symbols.empty())
+ return;
+ writeBloomFilter(Buf);
+ writeHashTable(Buf);
+}
+
+template <class ELFT>
+void GnuHashTableSection<ELFT>::writeHeader(uint8_t *&Buf) {
+ auto *P = reinterpret_cast<Elf_Word *>(Buf);
+ *P++ = NBuckets;
+ *P++ = In<ELFT>::DynSymTab->getNumSymbols() - Symbols.size();
+ *P++ = MaskWords;
+ *P++ = Shift2;
+ Buf = reinterpret_cast<uint8_t *>(P);
+}
+
+template <class ELFT>
+void GnuHashTableSection<ELFT>::writeBloomFilter(uint8_t *&Buf) {
+ unsigned C = sizeof(Elf_Off) * 8;
+
+ auto *Masks = reinterpret_cast<Elf_Off *>(Buf);
+ for (const SymbolData &Sym : Symbols) {
+ size_t Pos = (Sym.Hash / C) & (MaskWords - 1);
+ uintX_t V = (uintX_t(1) << (Sym.Hash % C)) |
+ (uintX_t(1) << ((Sym.Hash >> Shift2) % C));
+ Masks[Pos] |= V;
+ }
+ Buf += sizeof(Elf_Off) * MaskWords;
+}
+
+template <class ELFT>
+void GnuHashTableSection<ELFT>::writeHashTable(uint8_t *Buf) {
+ Elf_Word *Buckets = reinterpret_cast<Elf_Word *>(Buf);
+ Elf_Word *Values = Buckets + NBuckets;
+
+ int PrevBucket = -1;
+ int I = 0;
+ for (const SymbolData &Sym : Symbols) {
+ int Bucket = Sym.Hash % NBuckets;
+ assert(PrevBucket <= Bucket);
+ if (Bucket != PrevBucket) {
+ Buckets[Bucket] = Sym.Body->DynsymIndex;
+ PrevBucket = Bucket;
+ if (I > 0)
+ Values[I - 1] |= 1;
+ }
+ Values[I] = Sym.Hash & ~1;
+ ++I;
+ }
+ if (I > 0)
+ Values[I - 1] |= 1;
+}
+
+static uint32_t hashGnu(StringRef Name) {
+ uint32_t H = 5381;
+ for (uint8_t C : Name)
+ H = (H << 5) + H + C;
+ return H;
+}
+
+// Add symbols to this symbol hash table. Note that this function
+// destructively sort a given vector -- which is needed because
+// GNU-style hash table places some sorting requirements.
+template <class ELFT>
+void GnuHashTableSection<ELFT>::addSymbols(std::vector<SymbolTableEntry> &V) {
+ // Ideally this will just be 'auto' but GCC 6.1 is not able
+ // to deduce it correctly.
+ std::vector<SymbolTableEntry>::iterator Mid =
+ std::stable_partition(V.begin(), V.end(), [](const SymbolTableEntry &S) {
+ return S.Symbol->isUndefined();
+ });
+ if (Mid == V.end())
+ return;
+ for (auto I = Mid, E = V.end(); I != E; ++I) {
+ SymbolBody *B = I->Symbol;
+ size_t StrOff = I->StrTabOffset;
+ Symbols.push_back({B, StrOff, hashGnu(B->getName())});
+ }
+
+ unsigned NBuckets = calcNBuckets(Symbols.size());
+ std::stable_sort(Symbols.begin(), Symbols.end(),
+ [&](const SymbolData &L, const SymbolData &R) {
+ return L.Hash % NBuckets < R.Hash % NBuckets;
+ });
+
+ V.erase(Mid, V.end());
+ for (const SymbolData &Sym : Symbols)
+ V.push_back({Sym.Body, Sym.STName});
+}
+
+template <class ELFT>
+HashTableSection<ELFT>::HashTableSection()
+ : SyntheticSection<ELFT>(SHF_ALLOC, SHT_HASH, sizeof(Elf_Word), ".hash") {
+ this->Entsize = sizeof(Elf_Word);
+}
+
+template <class ELFT> void HashTableSection<ELFT>::finalize() {
+ this->OutSec->Link = this->Link = In<ELFT>::DynSymTab->OutSec->SectionIndex;
+ this->OutSec->Entsize = this->Entsize;
+
+ unsigned NumEntries = 2; // nbucket and nchain.
+ NumEntries += In<ELFT>::DynSymTab->getNumSymbols(); // The chain entries.
+
+ // Create as many buckets as there are symbols.
+ // FIXME: This is simplistic. We can try to optimize it, but implementing
+ // support for SHT_GNU_HASH is probably even more profitable.
+ NumEntries += In<ELFT>::DynSymTab->getNumSymbols();
+ this->Size = NumEntries * sizeof(Elf_Word);
+}
+
+template <class ELFT> void HashTableSection<ELFT>::writeTo(uint8_t *Buf) {
+ unsigned NumSymbols = In<ELFT>::DynSymTab->getNumSymbols();
+ auto *P = reinterpret_cast<Elf_Word *>(Buf);
+ *P++ = NumSymbols; // nbucket
+ *P++ = NumSymbols; // nchain
+
+ Elf_Word *Buckets = P;
+ Elf_Word *Chains = P + NumSymbols;
+
+ for (const SymbolTableEntry &S : In<ELFT>::DynSymTab->getSymbols()) {
+ SymbolBody *Body = S.Symbol;
+ StringRef Name = Body->getName();
+ unsigned I = Body->DynsymIndex;
+ uint32_t Hash = hashSysV(Name) % NumSymbols;
+ Chains[I] = Buckets[Hash];
+ Buckets[Hash] = I;
+ }
+}
+
+template <class ELFT>
+PltSection<ELFT>::PltSection()
+ : SyntheticSection<ELFT>(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16,
+ ".plt") {}
+
+template <class ELFT> void PltSection<ELFT>::writeTo(uint8_t *Buf) {
+ // At beginning of PLT, we have code to call the dynamic linker
+ // to resolve dynsyms at runtime. Write such code.
+ Target->writePltHeader(Buf);
+ size_t Off = Target->PltHeaderSize;
+
+ for (auto &I : Entries) {
+ const SymbolBody *B = I.first;
+ unsigned RelOff = I.second;
+ uint64_t Got = B->getGotPltVA<ELFT>();
+ uint64_t Plt = this->getVA() + Off;
+ Target->writePlt(Buf + Off, Got, Plt, B->PltIndex, RelOff);
+ Off += Target->PltEntrySize;
+ }
+}
+
+template <class ELFT> void PltSection<ELFT>::addEntry(SymbolBody &Sym) {
+ Sym.PltIndex = Entries.size();
+ unsigned RelOff = In<ELFT>::RelaPlt->getRelocOffset();
+ Entries.push_back(std::make_pair(&Sym, RelOff));
+}
+
+template <class ELFT> size_t PltSection<ELFT>::getSize() const {
+ return Target->PltHeaderSize + Entries.size() * Target->PltEntrySize;
+}
+
+template <class ELFT>
+IpltSection<ELFT>::IpltSection()
+ : SyntheticSection<ELFT>(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16,
+ ".plt") {}
+
+template <class ELFT> void IpltSection<ELFT>::writeTo(uint8_t *Buf) {
+ // The IRelative relocations do not support lazy binding so no header is
+ // needed
+ size_t Off = 0;
+ for (auto &I : Entries) {
+ const SymbolBody *B = I.first;
+ unsigned RelOff = I.second + In<ELFT>::Plt->getSize();
+ uint64_t Got = B->getGotPltVA<ELFT>();
+ uint64_t Plt = this->getVA() + Off;
+ Target->writePlt(Buf + Off, Got, Plt, B->PltIndex, RelOff);
+ Off += Target->PltEntrySize;
+ }
+}
+
+template <class ELFT> void IpltSection<ELFT>::addEntry(SymbolBody &Sym) {
+ Sym.PltIndex = Entries.size();
+ Sym.IsInIplt = true;
+ unsigned RelOff = In<ELFT>::RelaIplt->getRelocOffset();
+ Entries.push_back(std::make_pair(&Sym, RelOff));
+}
+
+template <class ELFT> size_t IpltSection<ELFT>::getSize() const {
+ return Entries.size() * Target->PltEntrySize;
+}
+
+template <class ELFT>
+GdbIndexSection<ELFT>::GdbIndexSection()
+ : SyntheticSection<ELFT>(0, SHT_PROGBITS, 1, ".gdb_index"),
+ StringPool(llvm::StringTableBuilder::ELF) {}
+
+template <class ELFT> void GdbIndexSection<ELFT>::parseDebugSections() {
+ for (InputSectionBase<ELFT> *S : Symtab<ELFT>::X->Sections)
+ if (InputSection<ELFT> *IS = dyn_cast<InputSection<ELFT>>(S))
+ if (IS->OutSec && IS->Name == ".debug_info")
+ readDwarf(IS);
+}
+
+// Iterative hash function for symbol's name is described in .gdb_index format
+// specification. Note that we use one for version 5 to 7 here, it is different
+// for version 4.
+static uint32_t hash(StringRef Str) {
+ uint32_t R = 0;
+ for (uint8_t C : Str)
+ R = R * 67 + tolower(C) - 113;
+ return R;
+}
+
+template <class ELFT>
+void GdbIndexSection<ELFT>::readDwarf(InputSection<ELFT> *I) {
+ GdbIndexBuilder<ELFT> Builder(I);
+ if (ErrorCount)
+ return;
+
+ size_t CuId = CompilationUnits.size();
+ std::vector<std::pair<uintX_t, uintX_t>> CuList = Builder.readCUList();
+ CompilationUnits.insert(CompilationUnits.end(), CuList.begin(), CuList.end());
+
+ std::vector<AddressEntry<ELFT>> AddrArea = Builder.readAddressArea(CuId);
+ AddressArea.insert(AddressArea.end(), AddrArea.begin(), AddrArea.end());
+
+ std::vector<std::pair<StringRef, uint8_t>> NamesAndTypes =
+ Builder.readPubNamesAndTypes();
+
+ for (std::pair<StringRef, uint8_t> &Pair : NamesAndTypes) {
+ uint32_t Hash = hash(Pair.first);
+ size_t Offset = StringPool.add(Pair.first);
+
+ bool IsNew;
+ GdbSymbol *Sym;
+ std::tie(IsNew, Sym) = SymbolTable.add(Hash, Offset);
+ if (IsNew) {
+ Sym->CuVectorIndex = CuVectors.size();
+ CuVectors.push_back({{CuId, Pair.second}});
+ continue;
+ }
+
+ std::vector<std::pair<uint32_t, uint8_t>> &CuVec =
+ CuVectors[Sym->CuVectorIndex];
+ CuVec.push_back({CuId, Pair.second});
+ }
+}
+
+template <class ELFT> void GdbIndexSection<ELFT>::finalize() {
+ if (Finalized)
+ return;
+ Finalized = true;
+
+ parseDebugSections();
+
+ // GdbIndex header consist from version fields
+ // and 5 more fields with different kinds of offsets.
+ CuTypesOffset = CuListOffset + CompilationUnits.size() * CompilationUnitSize;
+ SymTabOffset = CuTypesOffset + AddressArea.size() * AddressEntrySize;
+
+ ConstantPoolOffset =
+ SymTabOffset + SymbolTable.getCapacity() * SymTabEntrySize;
+
+ for (std::vector<std::pair<uint32_t, uint8_t>> &CuVec : CuVectors) {
+ CuVectorsOffset.push_back(CuVectorsSize);
+ CuVectorsSize += OffsetTypeSize * (CuVec.size() + 1);
+ }
+ StringPoolOffset = ConstantPoolOffset + CuVectorsSize;
+
+ StringPool.finalizeInOrder();
+}
+
+template <class ELFT> size_t GdbIndexSection<ELFT>::getSize() const {
+ const_cast<GdbIndexSection<ELFT> *>(this)->finalize();
+ return StringPoolOffset + StringPool.getSize();
+}
+
+template <class ELFT> void GdbIndexSection<ELFT>::writeTo(uint8_t *Buf) {
+ write32le(Buf, 7); // Write version.
+ write32le(Buf + 4, CuListOffset); // CU list offset.
+ write32le(Buf + 8, CuTypesOffset); // Types CU list offset.
+ write32le(Buf + 12, CuTypesOffset); // Address area offset.
+ write32le(Buf + 16, SymTabOffset); // Symbol table offset.
+ write32le(Buf + 20, ConstantPoolOffset); // Constant pool offset.
+ Buf += 24;
+
+ // Write the CU list.
+ for (std::pair<uintX_t, uintX_t> CU : CompilationUnits) {
+ write64le(Buf, CU.first);
+ write64le(Buf + 8, CU.second);
+ Buf += 16;
+ }
+
+ // Write the address area.
+ for (AddressEntry<ELFT> &E : AddressArea) {
+ uintX_t BaseAddr = E.Section->OutSec->Addr + E.Section->getOffset(0);
+ write64le(Buf, BaseAddr + E.LowAddress);
+ write64le(Buf + 8, BaseAddr + E.HighAddress);
+ write32le(Buf + 16, E.CuIndex);
+ Buf += 20;
+ }
+
+ // Write the symbol table.
+ for (size_t I = 0; I < SymbolTable.getCapacity(); ++I) {
+ GdbSymbol *Sym = SymbolTable.getSymbol(I);
+ if (Sym) {
+ size_t NameOffset =
+ Sym->NameOffset + StringPoolOffset - ConstantPoolOffset;
+ size_t CuVectorOffset = CuVectorsOffset[Sym->CuVectorIndex];
+ write32le(Buf, NameOffset);
+ write32le(Buf + 4, CuVectorOffset);
+ }
+ Buf += 8;
+ }
+
+ // Write the CU vectors into the constant pool.
+ for (std::vector<std::pair<uint32_t, uint8_t>> &CuVec : CuVectors) {
+ write32le(Buf, CuVec.size());
+ Buf += 4;
+ for (std::pair<uint32_t, uint8_t> &P : CuVec) {
+ uint32_t Index = P.first;
+ uint8_t Flags = P.second;
+ Index |= Flags << 24;
+ write32le(Buf, Index);
+ Buf += 4;
+ }
+ }
+
+ StringPool.write(Buf);
+}
+
+template <class ELFT> bool GdbIndexSection<ELFT>::empty() const {
+ return !Out<ELFT>::DebugInfo;
+}
+
+template <class ELFT>
+EhFrameHeader<ELFT>::EhFrameHeader()
+ : SyntheticSection<ELFT>(SHF_ALLOC, SHT_PROGBITS, 1, ".eh_frame_hdr") {}
+
+// .eh_frame_hdr contains a binary search table of pointers to FDEs.
+// Each entry of the search table consists of two values,
+// the starting PC from where FDEs covers, and the FDE's address.
+// It is sorted by PC.
+template <class ELFT> void EhFrameHeader<ELFT>::writeTo(uint8_t *Buf) {
+ const endianness E = ELFT::TargetEndianness;
+
+ // Sort the FDE list by their PC and uniqueify. Usually there is only
+ // one FDE for a PC (i.e. function), but if ICF merges two functions
+ // into one, there can be more than one FDEs pointing to the address.
+ auto Less = [](const FdeData &A, const FdeData &B) { return A.Pc < B.Pc; };
+ std::stable_sort(Fdes.begin(), Fdes.end(), Less);
+ auto Eq = [](const FdeData &A, const FdeData &B) { return A.Pc == B.Pc; };
+ Fdes.erase(std::unique(Fdes.begin(), Fdes.end(), Eq), Fdes.end());
+
+ Buf[0] = 1;
+ Buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4;
+ Buf[2] = DW_EH_PE_udata4;
+ Buf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;
+ write32<E>(Buf + 4, Out<ELFT>::EhFrame->Addr - this->getVA() - 4);
+ write32<E>(Buf + 8, Fdes.size());
+ Buf += 12;
+
+ uintX_t VA = this->getVA();
+ for (FdeData &Fde : Fdes) {
+ write32<E>(Buf, Fde.Pc - VA);
+ write32<E>(Buf + 4, Fde.FdeVA - VA);
+ Buf += 8;
+ }
+}
+
+template <class ELFT> size_t EhFrameHeader<ELFT>::getSize() const {
+ // .eh_frame_hdr has a 12 bytes header followed by an array of FDEs.
+ return 12 + Out<ELFT>::EhFrame->NumFdes * 8;
+}
+
+template <class ELFT>
+void EhFrameHeader<ELFT>::addFde(uint32_t Pc, uint32_t FdeVA) {
+ Fdes.push_back({Pc, FdeVA});
+}
+
+template <class ELFT> bool EhFrameHeader<ELFT>::empty() const {
+ return Out<ELFT>::EhFrame->empty();
+}
+
+template <class ELFT>
+VersionDefinitionSection<ELFT>::VersionDefinitionSection()
+ : SyntheticSection<ELFT>(SHF_ALLOC, SHT_GNU_verdef, sizeof(uint32_t),
+ ".gnu.version_d") {}
+
+static StringRef getFileDefName() {
+ if (!Config->SoName.empty())
+ return Config->SoName;
+ return Config->OutputFile;
+}
+
+template <class ELFT> void VersionDefinitionSection<ELFT>::finalize() {
+ FileDefNameOff = In<ELFT>::DynStrTab->addString(getFileDefName());
+ for (VersionDefinition &V : Config->VersionDefinitions)
+ V.NameOff = In<ELFT>::DynStrTab->addString(V.Name);
+
+ this->OutSec->Link = this->Link = In<ELFT>::DynStrTab->OutSec->SectionIndex;
+
+ // sh_info should be set to the number of definitions. This fact is missed in
+ // documentation, but confirmed by binutils community:
+ // https://sourceware.org/ml/binutils/2014-11/msg00355.html
+ this->OutSec->Info = this->Info = getVerDefNum();
+}
+
+template <class ELFT>
+void VersionDefinitionSection<ELFT>::writeOne(uint8_t *Buf, uint32_t Index,
+ StringRef Name, size_t NameOff) {
+ auto *Verdef = reinterpret_cast<Elf_Verdef *>(Buf);
+ Verdef->vd_version = 1;
+ Verdef->vd_cnt = 1;
+ Verdef->vd_aux = sizeof(Elf_Verdef);
+ Verdef->vd_next = sizeof(Elf_Verdef) + sizeof(Elf_Verdaux);
+ Verdef->vd_flags = (Index == 1 ? VER_FLG_BASE : 0);
+ Verdef->vd_ndx = Index;
+ Verdef->vd_hash = hashSysV(Name);
+
+ auto *Verdaux = reinterpret_cast<Elf_Verdaux *>(Buf + sizeof(Elf_Verdef));
+ Verdaux->vda_name = NameOff;
+ Verdaux->vda_next = 0;
+}
+
+template <class ELFT>
+void VersionDefinitionSection<ELFT>::writeTo(uint8_t *Buf) {
+ writeOne(Buf, 1, getFileDefName(), FileDefNameOff);
+
+ for (VersionDefinition &V : Config->VersionDefinitions) {
+ Buf += sizeof(Elf_Verdef) + sizeof(Elf_Verdaux);
+ writeOne(Buf, V.Id, V.Name, V.NameOff);
+ }
+
+ // Need to terminate the last version definition.
+ Elf_Verdef *Verdef = reinterpret_cast<Elf_Verdef *>(Buf);
+ Verdef->vd_next = 0;
+}
+
+template <class ELFT> size_t VersionDefinitionSection<ELFT>::getSize() const {
+ return (sizeof(Elf_Verdef) + sizeof(Elf_Verdaux)) * getVerDefNum();
+}
+
+template <class ELFT>
+VersionTableSection<ELFT>::VersionTableSection()
+ : SyntheticSection<ELFT>(SHF_ALLOC, SHT_GNU_versym, sizeof(uint16_t),
+ ".gnu.version") {}
+
+template <class ELFT> void VersionTableSection<ELFT>::finalize() {
+ this->OutSec->Entsize = this->Entsize = sizeof(Elf_Versym);
+ // At the moment of june 2016 GNU docs does not mention that sh_link field
+ // should be set, but Sun docs do. Also readelf relies on this field.
+ this->OutSec->Link = this->Link = In<ELFT>::DynSymTab->OutSec->SectionIndex;
+}
+
+template <class ELFT> size_t VersionTableSection<ELFT>::getSize() const {
+ return sizeof(Elf_Versym) * (In<ELFT>::DynSymTab->getSymbols().size() + 1);
+}
+
+template <class ELFT> void VersionTableSection<ELFT>::writeTo(uint8_t *Buf) {
+ auto *OutVersym = reinterpret_cast<Elf_Versym *>(Buf) + 1;
+ for (const SymbolTableEntry &S : In<ELFT>::DynSymTab->getSymbols()) {
+ OutVersym->vs_index = S.Symbol->symbol()->VersionId;
+ ++OutVersym;
+ }
+}
+
+template <class ELFT> bool VersionTableSection<ELFT>::empty() const {
+ return !In<ELFT>::VerDef && In<ELFT>::VerNeed->empty();
+}
+
+template <class ELFT>
+VersionNeedSection<ELFT>::VersionNeedSection()
+ : SyntheticSection<ELFT>(SHF_ALLOC, SHT_GNU_verneed, sizeof(uint32_t),
+ ".gnu.version_r") {
+ // Identifiers in verneed section start at 2 because 0 and 1 are reserved
+ // for VER_NDX_LOCAL and VER_NDX_GLOBAL.
+ // First identifiers are reserved by verdef section if it exist.
+ NextIndex = getVerDefNum() + 1;
+}
+
+template <class ELFT>
+void VersionNeedSection<ELFT>::addSymbol(SharedSymbol<ELFT> *SS) {
+ if (!SS->Verdef) {
+ SS->symbol()->VersionId = VER_NDX_GLOBAL;
+ return;
+ }
+ SharedFile<ELFT> *F = SS->file();
+ // If we don't already know that we need an Elf_Verneed for this DSO, prepare
+ // to create one by adding it to our needed list and creating a dynstr entry
+ // for the soname.
+ if (F->VerdefMap.empty())
+ Needed.push_back({F, In<ELFT>::DynStrTab->addString(F->getSoName())});
+ typename SharedFile<ELFT>::NeededVer &NV = F->VerdefMap[SS->Verdef];
+ // If we don't already know that we need an Elf_Vernaux for this Elf_Verdef,
+ // prepare to create one by allocating a version identifier and creating a
+ // dynstr entry for the version name.
+ if (NV.Index == 0) {
+ NV.StrTab = In<ELFT>::DynStrTab->addString(
+ SS->file()->getStringTable().data() + SS->Verdef->getAux()->vda_name);
+ NV.Index = NextIndex++;
+ }
+ SS->symbol()->VersionId = NV.Index;
+}
+
+template <class ELFT> void VersionNeedSection<ELFT>::writeTo(uint8_t *Buf) {
+ // The Elf_Verneeds need to appear first, followed by the Elf_Vernauxs.
+ auto *Verneed = reinterpret_cast<Elf_Verneed *>(Buf);
+ auto *Vernaux = reinterpret_cast<Elf_Vernaux *>(Verneed + Needed.size());
+
+ for (std::pair<SharedFile<ELFT> *, size_t> &P : Needed) {
+ // Create an Elf_Verneed for this DSO.
+ Verneed->vn_version = 1;
+ Verneed->vn_cnt = P.first->VerdefMap.size();
+ Verneed->vn_file = P.second;
+ Verneed->vn_aux =
+ reinterpret_cast<char *>(Vernaux) - reinterpret_cast<char *>(Verneed);
+ Verneed->vn_next = sizeof(Elf_Verneed);
+ ++Verneed;
+
+ // Create the Elf_Vernauxs for this Elf_Verneed. The loop iterates over
+ // VerdefMap, which will only contain references to needed version
+ // definitions. Each Elf_Vernaux is based on the information contained in
+ // the Elf_Verdef in the source DSO. This loop iterates over a std::map of
+ // pointers, but is deterministic because the pointers refer to Elf_Verdef
+ // data structures within a single input file.
+ for (auto &NV : P.first->VerdefMap) {
+ Vernaux->vna_hash = NV.first->vd_hash;
+ Vernaux->vna_flags = 0;
+ Vernaux->vna_other = NV.second.Index;
+ Vernaux->vna_name = NV.second.StrTab;
+ Vernaux->vna_next = sizeof(Elf_Vernaux);
+ ++Vernaux;
+ }
+
+ Vernaux[-1].vna_next = 0;
+ }
+ Verneed[-1].vn_next = 0;
+}
+
+template <class ELFT> void VersionNeedSection<ELFT>::finalize() {
+ this->OutSec->Link = this->Link = In<ELFT>::DynStrTab->OutSec->SectionIndex;
+ this->OutSec->Info = this->Info = Needed.size();
+}
+
+template <class ELFT> size_t VersionNeedSection<ELFT>::getSize() const {
+ unsigned Size = Needed.size() * sizeof(Elf_Verneed);
+ for (const std::pair<SharedFile<ELFT> *, size_t> &P : Needed)
+ Size += P.first->VerdefMap.size() * sizeof(Elf_Vernaux);
+ return Size;
+}
+
+template <class ELFT> bool VersionNeedSection<ELFT>::empty() const {
+ return getNeedNum() == 0;
+}
+
+template <class ELFT>
+MipsRldMapSection<ELFT>::MipsRldMapSection()
+ : SyntheticSection<ELFT>(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
+ sizeof(typename ELFT::uint), ".rld_map") {}
+
+template <class ELFT> void MipsRldMapSection<ELFT>::writeTo(uint8_t *Buf) {
+ // Apply filler from linker script.
+ uint64_t Filler = Script<ELFT>::X->getFiller(this->Name);
+ Filler = (Filler << 32) | Filler;
+ memcpy(Buf, &Filler, getSize());
+}
+
+template <class ELFT>
+ARMExidxSentinelSection<ELFT>::ARMExidxSentinelSection()
+ : SyntheticSection<ELFT>(SHF_ALLOC | SHF_LINK_ORDER, SHT_ARM_EXIDX,
+ sizeof(typename ELFT::uint), ".ARM.exidx") {}
+
+// Write a terminating sentinel entry to the end of the .ARM.exidx table.
+// This section will have been sorted last in the .ARM.exidx table.
+// This table entry will have the form:
+// | PREL31 upper bound of code that has exception tables | EXIDX_CANTUNWIND |
+template <class ELFT>
+void ARMExidxSentinelSection<ELFT>::writeTo(uint8_t *Buf) {
+ // Get the InputSection before us, we are by definition last
+ auto RI = cast<OutputSection<ELFT>>(this->OutSec)->Sections.rbegin();
+ InputSection<ELFT> *LE = *(++RI);
+ InputSection<ELFT> *LC = cast<InputSection<ELFT>>(LE->getLinkOrderDep());
+ uint64_t S = LC->OutSec->Addr + LC->getOffset(LC->getSize());
+ uint64_t P = this->getVA();
+ Target->relocateOne(Buf, R_ARM_PREL31, S - P);
+ write32le(Buf + 4, 0x1);
+}
+
+template InputSection<ELF32LE> *elf::createCommonSection();
+template InputSection<ELF32BE> *elf::createCommonSection();
+template InputSection<ELF64LE> *elf::createCommonSection();
+template InputSection<ELF64BE> *elf::createCommonSection();
+
+template InputSection<ELF32LE> *elf::createInterpSection();
+template InputSection<ELF32BE> *elf::createInterpSection();
+template InputSection<ELF64LE> *elf::createInterpSection();
+template InputSection<ELF64BE> *elf::createInterpSection();
+
+template MergeInputSection<ELF32LE> *elf::createCommentSection();
+template MergeInputSection<ELF32BE> *elf::createCommentSection();
+template MergeInputSection<ELF64LE> *elf::createCommentSection();
+template MergeInputSection<ELF64BE> *elf::createCommentSection();
+
+template class elf::MipsAbiFlagsSection<ELF32LE>;
+template class elf::MipsAbiFlagsSection<ELF32BE>;
+template class elf::MipsAbiFlagsSection<ELF64LE>;
+template class elf::MipsAbiFlagsSection<ELF64BE>;
+
+template class elf::MipsOptionsSection<ELF32LE>;
+template class elf::MipsOptionsSection<ELF32BE>;
+template class elf::MipsOptionsSection<ELF64LE>;
+template class elf::MipsOptionsSection<ELF64BE>;
+
+template class elf::MipsReginfoSection<ELF32LE>;
+template class elf::MipsReginfoSection<ELF32BE>;
+template class elf::MipsReginfoSection<ELF64LE>;
+template class elf::MipsReginfoSection<ELF64BE>;
+
+template class elf::BuildIdSection<ELF32LE>;
+template class elf::BuildIdSection<ELF32BE>;
+template class elf::BuildIdSection<ELF64LE>;
+template class elf::BuildIdSection<ELF64BE>;
+
+template class elf::GotSection<ELF32LE>;
+template class elf::GotSection<ELF32BE>;
+template class elf::GotSection<ELF64LE>;
+template class elf::GotSection<ELF64BE>;
+
+template class elf::MipsGotSection<ELF32LE>;
+template class elf::MipsGotSection<ELF32BE>;
+template class elf::MipsGotSection<ELF64LE>;
+template class elf::MipsGotSection<ELF64BE>;
+
+template class elf::GotPltSection<ELF32LE>;
+template class elf::GotPltSection<ELF32BE>;
+template class elf::GotPltSection<ELF64LE>;
+template class elf::GotPltSection<ELF64BE>;
+
+template class elf::IgotPltSection<ELF32LE>;
+template class elf::IgotPltSection<ELF32BE>;
+template class elf::IgotPltSection<ELF64LE>;
+template class elf::IgotPltSection<ELF64BE>;
+
+template class elf::StringTableSection<ELF32LE>;
+template class elf::StringTableSection<ELF32BE>;
+template class elf::StringTableSection<ELF64LE>;
+template class elf::StringTableSection<ELF64BE>;
+
+template class elf::DynamicSection<ELF32LE>;
+template class elf::DynamicSection<ELF32BE>;
+template class elf::DynamicSection<ELF64LE>;
+template class elf::DynamicSection<ELF64BE>;
+
+template class elf::RelocationSection<ELF32LE>;
+template class elf::RelocationSection<ELF32BE>;
+template class elf::RelocationSection<ELF64LE>;
+template class elf::RelocationSection<ELF64BE>;
+
+template class elf::SymbolTableSection<ELF32LE>;
+template class elf::SymbolTableSection<ELF32BE>;
+template class elf::SymbolTableSection<ELF64LE>;
+template class elf::SymbolTableSection<ELF64BE>;
+
+template class elf::GnuHashTableSection<ELF32LE>;
+template class elf::GnuHashTableSection<ELF32BE>;
+template class elf::GnuHashTableSection<ELF64LE>;
+template class elf::GnuHashTableSection<ELF64BE>;
+
+template class elf::HashTableSection<ELF32LE>;
+template class elf::HashTableSection<ELF32BE>;
+template class elf::HashTableSection<ELF64LE>;
+template class elf::HashTableSection<ELF64BE>;
+
+template class elf::PltSection<ELF32LE>;
+template class elf::PltSection<ELF32BE>;
+template class elf::PltSection<ELF64LE>;
+template class elf::PltSection<ELF64BE>;
+
+template class elf::IpltSection<ELF32LE>;
+template class elf::IpltSection<ELF32BE>;
+template class elf::IpltSection<ELF64LE>;
+template class elf::IpltSection<ELF64BE>;
+
+template class elf::GdbIndexSection<ELF32LE>;
+template class elf::GdbIndexSection<ELF32BE>;
+template class elf::GdbIndexSection<ELF64LE>;
+template class elf::GdbIndexSection<ELF64BE>;
+
+template class elf::EhFrameHeader<ELF32LE>;
+template class elf::EhFrameHeader<ELF32BE>;
+template class elf::EhFrameHeader<ELF64LE>;
+template class elf::EhFrameHeader<ELF64BE>;
+
+template class elf::VersionTableSection<ELF32LE>;
+template class elf::VersionTableSection<ELF32BE>;
+template class elf::VersionTableSection<ELF64LE>;
+template class elf::VersionTableSection<ELF64BE>;
+
+template class elf::VersionNeedSection<ELF32LE>;
+template class elf::VersionNeedSection<ELF32BE>;
+template class elf::VersionNeedSection<ELF64LE>;
+template class elf::VersionNeedSection<ELF64BE>;
+
+template class elf::VersionDefinitionSection<ELF32LE>;
+template class elf::VersionDefinitionSection<ELF32BE>;
+template class elf::VersionDefinitionSection<ELF64LE>;
+template class elf::VersionDefinitionSection<ELF64BE>;
+
+template class elf::MipsRldMapSection<ELF32LE>;
+template class elf::MipsRldMapSection<ELF32BE>;
+template class elf::MipsRldMapSection<ELF64LE>;
+template class elf::MipsRldMapSection<ELF64BE>;
+
+template class elf::ARMExidxSentinelSection<ELF32LE>;
+template class elf::ARMExidxSentinelSection<ELF32BE>;
+template class elf::ARMExidxSentinelSection<ELF64LE>;
+template class elf::ARMExidxSentinelSection<ELF64BE>;
diff --git a/ELF/SyntheticSections.h b/ELF/SyntheticSections.h
new file mode 100644
index 000000000000..dfefb3821e75
--- /dev/null
+++ b/ELF/SyntheticSections.h
@@ -0,0 +1,747 @@
+//===- SyntheticSection.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_SYNTHETIC_SECTION_H
+#define LLD_ELF_SYNTHETIC_SECTION_H
+
+#include "GdbIndex.h"
+#include "InputSection.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/MC/StringTableBuilder.h"
+
+namespace lld {
+namespace elf {
+
+template <class ELFT> class SyntheticSection : public InputSection<ELFT> {
+ typedef typename ELFT::uint uintX_t;
+
+public:
+ SyntheticSection(uintX_t Flags, uint32_t Type, uintX_t Addralign,
+ StringRef Name)
+ : InputSection<ELFT>(Flags, Type, Addralign, ArrayRef<uint8_t>(), Name,
+ InputSectionData::Synthetic) {
+ this->Live = true;
+ }
+
+ virtual ~SyntheticSection() = default;
+ virtual void writeTo(uint8_t *Buf) = 0;
+ virtual size_t getSize() const = 0;
+ virtual void finalize() {}
+ virtual bool empty() const { return false; }
+
+ uintX_t getVA() const {
+ return this->OutSec ? this->OutSec->Addr + this->OutSecOff : 0;
+ }
+
+ static bool classof(const InputSectionData *D) {
+ return D->kind() == InputSectionData::Synthetic;
+ }
+};
+
+template <class ELFT> class GotSection final : public SyntheticSection<ELFT> {
+ typedef typename ELFT::uint uintX_t;
+
+public:
+ GotSection();
+ void writeTo(uint8_t *Buf) override;
+ size_t getSize() const override { return Size; }
+ void finalize() override;
+ bool empty() const override;
+
+ void addEntry(SymbolBody &Sym);
+ bool addDynTlsEntry(SymbolBody &Sym);
+ bool addTlsIndex();
+ uintX_t getGlobalDynAddr(const SymbolBody &B) const;
+ uintX_t getGlobalDynOffset(const SymbolBody &B) const;
+
+ uintX_t getTlsIndexVA() { return this->getVA() + TlsIndexOff; }
+ uint32_t getTlsIndexOff() const { return TlsIndexOff; }
+
+ // Flag to force GOT to be in output if we have relocations
+ // that relies on its address.
+ bool HasGotOffRel = false;
+
+private:
+ size_t NumEntries = 0;
+ uint32_t TlsIndexOff = -1;
+ uintX_t Size = 0;
+};
+
+// .note.gnu.build-id section.
+template <class ELFT> class BuildIdSection : public SyntheticSection<ELFT> {
+ // First 16 bytes are a header.
+ static const unsigned HeaderSize = 16;
+
+public:
+ BuildIdSection();
+ void writeTo(uint8_t *Buf) override;
+ size_t getSize() const override { return HeaderSize + HashSize; }
+ void writeBuildId(llvm::ArrayRef<uint8_t> Buf);
+
+private:
+ void computeHash(llvm::ArrayRef<uint8_t> Buf,
+ std::function<void(uint8_t *, ArrayRef<uint8_t>)> Hash);
+
+ size_t HashSize;
+ uint8_t *HashBuf;
+};
+
+template <class ELFT>
+class MipsGotSection final : public SyntheticSection<ELFT> {
+ typedef typename ELFT::uint uintX_t;
+
+public:
+ MipsGotSection();
+ void writeTo(uint8_t *Buf) override;
+ size_t getSize() const override { return Size; }
+ void finalize() override;
+ bool empty() const override;
+ void addEntry(SymbolBody &Sym, uintX_t Addend, RelExpr Expr);
+ bool addDynTlsEntry(SymbolBody &Sym);
+ bool addTlsIndex();
+ uintX_t getPageEntryOffset(const SymbolBody &B, uintX_t Addend) const;
+ uintX_t getBodyEntryOffset(const SymbolBody &B, uintX_t Addend) const;
+ uintX_t getGlobalDynOffset(const SymbolBody &B) const;
+
+ // Returns the symbol which corresponds to the first entry of the global part
+ // of GOT on MIPS platform. It is required to fill up MIPS-specific dynamic
+ // table properties.
+ // Returns nullptr if the global part is empty.
+ const SymbolBody *getFirstGlobalEntry() const;
+
+ // Returns the number of entries in the local part of GOT including
+ // the number of reserved entries.
+ unsigned getLocalEntriesNum() const;
+
+ // Returns offset of TLS part of the MIPS GOT table. This part goes
+ // after 'local' and 'global' entries.
+ uintX_t getTlsOffset() const;
+
+ uint32_t getTlsIndexOff() const { return TlsIndexOff; }
+
+ uintX_t getGp() const;
+
+private:
+ // MIPS GOT consists of three parts: local, global and tls. Each part
+ // contains different types of entries. Here is a layout of GOT:
+ // - Header entries |
+ // - Page entries | Local part
+ // - Local entries (16-bit access) |
+ // - Local entries (32-bit access) |
+ // - Normal global entries || Global part
+ // - Reloc-only global entries ||
+ // - TLS entries ||| TLS part
+ //
+ // Header:
+ // Two entries hold predefined value 0x0 and 0x80000000.
+ // Page entries:
+ // These entries created by R_MIPS_GOT_PAGE relocation and R_MIPS_GOT16
+ // relocation against local symbols. They are initialized by higher 16-bit
+ // of the corresponding symbol's value. So each 64kb of address space
+ // requires a single GOT entry.
+ // Local entries (16-bit access):
+ // These entries created by GOT relocations against global non-preemptible
+ // symbols so dynamic linker is not necessary to resolve the symbol's
+ // values. "16-bit access" means that corresponding relocations address
+ // GOT using 16-bit index. Each unique Symbol-Addend pair has its own
+ // GOT entry.
+ // Local entries (32-bit access):
+ // These entries are the same as above but created by relocations which
+ // address GOT using 32-bit index (R_MIPS_GOT_HI16/LO16 etc).
+ // Normal global entries:
+ // These entries created by GOT relocations against preemptible global
+ // symbols. They need to be initialized by dynamic linker and they ordered
+ // exactly as the corresponding entries in the dynamic symbols table.
+ // Reloc-only global entries:
+ // These entries created for symbols that are referenced by dynamic
+ // relocations R_MIPS_REL32. These entries are not accessed with gp-relative
+ // addressing, but MIPS ABI requires that these entries be present in GOT.
+ // TLS entries:
+ // Entries created by TLS relocations.
+
+ // Number of "Header" entries.
+ static const unsigned HeaderEntriesNum = 2;
+ // Number of allocated "Page" entries.
+ uint32_t PageEntriesNum = 0;
+ // Map output sections referenced by MIPS GOT relocations
+ // to the first index of "Page" entries allocated for this section.
+ llvm::SmallMapVector<const OutputSectionBase *, size_t, 16> PageIndexMap;
+
+ typedef std::pair<const SymbolBody *, uintX_t> GotEntry;
+ typedef std::vector<GotEntry> GotEntries;
+ // Map from Symbol-Addend pair to the GOT index.
+ llvm::DenseMap<GotEntry, size_t> EntryIndexMap;
+ // Local entries (16-bit access).
+ GotEntries LocalEntries;
+ // Local entries (32-bit access).
+ GotEntries LocalEntries32;
+
+ // Normal and reloc-only global entries.
+ GotEntries GlobalEntries;
+
+ // TLS entries.
+ std::vector<const SymbolBody *> TlsEntries;
+
+ uint32_t TlsIndexOff = -1;
+ uintX_t Size = 0;
+};
+
+template <class ELFT>
+class GotPltSection final : public SyntheticSection<ELFT> {
+ typedef typename ELFT::uint uintX_t;
+
+public:
+ GotPltSection();
+ void addEntry(SymbolBody &Sym);
+ size_t getSize() const override;
+ void writeTo(uint8_t *Buf) override;
+ bool empty() const override { return Entries.empty(); }
+
+private:
+ std::vector<const SymbolBody *> Entries;
+};
+
+// The IgotPltSection is a Got associated with the IpltSection for GNU Ifunc
+// Symbols that will be relocated by Target->IRelativeRel.
+// On most Targets the IgotPltSection will immediately follow the GotPltSection
+// on ARM the IgotPltSection will immediately follow the GotSection.
+template <class ELFT>
+class IgotPltSection final : public SyntheticSection<ELFT> {
+ typedef typename ELFT::uint uintX_t;
+
+public:
+ IgotPltSection();
+ void addEntry(SymbolBody &Sym);
+ size_t getSize() const override;
+ void writeTo(uint8_t *Buf) override;
+ bool empty() const override { return Entries.empty(); }
+
+private:
+ std::vector<const SymbolBody *> Entries;
+};
+
+template <class ELFT>
+class StringTableSection final : public SyntheticSection<ELFT> {
+public:
+ typedef typename ELFT::uint uintX_t;
+ StringTableSection(StringRef Name, bool Dynamic);
+ unsigned addString(StringRef S, bool HashIt = true);
+ void writeTo(uint8_t *Buf) override;
+ size_t getSize() const override { return Size; }
+ bool isDynamic() const { return Dynamic; }
+
+private:
+ const bool Dynamic;
+
+ // ELF string tables start with a NUL byte, so 1.
+ uintX_t Size = 1;
+
+ llvm::DenseMap<StringRef, unsigned> StringMap;
+ std::vector<StringRef> Strings;
+};
+
+template <class ELFT> class DynamicReloc {
+ typedef typename ELFT::uint uintX_t;
+
+public:
+ DynamicReloc(uint32_t Type, const InputSectionBase<ELFT> *InputSec,
+ uintX_t OffsetInSec, bool UseSymVA, SymbolBody *Sym,
+ uintX_t Addend)
+ : Type(Type), Sym(Sym), InputSec(InputSec), OffsetInSec(OffsetInSec),
+ UseSymVA(UseSymVA), Addend(Addend) {}
+
+ DynamicReloc(uint32_t Type, const OutputSectionBase *OutputSec,
+ uintX_t OffsetInSec, bool UseSymVA, SymbolBody *Sym,
+ uintX_t Addend)
+ : Type(Type), Sym(Sym), OutputSec(OutputSec), OffsetInSec(OffsetInSec),
+ UseSymVA(UseSymVA), Addend(Addend) {}
+
+ uintX_t getOffset() const;
+ uintX_t getAddend() const;
+ uint32_t getSymIndex() const;
+ const OutputSectionBase *getOutputSec() const { return OutputSec; }
+ const InputSectionBase<ELFT> *getInputSec() const { return InputSec; }
+
+ uint32_t Type;
+
+private:
+ SymbolBody *Sym;
+ const InputSectionBase<ELFT> *InputSec = nullptr;
+ const OutputSectionBase *OutputSec = nullptr;
+ uintX_t OffsetInSec;
+ bool UseSymVA;
+ uintX_t Addend;
+};
+
+template <class ELFT>
+class DynamicSection final : public SyntheticSection<ELFT> {
+ typedef typename ELFT::Dyn Elf_Dyn;
+ typedef typename ELFT::Rel Elf_Rel;
+ typedef typename ELFT::Rela Elf_Rela;
+ typedef typename ELFT::Shdr Elf_Shdr;
+ typedef typename ELFT::Sym Elf_Sym;
+ typedef typename ELFT::uint uintX_t;
+
+ // The .dynamic section contains information for the dynamic linker.
+ // The section consists of fixed size entries, which consist of
+ // type and value fields. Value are one of plain integers, symbol
+ // addresses, or section addresses. This struct represents the entry.
+ struct Entry {
+ int32_t Tag;
+ union {
+ OutputSectionBase *OutSec;
+ InputSection<ELFT> *InSec;
+ uint64_t Val;
+ const SymbolBody *Sym;
+ };
+ enum KindT { SecAddr, SecSize, SymAddr, PlainInt, InSecAddr } Kind;
+ Entry(int32_t Tag, OutputSectionBase *OutSec, KindT Kind = SecAddr)
+ : Tag(Tag), OutSec(OutSec), Kind(Kind) {}
+ Entry(int32_t Tag, InputSection<ELFT> *Sec)
+ : Tag(Tag), InSec(Sec), Kind(InSecAddr) {}
+ Entry(int32_t Tag, uint64_t Val) : Tag(Tag), Val(Val), Kind(PlainInt) {}
+ Entry(int32_t Tag, const SymbolBody *Sym)
+ : Tag(Tag), Sym(Sym), Kind(SymAddr) {}
+ };
+
+ // finalize() fills this vector with the section contents. finalize()
+ // cannot directly create final section contents because when the
+ // function is called, symbol or section addresses are not fixed yet.
+ std::vector<Entry> Entries;
+
+public:
+ DynamicSection();
+ void finalize() override;
+ void writeTo(uint8_t *Buf) override;
+ size_t getSize() const override { return Size; }
+
+private:
+ void addEntries();
+ void add(Entry E) { Entries.push_back(E); }
+ uintX_t Size = 0;
+};
+
+template <class ELFT>
+class RelocationSection final : public SyntheticSection<ELFT> {
+ typedef typename ELFT::Rel Elf_Rel;
+ typedef typename ELFT::Rela Elf_Rela;
+ typedef typename ELFT::uint uintX_t;
+
+public:
+ RelocationSection(StringRef Name, bool Sort);
+ void addReloc(const DynamicReloc<ELFT> &Reloc);
+ unsigned getRelocOffset();
+ void finalize() override;
+ void writeTo(uint8_t *Buf) override;
+ bool empty() const override { return Relocs.empty(); }
+ size_t getSize() const override { return Relocs.size() * this->Entsize; }
+ size_t getRelativeRelocCount() const { return NumRelativeRelocs; }
+
+private:
+ bool Sort;
+ size_t NumRelativeRelocs = 0;
+ std::vector<DynamicReloc<ELFT>> Relocs;
+};
+
+struct SymbolTableEntry {
+ SymbolBody *Symbol;
+ size_t StrTabOffset;
+};
+
+template <class ELFT>
+class SymbolTableSection final : public SyntheticSection<ELFT> {
+public:
+ typedef typename ELFT::Shdr Elf_Shdr;
+ typedef typename ELFT::Sym Elf_Sym;
+ typedef typename ELFT::SymRange Elf_Sym_Range;
+ typedef typename ELFT::uint uintX_t;
+ SymbolTableSection(StringTableSection<ELFT> &StrTabSec);
+
+ void finalize() override;
+ void writeTo(uint8_t *Buf) override;
+ size_t getSize() const override { return getNumSymbols() * sizeof(Elf_Sym); }
+ void addSymbol(SymbolBody *Body);
+ StringTableSection<ELFT> &getStrTabSec() const { return StrTabSec; }
+ unsigned getNumSymbols() const { return NumLocals + Symbols.size() + 1; }
+
+ ArrayRef<SymbolTableEntry> getSymbols() const { return Symbols; }
+
+ unsigned NumLocals = 0;
+ StringTableSection<ELFT> &StrTabSec;
+
+private:
+ void writeLocalSymbols(uint8_t *&Buf);
+ void writeGlobalSymbols(uint8_t *Buf);
+
+ const OutputSectionBase *getOutputSection(SymbolBody *Sym);
+
+ // A vector of symbols and their string table offsets.
+ std::vector<SymbolTableEntry> Symbols;
+};
+
+// Outputs GNU Hash section. For detailed explanation see:
+// https://blogs.oracle.com/ali/entry/gnu_hash_elf_sections
+template <class ELFT>
+class GnuHashTableSection final : public SyntheticSection<ELFT> {
+ typedef typename ELFT::Off Elf_Off;
+ typedef typename ELFT::Word Elf_Word;
+ typedef typename ELFT::uint uintX_t;
+
+public:
+ GnuHashTableSection();
+ void finalize() override;
+ void writeTo(uint8_t *Buf) override;
+ size_t getSize() const override { return this->Size; }
+
+ // Adds symbols to the hash table.
+ // Sorts the input to satisfy GNU hash section requirements.
+ void addSymbols(std::vector<SymbolTableEntry> &Symbols);
+
+private:
+ static unsigned calcNBuckets(unsigned NumHashed);
+ static unsigned calcMaskWords(unsigned NumHashed);
+
+ void writeHeader(uint8_t *&Buf);
+ void writeBloomFilter(uint8_t *&Buf);
+ void writeHashTable(uint8_t *Buf);
+
+ struct SymbolData {
+ SymbolBody *Body;
+ size_t STName;
+ uint32_t Hash;
+ };
+
+ std::vector<SymbolData> Symbols;
+
+ unsigned MaskWords;
+ unsigned NBuckets;
+ unsigned Shift2;
+ uintX_t Size = 0;
+};
+
+template <class ELFT>
+class HashTableSection final : public SyntheticSection<ELFT> {
+ typedef typename ELFT::Word Elf_Word;
+
+public:
+ HashTableSection();
+ void finalize() override;
+ void writeTo(uint8_t *Buf) override;
+ size_t getSize() const override { return this->Size; }
+
+private:
+ size_t Size = 0;
+};
+
+template <class ELFT> class PltSection final : public SyntheticSection<ELFT> {
+public:
+ PltSection();
+ void writeTo(uint8_t *Buf) override;
+ size_t getSize() const override;
+ void addEntry(SymbolBody &Sym);
+ bool empty() const override { return Entries.empty(); }
+
+private:
+ std::vector<std::pair<const SymbolBody *, unsigned>> Entries;
+};
+
+// The IpltSection is a variant of Plt for recording entries for GNU Ifunc
+// symbols that will be subject to a Target->IRelativeRel
+// The IpltSection immediately follows the Plt section in the Output Section
+template <class ELFT> class IpltSection final : public SyntheticSection<ELFT> {
+public:
+ IpltSection();
+ void writeTo(uint8_t *Buf) override;
+ size_t getSize() const override;
+ void addEntry(SymbolBody &Sym);
+ bool empty() const override { return Entries.empty(); }
+
+private:
+ std::vector<std::pair<const SymbolBody *, unsigned>> Entries;
+};
+
+template <class ELFT>
+class GdbIndexSection final : public SyntheticSection<ELFT> {
+ typedef typename ELFT::uint uintX_t;
+
+ const unsigned OffsetTypeSize = 4;
+ const unsigned CuListOffset = 6 * OffsetTypeSize;
+ const unsigned CompilationUnitSize = 16;
+ const unsigned AddressEntrySize = 16 + OffsetTypeSize;
+ const unsigned SymTabEntrySize = 2 * OffsetTypeSize;
+
+public:
+ GdbIndexSection();
+ void finalize() override;
+ void writeTo(uint8_t *Buf) override;
+ size_t getSize() const override;
+ bool empty() const override;
+
+ // Pairs of [CU Offset, CU length].
+ std::vector<std::pair<uintX_t, uintX_t>> CompilationUnits;
+
+ llvm::StringTableBuilder StringPool;
+
+ GdbHashTab SymbolTable;
+
+ // The CU vector portion of the constant pool.
+ std::vector<std::vector<std::pair<uint32_t, uint8_t>>> CuVectors;
+
+ std::vector<AddressEntry<ELFT>> AddressArea;
+
+private:
+ void parseDebugSections();
+ void readDwarf(InputSection<ELFT> *I);
+
+ uint32_t CuTypesOffset;
+ uint32_t SymTabOffset;
+ uint32_t ConstantPoolOffset;
+ uint32_t StringPoolOffset;
+
+ size_t CuVectorsSize = 0;
+ std::vector<size_t> CuVectorsOffset;
+
+ bool Finalized = false;
+};
+
+// --eh-frame-hdr option tells linker to construct a header for all the
+// .eh_frame sections. This header is placed to a section named .eh_frame_hdr
+// and also to a PT_GNU_EH_FRAME segment.
+// At runtime the unwinder then can find all the PT_GNU_EH_FRAME segments by
+// calling dl_iterate_phdr.
+// This section contains a lookup table for quick binary search of FDEs.
+// Detailed info about internals can be found in Ian Lance Taylor's blog:
+// http://www.airs.com/blog/archives/460 (".eh_frame")
+// http://www.airs.com/blog/archives/462 (".eh_frame_hdr")
+template <class ELFT>
+class EhFrameHeader final : public SyntheticSection<ELFT> {
+ typedef typename ELFT::uint uintX_t;
+
+public:
+ EhFrameHeader();
+ void writeTo(uint8_t *Buf) override;
+ size_t getSize() const override;
+ void addFde(uint32_t Pc, uint32_t FdeVA);
+ bool empty() const override;
+
+private:
+ struct FdeData {
+ uint32_t Pc;
+ uint32_t FdeVA;
+ };
+
+ std::vector<FdeData> Fdes;
+};
+
+// For more information about .gnu.version and .gnu.version_r see:
+// https://www.akkadia.org/drepper/symbol-versioning
+
+// The .gnu.version_d section which has a section type of SHT_GNU_verdef shall
+// contain symbol version definitions. The number of entries in this section
+// shall be contained in the DT_VERDEFNUM entry of the .dynamic section.
+// The section shall contain an array of Elf_Verdef structures, optionally
+// followed by an array of Elf_Verdaux structures.
+template <class ELFT>
+class VersionDefinitionSection final : public SyntheticSection<ELFT> {
+ typedef typename ELFT::Verdef Elf_Verdef;
+ typedef typename ELFT::Verdaux Elf_Verdaux;
+
+public:
+ VersionDefinitionSection();
+ void finalize() override;
+ size_t getSize() const override;
+ void writeTo(uint8_t *Buf) override;
+
+private:
+ void writeOne(uint8_t *Buf, uint32_t Index, StringRef Name, size_t NameOff);
+
+ unsigned FileDefNameOff;
+};
+
+// The .gnu.version section specifies the required version of each symbol in the
+// dynamic symbol table. It contains one Elf_Versym for each dynamic symbol
+// table entry. An Elf_Versym is just a 16-bit integer that refers to a version
+// identifier defined in the either .gnu.version_r or .gnu.version_d section.
+// The values 0 and 1 are reserved. All other values are used for versions in
+// the own object or in any of the dependencies.
+template <class ELFT>
+class VersionTableSection final : public SyntheticSection<ELFT> {
+ typedef typename ELFT::Versym Elf_Versym;
+
+public:
+ VersionTableSection();
+ void finalize() override;
+ size_t getSize() const override;
+ void writeTo(uint8_t *Buf) override;
+ bool empty() const override;
+};
+
+// The .gnu.version_r section defines the version identifiers used by
+// .gnu.version. It contains a linked list of Elf_Verneed data structures. Each
+// Elf_Verneed specifies the version requirements for a single DSO, and contains
+// a reference to a linked list of Elf_Vernaux data structures which define the
+// mapping from version identifiers to version names.
+template <class ELFT>
+class VersionNeedSection final : public SyntheticSection<ELFT> {
+ typedef typename ELFT::Verneed Elf_Verneed;
+ typedef typename ELFT::Vernaux Elf_Vernaux;
+
+ // A vector of shared files that need Elf_Verneed data structures and the
+ // string table offsets of their sonames.
+ std::vector<std::pair<SharedFile<ELFT> *, size_t>> Needed;
+
+ // The next available version identifier.
+ unsigned NextIndex;
+
+public:
+ VersionNeedSection();
+ void addSymbol(SharedSymbol<ELFT> *SS);
+ void finalize() override;
+ void writeTo(uint8_t *Buf) override;
+ size_t getSize() const override;
+ size_t getNeedNum() const { return Needed.size(); }
+ bool empty() const override;
+};
+
+// .MIPS.abiflags section.
+template <class ELFT>
+class MipsAbiFlagsSection final : public SyntheticSection<ELFT> {
+ typedef llvm::object::Elf_Mips_ABIFlags<ELFT> Elf_Mips_ABIFlags;
+
+public:
+ static MipsAbiFlagsSection *create();
+
+ MipsAbiFlagsSection(Elf_Mips_ABIFlags Flags);
+ size_t getSize() const override { return sizeof(Elf_Mips_ABIFlags); }
+ void writeTo(uint8_t *Buf) override;
+
+private:
+ Elf_Mips_ABIFlags Flags;
+};
+
+// .MIPS.options section.
+template <class ELFT>
+class MipsOptionsSection final : public SyntheticSection<ELFT> {
+ typedef llvm::object::Elf_Mips_Options<ELFT> Elf_Mips_Options;
+ typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo;
+
+public:
+ static MipsOptionsSection *create();
+
+ MipsOptionsSection(Elf_Mips_RegInfo Reginfo);
+ void writeTo(uint8_t *Buf) override;
+
+ size_t getSize() const override {
+ return sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo);
+ }
+
+private:
+ Elf_Mips_RegInfo Reginfo;
+};
+
+// MIPS .reginfo section.
+template <class ELFT>
+class MipsReginfoSection final : public SyntheticSection<ELFT> {
+ typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo;
+
+public:
+ static MipsReginfoSection *create();
+
+ MipsReginfoSection(Elf_Mips_RegInfo Reginfo);
+ size_t getSize() const override { return sizeof(Elf_Mips_RegInfo); }
+ void writeTo(uint8_t *Buf) override;
+
+private:
+ Elf_Mips_RegInfo Reginfo;
+};
+
+// This is a MIPS specific section to hold a space within the data segment
+// of executable file which is pointed to by the DT_MIPS_RLD_MAP entry.
+// See "Dynamic section" in Chapter 5 in the following document:
+// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
+template <class ELFT> class MipsRldMapSection : public SyntheticSection<ELFT> {
+public:
+ MipsRldMapSection();
+ size_t getSize() const override { return sizeof(typename ELFT::uint); }
+ void writeTo(uint8_t *Buf) override;
+};
+
+template <class ELFT> class ARMExidxSentinelSection : public SyntheticSection<ELFT> {
+public:
+ ARMExidxSentinelSection();
+ size_t getSize() const override { return 8; }
+ void writeTo(uint8_t *Buf) override;
+};
+
+template <class ELFT> InputSection<ELFT> *createCommonSection();
+template <class ELFT> InputSection<ELFT> *createInterpSection();
+template <class ELFT> MergeInputSection<ELFT> *createCommentSection();
+
+// Linker generated sections which can be used as inputs.
+template <class ELFT> struct In {
+ static InputSection<ELFT> *ARMAttributes;
+ static BuildIdSection<ELFT> *BuildId;
+ static InputSection<ELFT> *Common;
+ static DynamicSection<ELFT> *Dynamic;
+ static StringTableSection<ELFT> *DynStrTab;
+ static SymbolTableSection<ELFT> *DynSymTab;
+ static EhFrameHeader<ELFT> *EhFrameHdr;
+ static GnuHashTableSection<ELFT> *GnuHashTab;
+ static GdbIndexSection<ELFT> *GdbIndex;
+ static GotSection<ELFT> *Got;
+ static MipsGotSection<ELFT> *MipsGot;
+ static GotPltSection<ELFT> *GotPlt;
+ static IgotPltSection<ELFT> *IgotPlt;
+ static HashTableSection<ELFT> *HashTab;
+ static InputSection<ELFT> *Interp;
+ static MipsRldMapSection<ELFT> *MipsRldMap;
+ static PltSection<ELFT> *Plt;
+ static IpltSection<ELFT> *Iplt;
+ static RelocationSection<ELFT> *RelaDyn;
+ static RelocationSection<ELFT> *RelaPlt;
+ static RelocationSection<ELFT> *RelaIplt;
+ static StringTableSection<ELFT> *ShStrTab;
+ static StringTableSection<ELFT> *StrTab;
+ static SymbolTableSection<ELFT> *SymTab;
+ static VersionDefinitionSection<ELFT> *VerDef;
+ static VersionTableSection<ELFT> *VerSym;
+ static VersionNeedSection<ELFT> *VerNeed;
+};
+
+template <class ELFT> InputSection<ELFT> *In<ELFT>::ARMAttributes;
+template <class ELFT> BuildIdSection<ELFT> *In<ELFT>::BuildId;
+template <class ELFT> InputSection<ELFT> *In<ELFT>::Common;
+template <class ELFT> DynamicSection<ELFT> *In<ELFT>::Dynamic;
+template <class ELFT> StringTableSection<ELFT> *In<ELFT>::DynStrTab;
+template <class ELFT> SymbolTableSection<ELFT> *In<ELFT>::DynSymTab;
+template <class ELFT> EhFrameHeader<ELFT> *In<ELFT>::EhFrameHdr;
+template <class ELFT> GdbIndexSection<ELFT> *In<ELFT>::GdbIndex;
+template <class ELFT> GnuHashTableSection<ELFT> *In<ELFT>::GnuHashTab;
+template <class ELFT> GotSection<ELFT> *In<ELFT>::Got;
+template <class ELFT> MipsGotSection<ELFT> *In<ELFT>::MipsGot;
+template <class ELFT> GotPltSection<ELFT> *In<ELFT>::GotPlt;
+template <class ELFT> IgotPltSection<ELFT> *In<ELFT>::IgotPlt;
+template <class ELFT> HashTableSection<ELFT> *In<ELFT>::HashTab;
+template <class ELFT> InputSection<ELFT> *In<ELFT>::Interp;
+template <class ELFT> MipsRldMapSection<ELFT> *In<ELFT>::MipsRldMap;
+template <class ELFT> PltSection<ELFT> *In<ELFT>::Plt;
+template <class ELFT> IpltSection<ELFT> *In<ELFT>::Iplt;
+template <class ELFT> RelocationSection<ELFT> *In<ELFT>::RelaDyn;
+template <class ELFT> RelocationSection<ELFT> *In<ELFT>::RelaPlt;
+template <class ELFT> RelocationSection<ELFT> *In<ELFT>::RelaIplt;
+template <class ELFT> StringTableSection<ELFT> *In<ELFT>::ShStrTab;
+template <class ELFT> StringTableSection<ELFT> *In<ELFT>::StrTab;
+template <class ELFT> SymbolTableSection<ELFT> *In<ELFT>::SymTab;
+template <class ELFT> VersionDefinitionSection<ELFT> *In<ELFT>::VerDef;
+template <class ELFT> VersionTableSection<ELFT> *In<ELFT>::VerSym;
+template <class ELFT> VersionNeedSection<ELFT> *In<ELFT>::VerNeed;
+} // namespace elf
+} // namespace lld
+
+#endif
diff --git a/ELF/Target.cpp b/ELF/Target.cpp
index 0375eb96dc3e..edae7c65c1b4 100644
--- a/ELF/Target.cpp
+++ b/ELF/Target.cpp
@@ -10,7 +10,7 @@
// Machine-specific things, such as applying relocations, creation of
// GOT or PLT entries, etc., are handled in this file.
//
-// Refer the ELF spec for the single letter varaibles, S, A or P, used
+// Refer the ELF spec for the single letter variables, S, A or P, used
// in this file.
//
// Some functions defined in this file has "relaxTls" as part of their names.
@@ -27,14 +27,17 @@
#include "Target.h"
#include "Error.h"
#include "InputFiles.h"
+#include "Memory.h"
#include "OutputSections.h"
+#include "SymbolTable.h"
#include "Symbols.h"
+#include "SyntheticSections.h"
#include "Thunks.h"
-
+#include "Writer.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Object/ELF.h"
-#include "llvm/Support/Endian.h"
#include "llvm/Support/ELF.h"
+#include "llvm/Support/Endian.h"
using namespace llvm;
using namespace llvm::object;
@@ -47,34 +50,66 @@ namespace elf {
TargetInfo *Target;
static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); }
+static void or32be(uint8_t *P, int32_t V) { write32be(P, read32be(P) | V); }
-StringRef getRelName(uint32_t Type) {
+std::string toString(uint32_t Type) {
return getELFRelocationTypeName(Config->EMachine, Type);
}
-template <unsigned N> static void checkInt(int64_t V, uint32_t Type) {
+template <class ELFT> static std::string getErrorLoc(uint8_t *Loc) {
+ for (InputSectionData *D : Symtab<ELFT>::X->Sections) {
+ auto *IS = dyn_cast_or_null<InputSection<ELFT>>(D);
+ if (!IS || !IS->OutSec)
+ continue;
+
+ uint8_t *ISLoc = cast<OutputSection<ELFT>>(IS->OutSec)->Loc + IS->OutSecOff;
+ if (ISLoc <= Loc && Loc < ISLoc + IS->getSize())
+ return IS->getLocation(Loc - ISLoc) + ": ";
+ }
+ return "";
+}
+
+static std::string getErrorLocation(uint8_t *Loc) {
+ switch (Config->EKind) {
+ case ELF32LEKind:
+ return getErrorLoc<ELF32LE>(Loc);
+ case ELF32BEKind:
+ return getErrorLoc<ELF32BE>(Loc);
+ case ELF64LEKind:
+ return getErrorLoc<ELF64LE>(Loc);
+ case ELF64BEKind:
+ return getErrorLoc<ELF64BE>(Loc);
+ default:
+ llvm_unreachable("unknown ELF type");
+ }
+}
+
+template <unsigned N>
+static void checkInt(uint8_t *Loc, int64_t V, uint32_t Type) {
if (!isInt<N>(V))
- error("relocation " + getRelName(Type) + " out of range");
+ error(getErrorLocation(Loc) + "relocation " + toString(Type) +
+ " out of range");
}
-template <unsigned N> static void checkUInt(uint64_t V, uint32_t Type) {
+template <unsigned N>
+static void checkUInt(uint8_t *Loc, uint64_t V, uint32_t Type) {
if (!isUInt<N>(V))
- error("relocation " + getRelName(Type) + " out of range");
+ error(getErrorLocation(Loc) + "relocation " + toString(Type) +
+ " out of range");
}
-template <unsigned N> static void checkIntUInt(uint64_t V, uint32_t Type) {
+template <unsigned N>
+static void checkIntUInt(uint8_t *Loc, uint64_t V, uint32_t Type) {
if (!isInt<N>(V) && !isUInt<N>(V))
- error("relocation " + getRelName(Type) + " out of range");
+ error(getErrorLocation(Loc) + "relocation " + toString(Type) +
+ " out of range");
}
-template <unsigned N> static void checkAlignment(uint64_t V, uint32_t Type) {
+template <unsigned N>
+static void checkAlignment(uint8_t *Loc, uint64_t V, uint32_t Type) {
if ((V & (N - 1)) != 0)
- error("improper alignment for relocation " + getRelName(Type));
-}
-
-static void errorDynRel(uint32_t Type) {
- error("relocation " + getRelName(Type) +
- " cannot be used against shared object; recompile with -fPIC.");
+ error(getErrorLocation(Loc) + "improper alignment for relocation " +
+ toString(Type));
}
namespace {
@@ -89,6 +124,7 @@ public:
bool isTlsGlobalDynamicRel(uint32_t Type) const override;
bool isTlsInitialExecRel(uint32_t Type) const override;
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
+ void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const override;
void writePltHeader(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
@@ -106,7 +142,7 @@ template <class ELFT> class X86_64TargetInfo final : public TargetInfo {
public:
X86_64TargetInfo();
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override;
- uint32_t getDynRel(uint32_t Type) const override;
+ bool isPicRel(uint32_t Type) const override;
bool isTlsLocalDynamicRel(uint32_t Type) const override;
bool isTlsGlobalDynamicRel(uint32_t Type) const override;
bool isTlsInitialExecRel(uint32_t Type) const override;
@@ -150,7 +186,7 @@ class AArch64TargetInfo final : public TargetInfo {
public:
AArch64TargetInfo();
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override;
- uint32_t getDynRel(uint32_t Type) const override;
+ bool isPicRel(uint32_t Type) const override;
bool isTlsInitialExecRel(uint32_t Type) const override;
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
void writePltHeader(uint8_t *Buf) const override;
@@ -176,14 +212,18 @@ class ARMTargetInfo final : public TargetInfo {
public:
ARMTargetInfo();
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override;
+ bool isPicRel(uint32_t Type) const override;
uint32_t getDynRel(uint32_t Type) const override;
uint64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
+ bool isTlsLocalDynamicRel(uint32_t Type) const override;
+ bool isTlsGlobalDynamicRel(uint32_t Type) const override;
+ bool isTlsInitialExecRel(uint32_t Type) const override;
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
+ void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const override;
void writePltHeader(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
- RelExpr getThunkExpr(RelExpr Expr, uint32_t RelocType,
- const InputFile &File,
+ RelExpr getThunkExpr(RelExpr Expr, uint32_t RelocType, const InputFile &File,
const SymbolBody &S) const override;
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
};
@@ -193,6 +233,7 @@ public:
MipsTargetInfo();
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override;
uint64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
+ bool isPicRel(uint32_t Type) const override;
uint32_t getDynRel(uint32_t Type) const override;
bool isTlsLocalDynamicRel(uint32_t Type) const override;
bool isTlsGlobalDynamicRel(uint32_t Type) const override;
@@ -200,8 +241,7 @@ public:
void writePltHeader(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
- RelExpr getThunkExpr(RelExpr Expr, uint32_t RelocType,
- const InputFile &File,
+ RelExpr getThunkExpr(RelExpr Expr, uint32_t RelocType, const InputFile &File,
const SymbolBody &S) const override;
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
bool usesOnlyLowPageBits(uint32_t Type) const override;
@@ -211,34 +251,35 @@ public:
TargetInfo *createTarget() {
switch (Config->EMachine) {
case EM_386:
- return new X86TargetInfo();
+ case EM_IAMCU:
+ return make<X86TargetInfo>();
case EM_AARCH64:
- return new AArch64TargetInfo();
+ return make<AArch64TargetInfo>();
case EM_AMDGPU:
- return new AMDGPUTargetInfo();
+ return make<AMDGPUTargetInfo>();
case EM_ARM:
- return new ARMTargetInfo();
+ return make<ARMTargetInfo>();
case EM_MIPS:
switch (Config->EKind) {
case ELF32LEKind:
- return new MipsTargetInfo<ELF32LE>();
+ return make<MipsTargetInfo<ELF32LE>>();
case ELF32BEKind:
- return new MipsTargetInfo<ELF32BE>();
+ return make<MipsTargetInfo<ELF32BE>>();
case ELF64LEKind:
- return new MipsTargetInfo<ELF64LE>();
+ return make<MipsTargetInfo<ELF64LE>>();
case ELF64BEKind:
- return new MipsTargetInfo<ELF64BE>();
+ return make<MipsTargetInfo<ELF64BE>>();
default:
fatal("unsupported MIPS target");
}
case EM_PPC:
- return new PPCTargetInfo();
+ return make<PPCTargetInfo>();
case EM_PPC64:
- return new PPC64TargetInfo();
+ return make<PPC64TargetInfo>();
case EM_X86_64:
if (Config->EKind == ELF32LEKind)
- return new X86_64TargetInfo<ELF32LE>();
- return new X86_64TargetInfo<ELF64LE>();
+ return make<X86_64TargetInfo<ELF32LE>>();
+ return make<X86_64TargetInfo<ELF64LE>>();
}
fatal("unknown target machine");
}
@@ -262,8 +303,10 @@ bool TargetInfo::isTlsInitialExecRel(uint32_t Type) const { return false; }
bool TargetInfo::isTlsLocalDynamicRel(uint32_t Type) const { return false; }
-bool TargetInfo::isTlsGlobalDynamicRel(uint32_t Type) const {
- return false;
+bool TargetInfo::isTlsGlobalDynamicRel(uint32_t Type) const { return false; }
+
+void TargetInfo::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
+ writeGotPlt(Buf, S);
}
RelExpr TargetInfo::adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
@@ -321,10 +364,11 @@ RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
return R_TLSLD;
case R_386_PLT32:
return R_PLT_PC;
+ case R_386_PC16:
case R_386_PC32:
return R_PC;
case R_386_GOTPC:
- return R_GOTONLY_PC;
+ return R_GOTONLY_PC_FROM_END;
case R_386_TLS_IE:
return R_GOT;
case R_386_GOT32:
@@ -332,7 +376,7 @@ RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
case R_386_TLS_GOTIE:
return R_GOT_FROM_END;
case R_386_GOTOFF:
- return R_GOTREL;
+ return R_GOTREL_FROM_END;
case R_386_TLS_LE:
return R_TLS;
case R_386_TLS_LE_32:
@@ -353,7 +397,7 @@ RelExpr X86TargetInfo::adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
}
void X86TargetInfo::writeGotPltHeader(uint8_t *Buf) const {
- write32le(Buf, Out<ELF32LE>::Dynamic->getVA());
+ write32le(Buf, In<ELF32LE>::Dynamic->getVA());
}
void X86TargetInfo::writeGotPlt(uint8_t *Buf, const SymbolBody &S) const {
@@ -362,6 +406,11 @@ void X86TargetInfo::writeGotPlt(uint8_t *Buf, const SymbolBody &S) const {
write32le(Buf, S.getPltVA<ELF32LE>() + 6);
}
+void X86TargetInfo::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
+ // An x86 entry is the address of the ifunc resolver function.
+ write32le(Buf, S.getVA<ELF32LE>());
+}
+
uint32_t X86TargetInfo::getDynRel(uint32_t Type) const {
if (Type == R_386_TLS_LE)
return R_386_TLS_TPOFF;
@@ -401,7 +450,7 @@ void X86TargetInfo::writePltHeader(uint8_t *Buf) const {
0x90, 0x90, 0x90, 0x90 // nop; nop; nop; nop
};
memcpy(Buf, PltData, sizeof(PltData));
- uint32_t Got = Out<ELF32LE>::GotPlt->getVA();
+ uint32_t Got = In<ELF32LE>::GotPlt->getVA();
write32le(Buf + 2, Got + 4);
write32le(Buf + 8, Got + 8);
}
@@ -418,7 +467,7 @@ void X86TargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
// jmp *foo@GOT(%ebx) or jmp *foo_in_GOT
Buf[1] = Config->Pic ? 0xa3 : 0x25;
- uint32_t Got = Out<ELF32LE>::GotPlt->getVA();
+ uint32_t Got = In<ELF32LE>::GotPlt->getVA();
write32le(Buf + 2, Config->Shared ? GotEntryAddr - Got : GotEntryAddr);
write32le(Buf + 7, RelOff);
write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16);
@@ -429,6 +478,9 @@ uint64_t X86TargetInfo::getImplicitAddend(const uint8_t *Buf,
switch (Type) {
default:
return 0;
+ case R_386_16:
+ case R_386_PC16:
+ return read16le(Buf);
case R_386_32:
case R_386_GOT32:
case R_386_GOT32X:
@@ -443,7 +495,14 @@ uint64_t X86TargetInfo::getImplicitAddend(const uint8_t *Buf,
void X86TargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
uint64_t Val) const {
- checkInt<32>(Val, Type);
+ checkInt<32>(Loc, Val, Type);
+
+ // R_386_PC16 and R_386_16 are not part of the current i386 psABI. They are
+ // used by 16-bit x86 objects, like boot loaders.
+ if (Type == R_386_16 || Type == R_386_PC16) {
+ write16le(Loc, Val);
+ return;
+ }
write32le(Loc, Val);
}
@@ -555,6 +614,9 @@ template <class ELFT> X86_64TargetInfo<ELFT>::X86_64TargetInfo() {
PltEntrySize = 16;
PltHeaderSize = 16;
TlsGdRelaxSkip = 2;
+ // Align to the large page size (known as a superpage or huge page).
+ // FreeBSD automatically promotes large, superpage-aligned allocations.
+ DefaultImageBase = 0x200000;
}
template <class ELFT>
@@ -578,12 +640,15 @@ RelExpr X86_64TargetInfo<ELFT>::getRelExpr(uint32_t Type,
case R_X86_64_PC64:
return R_PC;
case R_X86_64_GOT32:
+ case R_X86_64_GOT64:
return R_GOT_FROM_END;
case R_X86_64_GOTPCREL:
case R_X86_64_GOTPCRELX:
case R_X86_64_REX_GOTPCRELX:
case R_X86_64_GOTTPOFF:
return R_GOT_PC;
+ case R_X86_64_NONE:
+ return R_HINT;
}
}
@@ -593,7 +658,7 @@ void X86_64TargetInfo<ELFT>::writeGotPltHeader(uint8_t *Buf) const {
// required, but it is documented in the psabi and the glibc dynamic linker
// seems to use it (note that this is relevant for linking ld.so, not any
// other program).
- write64le(Buf, Out<ELFT>::Dynamic->getVA());
+ write64le(Buf, In<ELFT>::Dynamic->getVA());
}
template <class ELFT>
@@ -611,8 +676,8 @@ void X86_64TargetInfo<ELFT>::writePltHeader(uint8_t *Buf) const {
0x0f, 0x1f, 0x40, 0x00 // nopl 0x0(rax)
};
memcpy(Buf, PltData, sizeof(PltData));
- uint64_t Got = Out<ELFT>::GotPlt->getVA();
- uint64_t Plt = Out<ELFT>::Plt->getVA();
+ uint64_t Got = In<ELFT>::GotPlt->getVA();
+ uint64_t Plt = In<ELFT>::Plt->getVA();
write32le(Buf + 2, Got - Plt + 2); // GOT+8
write32le(Buf + 8, Got - Plt + 4); // GOT+16
}
@@ -634,10 +699,8 @@ void X86_64TargetInfo<ELFT>::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
}
template <class ELFT>
-uint32_t X86_64TargetInfo<ELFT>::getDynRel(uint32_t Type) const {
- if (Type == R_X86_64_PC32 || Type == R_X86_64_32)
- errorDynRel(Type);
- return Type;
+bool X86_64TargetInfo<ELFT>::isPicRel(uint32_t Type) const {
+ return Type != R_X86_64_PC32 && Type != R_X86_64_32;
}
template <class ELFT>
@@ -736,7 +799,8 @@ void X86_64TargetInfo<ELFT>::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type,
memcpy(Inst, "\x48\xc7", 2);
*RegSlot = 0xc0 | Reg;
} else {
- fatal("R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only");
+ error(getErrorLocation(Loc - 3) +
+ "R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only");
}
// The original code used a PC relative relocation.
@@ -778,7 +842,7 @@ void X86_64TargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
uint64_t Val) const {
switch (Type) {
case R_X86_64_32:
- checkUInt<32>(Val, Type);
+ checkUInt<32>(Loc, Val, Type);
write32le(Loc, Val);
break;
case R_X86_64_32S:
@@ -794,17 +858,19 @@ void X86_64TargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
case R_X86_64_TLSLD:
case R_X86_64_DTPOFF32:
case R_X86_64_SIZE32:
- checkInt<32>(Val, Type);
+ checkInt<32>(Loc, Val, Type);
write32le(Loc, Val);
break;
case R_X86_64_64:
case R_X86_64_DTPOFF64:
- case R_X86_64_SIZE64:
+ case R_X86_64_GLOB_DAT:
case R_X86_64_PC64:
+ case R_X86_64_SIZE64:
+ case R_X86_64_GOT64:
write64le(Loc, Val);
break;
default:
- fatal("unrecognized reloc " + Twine(Type));
+ error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
}
}
@@ -965,13 +1031,26 @@ void PPCTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
case R_PPC_ADDR16_LO:
write16be(Loc, applyPPCLo(Val));
break;
+ case R_PPC_ADDR32:
+ case R_PPC_REL32:
+ write32be(Loc, Val);
+ break;
+ case R_PPC_REL24:
+ or32be(Loc, Val & 0x3FFFFFC);
+ break;
default:
- fatal("unrecognized reloc " + Twine(Type));
+ error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
}
}
RelExpr PPCTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
- return R_ABS;
+ switch (Type) {
+ case R_PPC_REL24:
+ case R_PPC_REL32:
+ return R_PC;
+ default:
+ return R_ABS;
+ }
}
PPC64TargetInfo::PPC64TargetInfo() {
@@ -984,7 +1063,7 @@ PPC64TargetInfo::PPC64TargetInfo() {
// We need 64K pages (at least under glibc/Linux, the loader won't
// set different permissions on a finer granularity than that).
- PageSize = 65536;
+ DefaultMaxPageSize = 65536;
// The PPC64 ELF ABI v1 spec, says:
//
@@ -1004,7 +1083,7 @@ uint64_t getPPC64TocBase() {
// TOC starts where the first of these sections starts. We always create a
// .got when we see a relocation that uses it, so for us the start is always
// the .got.
- uint64_t TocVA = Out<ELF64BE>::Got->getVA();
+ uint64_t TocVA = In<ELF64BE>::Got->getVA();
// Per the ppc64-elf-linux ABI, The TOC base is TOC value plus 0x8000
// thus permitting a full 64 Kbytes segment. Note that the glibc startup
@@ -1042,26 +1121,33 @@ void PPC64TargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
// be a pointer to the function descriptor in the .opd section. Using
// this scheme is simpler, but requires an extra indirection per PLT dispatch.
- write32be(Buf, 0xf8410028); // std %r2, 40(%r1)
- write32be(Buf + 4, 0x3d620000 | applyPPCHa(Off)); // addis %r11, %r2, X@ha
- write32be(Buf + 8, 0xe98b0000 | applyPPCLo(Off)); // ld %r12, X@l(%r11)
- write32be(Buf + 12, 0xe96c0000); // ld %r11,0(%r12)
- write32be(Buf + 16, 0x7d6903a6); // mtctr %r11
- write32be(Buf + 20, 0xe84c0008); // ld %r2,8(%r12)
- write32be(Buf + 24, 0xe96c0010); // ld %r11,16(%r12)
- write32be(Buf + 28, 0x4e800420); // bctr
+ write32be(Buf, 0xf8410028); // std %r2, 40(%r1)
+ write32be(Buf + 4, 0x3d620000 | applyPPCHa(Off)); // addis %r11, %r2, X@ha
+ write32be(Buf + 8, 0xe98b0000 | applyPPCLo(Off)); // ld %r12, X@l(%r11)
+ write32be(Buf + 12, 0xe96c0000); // ld %r11,0(%r12)
+ write32be(Buf + 16, 0x7d6903a6); // mtctr %r11
+ write32be(Buf + 20, 0xe84c0008); // ld %r2,8(%r12)
+ write32be(Buf + 24, 0xe96c0010); // ld %r11,16(%r12)
+ write32be(Buf + 28, 0x4e800420); // bctr
}
static std::pair<uint32_t, uint64_t> toAddr16Rel(uint32_t Type, uint64_t Val) {
uint64_t V = Val - PPC64TocOffset;
switch (Type) {
- case R_PPC64_TOC16: return {R_PPC64_ADDR16, V};
- case R_PPC64_TOC16_DS: return {R_PPC64_ADDR16_DS, V};
- case R_PPC64_TOC16_HA: return {R_PPC64_ADDR16_HA, V};
- case R_PPC64_TOC16_HI: return {R_PPC64_ADDR16_HI, V};
- case R_PPC64_TOC16_LO: return {R_PPC64_ADDR16_LO, V};
- case R_PPC64_TOC16_LO_DS: return {R_PPC64_ADDR16_LO_DS, V};
- default: return {Type, Val};
+ case R_PPC64_TOC16:
+ return {R_PPC64_ADDR16, V};
+ case R_PPC64_TOC16_DS:
+ return {R_PPC64_ADDR16_DS, V};
+ case R_PPC64_TOC16_HA:
+ return {R_PPC64_ADDR16_HA, V};
+ case R_PPC64_TOC16_HI:
+ return {R_PPC64_ADDR16_HI, V};
+ case R_PPC64_TOC16_LO:
+ return {R_PPC64_ADDR16_LO, V};
+ case R_PPC64_TOC16_LO_DS:
+ return {R_PPC64_ADDR16_LO_DS, V};
+ default:
+ return {Type, Val};
}
}
@@ -1073,18 +1159,18 @@ void PPC64TargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
switch (Type) {
case R_PPC64_ADDR14: {
- checkAlignment<4>(Val, Type);
+ checkAlignment<4>(Loc, Val, Type);
// Preserve the AA/LK bits in the branch instruction
uint8_t AALK = Loc[3];
write16be(Loc + 2, (AALK & 3) | (Val & 0xfffc));
break;
}
case R_PPC64_ADDR16:
- checkInt<16>(Val, Type);
+ checkInt<16>(Loc, Val, Type);
write16be(Loc, Val);
break;
case R_PPC64_ADDR16_DS:
- checkInt<16>(Val, Type);
+ checkInt<16>(Loc, Val, Type);
write16be(Loc, (read16be(Loc) & 3) | (Val & ~3));
break;
case R_PPC64_ADDR16_HA:
@@ -1116,7 +1202,7 @@ void PPC64TargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
break;
case R_PPC64_ADDR32:
case R_PPC64_REL32:
- checkInt<32>(Val, Type);
+ checkInt<32>(Loc, Val, Type);
write32be(Loc, Val);
break;
case R_PPC64_ADDR64:
@@ -1126,12 +1212,12 @@ void PPC64TargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
break;
case R_PPC64_REL24: {
uint32_t Mask = 0x03FFFFFC;
- checkInt<24>(Val, Type);
+ checkInt<24>(Loc, Val, Type);
write32be(Loc, (read32be(Loc) & ~Mask) | (Val & Mask));
break;
}
default:
- fatal("unrecognized reloc " + Twine(Type));
+ error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
}
}
@@ -1147,6 +1233,7 @@ AArch64TargetInfo::AArch64TargetInfo() {
GotPltEntrySize = 8;
PltEntrySize = 16;
PltHeaderSize = 32;
+ DefaultMaxPageSize = 65536;
// It doesn't seem to be documented anywhere, but tls on aarch64 uses variant
// 1 of the tls structures and the tcb size is 16.
@@ -1164,7 +1251,7 @@ RelExpr AArch64TargetInfo::getRelExpr(uint32_t Type,
case R_AARCH64_TLSDESC_ADD_LO12_NC:
return R_TLSDESC;
case R_AARCH64_TLSDESC_CALL:
- return R_HINT;
+ return R_TLSDESC_CALL;
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
return R_TLS;
@@ -1222,19 +1309,18 @@ bool AArch64TargetInfo::isTlsInitialExecRel(uint32_t Type) const {
Type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC;
}
-uint32_t AArch64TargetInfo::getDynRel(uint32_t Type) const {
- if (Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64)
- return Type;
- // Keep it going with a dummy value so that we can find more reloc errors.
- errorDynRel(Type);
- return R_AARCH64_ABS32;
+bool AArch64TargetInfo::isPicRel(uint32_t Type) const {
+ return Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64;
}
void AArch64TargetInfo::writeGotPlt(uint8_t *Buf, const SymbolBody &) const {
- write64le(Buf, Out<ELF64LE>::Plt->getVA());
+ write64le(Buf, In<ELF64LE>::Plt->getVA());
}
-static uint64_t getAArch64Page(uint64_t Expr) {
+// Page(Expr) is the page address of the expression Expr, defined
+// as (Expr & ~0xFFF). (This applies even if the machine page size
+// supported by the platform has a different value.)
+uint64_t getAArch64Page(uint64_t Expr) {
return Expr & (~static_cast<uint64_t>(0xFFF));
}
@@ -1251,8 +1337,8 @@ void AArch64TargetInfo::writePltHeader(uint8_t *Buf) const {
};
memcpy(Buf, PltData, sizeof(PltData));
- uint64_t Got = Out<ELF64LE>::GotPlt->getVA();
- uint64_t Plt = Out<ELF64LE>::Plt->getVA();
+ uint64_t Got = In<ELF64LE>::GotPlt->getVA();
+ uint64_t Plt = In<ELF64LE>::Plt->getVA();
relocateOne(Buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
getAArch64Page(Got + 16) - getAArch64Page(Plt + 4));
relocateOne(Buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, Got + 16);
@@ -1276,14 +1362,22 @@ void AArch64TargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
relocateOne(Buf + 8, R_AARCH64_ADD_ABS_LO12_NC, GotEntryAddr);
}
-static void updateAArch64Addr(uint8_t *L, uint64_t Imm) {
+static void write32AArch64Addr(uint8_t *L, uint64_t Imm) {
uint32_t ImmLo = (Imm & 0x3) << 29;
uint32_t ImmHi = (Imm & 0x1FFFFC) << 3;
uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3);
write32le(L, (read32le(L) & ~Mask) | ImmLo | ImmHi);
}
-static inline void updateAArch64Add(uint8_t *L, uint64_t Imm) {
+// Return the bits [Start, End] from Val shifted Start bits.
+// For instance, getBits(0xF0, 4, 8) returns 0xF.
+static uint64_t getBits(uint64_t Val, int Start, int End) {
+ uint64_t Mask = ((uint64_t)1 << (End + 1 - Start)) - 1;
+ return (Val >> Start) & Mask;
+}
+
+// Update the immediate field in a AARCH64 ldr, str, and add instruction.
+static void or32AArch64Imm(uint8_t *L, uint64_t Imm) {
or32le(L, (Imm & 0xFFF) << 10);
}
@@ -1292,80 +1386,89 @@ void AArch64TargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
switch (Type) {
case R_AARCH64_ABS16:
case R_AARCH64_PREL16:
- checkIntUInt<16>(Val, Type);
+ checkIntUInt<16>(Loc, Val, Type);
write16le(Loc, Val);
break;
case R_AARCH64_ABS32:
case R_AARCH64_PREL32:
- checkIntUInt<32>(Val, Type);
+ checkIntUInt<32>(Loc, Val, Type);
write32le(Loc, Val);
break;
case R_AARCH64_ABS64:
+ case R_AARCH64_GLOB_DAT:
case R_AARCH64_PREL64:
write64le(Loc, Val);
break;
case R_AARCH64_ADD_ABS_LO12_NC:
- // This relocation stores 12 bits and there's no instruction
- // to do it. Instead, we do a 32 bits store of the value
- // of r_addend bitwise-or'ed Loc. This assumes that the addend
- // bits in Loc are zero.
- or32le(Loc, (Val & 0xFFF) << 10);
+ or32AArch64Imm(Loc, Val);
break;
case R_AARCH64_ADR_GOT_PAGE:
case R_AARCH64_ADR_PREL_PG_HI21:
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case R_AARCH64_TLSDESC_ADR_PAGE21:
- checkInt<33>(Val, Type);
- updateAArch64Addr(Loc, Val >> 12);
+ checkInt<33>(Loc, Val, Type);
+ write32AArch64Addr(Loc, Val >> 12);
break;
case R_AARCH64_ADR_PREL_LO21:
- checkInt<21>(Val, Type);
- updateAArch64Addr(Loc, Val);
+ checkInt<21>(Loc, Val, Type);
+ write32AArch64Addr(Loc, Val);
break;
case R_AARCH64_CALL26:
case R_AARCH64_JUMP26:
- checkInt<28>(Val, Type);
+ checkInt<28>(Loc, Val, Type);
or32le(Loc, (Val & 0x0FFFFFFC) >> 2);
break;
case R_AARCH64_CONDBR19:
- checkInt<21>(Val, Type);
+ checkInt<21>(Loc, Val, Type);
or32le(Loc, (Val & 0x1FFFFC) << 3);
break;
case R_AARCH64_LD64_GOT_LO12_NC:
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case R_AARCH64_TLSDESC_LD64_LO12_NC:
- checkAlignment<8>(Val, Type);
+ checkAlignment<8>(Loc, Val, Type);
or32le(Loc, (Val & 0xFF8) << 7);
break;
- case R_AARCH64_LDST128_ABS_LO12_NC:
- or32le(Loc, (Val & 0x0FF8) << 6);
+ case R_AARCH64_LDST8_ABS_LO12_NC:
+ or32AArch64Imm(Loc, getBits(Val, 0, 11));
break;
case R_AARCH64_LDST16_ABS_LO12_NC:
- or32le(Loc, (Val & 0x0FFC) << 9);
- break;
- case R_AARCH64_LDST8_ABS_LO12_NC:
- or32le(Loc, (Val & 0xFFF) << 10);
+ or32AArch64Imm(Loc, getBits(Val, 1, 11));
break;
case R_AARCH64_LDST32_ABS_LO12_NC:
- or32le(Loc, (Val & 0xFFC) << 8);
+ or32AArch64Imm(Loc, getBits(Val, 2, 11));
break;
case R_AARCH64_LDST64_ABS_LO12_NC:
- or32le(Loc, (Val & 0xFF8) << 7);
+ or32AArch64Imm(Loc, getBits(Val, 3, 11));
+ break;
+ case R_AARCH64_LDST128_ABS_LO12_NC:
+ or32AArch64Imm(Loc, getBits(Val, 4, 11));
+ break;
+ case R_AARCH64_MOVW_UABS_G0_NC:
+ or32le(Loc, (Val & 0xFFFF) << 5);
+ break;
+ case R_AARCH64_MOVW_UABS_G1_NC:
+ or32le(Loc, (Val & 0xFFFF0000) >> 11);
+ break;
+ case R_AARCH64_MOVW_UABS_G2_NC:
+ or32le(Loc, (Val & 0xFFFF00000000) >> 27);
+ break;
+ case R_AARCH64_MOVW_UABS_G3:
+ or32le(Loc, (Val & 0xFFFF000000000000) >> 43);
break;
case R_AARCH64_TSTBR14:
- checkInt<16>(Val, Type);
+ checkInt<16>(Loc, Val, Type);
or32le(Loc, (Val & 0xFFFC) << 3);
break;
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
- checkInt<24>(Val, Type);
- updateAArch64Add(Loc, Val >> 12);
+ checkInt<24>(Loc, Val, Type);
+ or32AArch64Imm(Loc, Val >> 12);
break;
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
case R_AARCH64_TLSDESC_ADD_LO12_NC:
- updateAArch64Add(Loc, Val);
+ or32AArch64Imm(Loc, Val);
break;
default:
- fatal("unrecognized reloc " + Twine(Type));
+ error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
}
}
@@ -1382,7 +1485,7 @@ void AArch64TargetInfo::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type,
// movk x0, #0x10
// nop
// nop
- checkUInt<32>(Val, Type);
+ checkUInt<32>(Loc, Val, Type);
switch (Type) {
case R_AARCH64_TLSDESC_ADD_LO12_NC:
@@ -1434,7 +1537,7 @@ void AArch64TargetInfo::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type,
void AArch64TargetInfo::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type,
uint64_t Val) const {
- checkUInt<32>(Val, Type);
+ checkUInt<32>(Loc, Val, Type);
if (Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
// Generate MOVZ.
@@ -1452,6 +1555,7 @@ void AArch64TargetInfo::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type,
}
AMDGPUTargetInfo::AMDGPUTargetInfo() {
+ RelativeRel = R_AMDGPU_REL64;
GotRel = R_AMDGPU_ABS64;
GotEntrySize = 8;
}
@@ -1459,20 +1563,37 @@ AMDGPUTargetInfo::AMDGPUTargetInfo() {
void AMDGPUTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
uint64_t Val) const {
switch (Type) {
+ case R_AMDGPU_ABS32:
case R_AMDGPU_GOTPCREL:
+ case R_AMDGPU_GOTPCREL32_LO:
case R_AMDGPU_REL32:
+ case R_AMDGPU_REL32_LO:
write32le(Loc, Val);
break;
+ case R_AMDGPU_ABS64:
+ write64le(Loc, Val);
+ break;
+ case R_AMDGPU_GOTPCREL32_HI:
+ case R_AMDGPU_REL32_HI:
+ write32le(Loc, Val >> 32);
+ break;
default:
- fatal("unrecognized reloc " + Twine(Type));
+ error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
}
}
RelExpr AMDGPUTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
switch (Type) {
+ case R_AMDGPU_ABS32:
+ case R_AMDGPU_ABS64:
+ return R_ABS;
case R_AMDGPU_REL32:
+ case R_AMDGPU_REL32_LO:
+ case R_AMDGPU_REL32_HI:
return R_PC;
case R_AMDGPU_GOTPCREL:
+ case R_AMDGPU_GOTPCREL32_LO:
+ case R_AMDGPU_GOTPCREL32_HI:
return R_GOT_PC;
default:
fatal("do not know how to handle relocation " + Twine(Type));
@@ -1492,6 +1613,9 @@ ARMTargetInfo::ARMTargetInfo() {
GotPltEntrySize = 4;
PltEntrySize = 16;
PltHeaderSize = 20;
+ // ARM uses Variant 1 TLS
+ TcbSize = 8;
+ NeedsThunks = true;
}
RelExpr ARMTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
@@ -1504,6 +1628,7 @@ RelExpr ARMTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
case R_ARM_JUMP24:
case R_ARM_PC24:
case R_ARM_PLT32:
+ case R_ARM_PREL31:
case R_ARM_THM_JUMP19:
case R_ARM_THM_JUMP24:
case R_ARM_THM_CALL:
@@ -1515,8 +1640,21 @@ RelExpr ARMTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
// GOT(S) + A - GOT_ORG
return R_GOT_OFF;
case R_ARM_GOT_PREL:
- // GOT(S) + - GOT_ORG
+ case R_ARM_TLS_IE32:
+ // GOT(S) + A - P
+ return R_GOT_PC;
+ case R_ARM_TARGET1:
+ return Config->Target1Rel ? R_PC : R_ABS;
+ case R_ARM_TARGET2:
+ if (Config->Target2 == Target2Policy::Rel)
+ return R_PC;
+ if (Config->Target2 == Target2Policy::Abs)
+ return R_ABS;
return R_GOT_PC;
+ case R_ARM_TLS_GD32:
+ return R_TLSGD_PC;
+ case R_ARM_TLS_LDM32:
+ return R_TLSLD_PC;
case R_ARM_BASE_PREL:
// B(S) + A - P
// FIXME: currently B(S) assumed to be .got, this may not hold for all
@@ -1524,24 +1662,38 @@ RelExpr ARMTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
return R_GOTONLY_PC;
case R_ARM_MOVW_PREL_NC:
case R_ARM_MOVT_PREL:
- case R_ARM_PREL31:
case R_ARM_REL32:
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVT_PREL:
return R_PC;
+ case R_ARM_NONE:
+ return R_HINT;
+ case R_ARM_TLS_LE32:
+ return R_TLS;
}
}
+bool ARMTargetInfo::isPicRel(uint32_t Type) const {
+ return (Type == R_ARM_TARGET1 && !Config->Target1Rel) ||
+ (Type == R_ARM_ABS32);
+}
+
uint32_t ARMTargetInfo::getDynRel(uint32_t Type) const {
+ if (Type == R_ARM_TARGET1 && !Config->Target1Rel)
+ return R_ARM_ABS32;
if (Type == R_ARM_ABS32)
return Type;
// Keep it going with a dummy value so that we can find more reloc errors.
- errorDynRel(Type);
return R_ARM_ABS32;
}
void ARMTargetInfo::writeGotPlt(uint8_t *Buf, const SymbolBody &) const {
- write32le(Buf, Out<ELF32LE>::Plt->getVA());
+ write32le(Buf, In<ELF32LE>::Plt->getVA());
+}
+
+void ARMTargetInfo::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
+ // An ARM entry is the address of the ifunc resolver function.
+ write32le(Buf, S.getVA<ELF32LE>());
}
void ARMTargetInfo::writePltHeader(uint8_t *Buf) const {
@@ -1553,8 +1705,8 @@ void ARMTargetInfo::writePltHeader(uint8_t *Buf) const {
0x00, 0x00, 0x00, 0x00, // L2: .word &(.got.plt) - L1 - 8
};
memcpy(Buf, PltData, sizeof(PltData));
- uint64_t GotPlt = Out<ELF32LE>::GotPlt->getVA();
- uint64_t L1 = Out<ELF32LE>::Plt->getVA() + 8;
+ uint64_t GotPlt = In<ELF32LE>::GotPlt->getVA();
+ uint64_t L1 = In<ELF32LE>::Plt->getVA() + 8;
write32le(Buf + 16, GotPlt - L1 - 8);
}
@@ -1578,6 +1730,9 @@ void ARMTargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
RelExpr ARMTargetInfo::getThunkExpr(RelExpr Expr, uint32_t RelocType,
const InputFile &File,
const SymbolBody &S) const {
+ // If S is an undefined weak symbol we don't need a Thunk
+ if (S.isUndefined())
+ return Expr;
// A state change from ARM to Thumb and vice versa must go through an
// interworking thunk if the relocation type is not R_ARM_CALL or
// R_ARM_THM_CALL.
@@ -1606,18 +1761,29 @@ RelExpr ARMTargetInfo::getThunkExpr(RelExpr Expr, uint32_t RelocType,
void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
uint64_t Val) const {
switch (Type) {
- case R_ARM_NONE:
- break;
case R_ARM_ABS32:
case R_ARM_BASE_PREL:
+ case R_ARM_GLOB_DAT:
case R_ARM_GOTOFF32:
case R_ARM_GOT_BREL:
case R_ARM_GOT_PREL:
case R_ARM_REL32:
+ case R_ARM_RELATIVE:
+ case R_ARM_TARGET1:
+ case R_ARM_TARGET2:
+ case R_ARM_TLS_GD32:
+ case R_ARM_TLS_IE32:
+ case R_ARM_TLS_LDM32:
+ case R_ARM_TLS_LDO32:
+ case R_ARM_TLS_LE32:
+ case R_ARM_TLS_TPOFF32:
write32le(Loc, Val);
break;
+ case R_ARM_TLS_DTPMOD32:
+ write32le(Loc, 1);
+ break;
case R_ARM_PREL31:
- checkInt<31>(Val, Type);
+ checkInt<31>(Loc, Val, Type);
write32le(Loc, (read32le(Loc) & 0x80000000) | (Val & ~0x80000000));
break;
case R_ARM_CALL:
@@ -1626,7 +1792,7 @@ void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
if (Val & 1) {
// If bit 0 of Val is 1 the target is Thumb, we must select a BLX.
// The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1'
- checkInt<26>(Val, Type);
+ checkInt<26>(Loc, Val, Type);
write32le(Loc, 0xfa000000 | // opcode
((Val & 2) << 23) | // H
((Val >> 2) & 0x00ffffff)); // imm24
@@ -1636,20 +1802,20 @@ void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
// BLX (always unconditional) instruction to an ARM Target, select an
// unconditional BL.
write32le(Loc, 0xeb000000 | (read32le(Loc) & 0x00ffffff));
- // fall through as BL encoding is shared with B
+ // fall through as BL encoding is shared with B
case R_ARM_JUMP24:
case R_ARM_PC24:
case R_ARM_PLT32:
- checkInt<26>(Val, Type);
+ checkInt<26>(Loc, Val, Type);
write32le(Loc, (read32le(Loc) & ~0x00ffffff) | ((Val >> 2) & 0x00ffffff));
break;
case R_ARM_THM_JUMP11:
- checkInt<12>(Val, Type);
+ checkInt<12>(Loc, Val, Type);
write16le(Loc, (read32le(Loc) & 0xf800) | ((Val >> 1) & 0x07ff));
break;
case R_ARM_THM_JUMP19:
// Encoding T3: Val = S:J2:J1:imm6:imm11:0
- checkInt<21>(Val, Type);
+ checkInt<21>(Loc, Val, Type);
write16le(Loc,
(read16le(Loc) & 0xfbc0) | // opcode cond
((Val >> 10) & 0x0400) | // S
@@ -1670,11 +1836,11 @@ void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
}
// Bit 12 is 0 for BLX, 1 for BL
write16le(Loc + 2, (read16le(Loc + 2) & ~0x1000) | (Val & 1) << 12);
- // Fall through as rest of encoding is the same as B.W
+ // Fall through as rest of encoding is the same as B.W
case R_ARM_THM_JUMP24:
// Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0
// FIXME: Use of I1 and I2 require v6T2ops
- checkInt<25>(Val, Type);
+ checkInt<25>(Loc, Val, Type);
write16le(Loc,
0xf000 | // opcode
((Val >> 14) & 0x0400) | // S
@@ -1692,14 +1858,14 @@ void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
break;
case R_ARM_MOVT_ABS:
case R_ARM_MOVT_PREL:
- checkInt<32>(Val, Type);
+ checkInt<32>(Loc, Val, Type);
write32le(Loc, (read32le(Loc) & ~0x000f0fff) |
(((Val >> 16) & 0xf000) << 4) | ((Val >> 16) & 0xfff));
break;
case R_ARM_THM_MOVT_ABS:
case R_ARM_THM_MOVT_PREL:
// Encoding T1: A = imm4:i:imm3:imm8
- checkInt<32>(Val, Type);
+ checkInt<32>(Loc, Val, Type);
write16le(Loc,
0xf2c0 | // opcode
((Val >> 17) & 0x0400) | // i
@@ -1722,7 +1888,7 @@ void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
(Val & 0x00ff)); // imm8
break;
default:
- fatal("unrecognized reloc " + Twine(Type));
+ error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
}
}
@@ -1737,6 +1903,13 @@ uint64_t ARMTargetInfo::getImplicitAddend(const uint8_t *Buf,
case R_ARM_GOT_BREL:
case R_ARM_GOT_PREL:
case R_ARM_REL32:
+ case R_ARM_TARGET1:
+ case R_ARM_TARGET2:
+ case R_ARM_TLS_GD32:
+ case R_ARM_TLS_LDM32:
+ case R_ARM_TLS_LDO32:
+ case R_ARM_TLS_IE32:
+ case R_ARM_TLS_LE32:
return SignExtend64<32>(read32le(Buf));
case R_ARM_PREL31:
return SignExtend64<31>(read32le(Buf));
@@ -1794,15 +1967,28 @@ uint64_t ARMTargetInfo::getImplicitAddend(const uint8_t *Buf,
}
}
+bool ARMTargetInfo::isTlsLocalDynamicRel(uint32_t Type) const {
+ return Type == R_ARM_TLS_LDO32 || Type == R_ARM_TLS_LDM32;
+}
+
+bool ARMTargetInfo::isTlsGlobalDynamicRel(uint32_t Type) const {
+ return Type == R_ARM_TLS_GD32;
+}
+
+bool ARMTargetInfo::isTlsInitialExecRel(uint32_t Type) const {
+ return Type == R_ARM_TLS_IE32;
+}
+
template <class ELFT> MipsTargetInfo<ELFT>::MipsTargetInfo() {
GotPltHeaderEntriesNum = 2;
- PageSize = 65536;
+ DefaultMaxPageSize = 65536;
GotEntrySize = sizeof(typename ELFT::uint);
GotPltEntrySize = sizeof(typename ELFT::uint);
PltEntrySize = 16;
PltHeaderSize = 32;
CopyRel = R_MIPS_COPY;
PltRel = R_MIPS_JUMP_SLOT;
+ NeedsThunks = true;
if (ELFT::Is64Bits) {
RelativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32;
TlsGotRel = R_MIPS_TLS_TPREL64;
@@ -1819,8 +2005,8 @@ template <class ELFT> MipsTargetInfo<ELFT>::MipsTargetInfo() {
template <class ELFT>
RelExpr MipsTargetInfo<ELFT>::getRelExpr(uint32_t Type,
const SymbolBody &S) const {
- if (ELFT::Is64Bits)
- // See comment in the calculateMips64RelChain.
+ // See comment in the calculateMipsRelChain.
+ if (ELFT::Is64Bits || Config->MipsN32Abi)
Type &= 0xff;
switch (Type) {
default:
@@ -1829,16 +2015,16 @@ RelExpr MipsTargetInfo<ELFT>::getRelExpr(uint32_t Type,
return R_HINT;
case R_MIPS_GPREL16:
case R_MIPS_GPREL32:
- return R_GOTREL;
+ return R_MIPS_GOTREL;
case R_MIPS_26:
return R_PLT;
case R_MIPS_HI16:
case R_MIPS_LO16:
case R_MIPS_GOT_OFST:
- // MIPS _gp_disp designates offset between start of function and 'gp'
- // pointer into GOT. __gnu_local_gp is equal to the current value of
- // the 'gp'. Therefore any relocations against them do not require
- // dynamic relocation.
+ // R_MIPS_HI16/R_MIPS_LO16 relocations against _gp_disp calculate
+ // offset between start of function and 'gp' value which by default
+ // equal to the start of .got section. In that case we consider these
+ // relocations as relative.
if (&S == ElfSym<ELFT>::MipsGpDisp)
return R_PC;
return R_ABS;
@@ -1858,6 +2044,11 @@ RelExpr MipsTargetInfo<ELFT>::getRelExpr(uint32_t Type,
case R_MIPS_GOT_DISP:
case R_MIPS_TLS_GOTTPREL:
return R_MIPS_GOT_OFF;
+ case R_MIPS_CALL_HI16:
+ case R_MIPS_CALL_LO16:
+ case R_MIPS_GOT_HI16:
+ case R_MIPS_GOT_LO16:
+ return R_MIPS_GOT_OFF32;
case R_MIPS_GOT_PAGE:
return R_MIPS_GOT_LOCAL_PAGE;
case R_MIPS_TLS_GD:
@@ -1867,13 +2058,13 @@ RelExpr MipsTargetInfo<ELFT>::getRelExpr(uint32_t Type,
}
}
+template <class ELFT> bool MipsTargetInfo<ELFT>::isPicRel(uint32_t Type) const {
+ return Type == R_MIPS_32 || Type == R_MIPS_64;
+}
+
template <class ELFT>
uint32_t MipsTargetInfo<ELFT>::getDynRel(uint32_t Type) const {
- if (Type == R_MIPS_32 || Type == R_MIPS_64)
- return RelativeRel;
- // Keep it going with a dummy value so that we can find more reloc errors.
- errorDynRel(Type);
- return R_MIPS_32;
+ return RelativeRel;
}
template <class ELFT>
@@ -1888,11 +2079,9 @@ bool MipsTargetInfo<ELFT>::isTlsGlobalDynamicRel(uint32_t Type) const {
template <class ELFT>
void MipsTargetInfo<ELFT>::writeGotPlt(uint8_t *Buf, const SymbolBody &) const {
- write32<ELFT::TargetEndianness>(Buf, Out<ELFT>::Plt->getVA());
+ write32<ELFT::TargetEndianness>(Buf, In<ELFT>::Plt->getVA());
}
-static uint16_t mipsHigh(uint64_t V) { return (V + 0x8000) >> 16; }
-
template <endianness E, uint8_t BSIZE, uint8_t SHIFT>
static int64_t getPcRelocAddend(const uint8_t *Loc) {
uint32_t Instr = read32<E>(Loc);
@@ -1905,35 +2094,59 @@ static void applyMipsPcReloc(uint8_t *Loc, uint32_t Type, uint64_t V) {
uint32_t Mask = 0xffffffff >> (32 - BSIZE);
uint32_t Instr = read32<E>(Loc);
if (SHIFT > 0)
- checkAlignment<(1 << SHIFT)>(V, Type);
- checkInt<BSIZE + SHIFT>(V, Type);
+ checkAlignment<(1 << SHIFT)>(Loc, V, Type);
+ checkInt<BSIZE + SHIFT>(Loc, V, Type);
write32<E>(Loc, (Instr & ~Mask) | ((V >> SHIFT) & Mask));
}
-template <endianness E>
-static void writeMipsHi16(uint8_t *Loc, uint64_t V) {
+template <endianness E> static void writeMipsHi16(uint8_t *Loc, uint64_t V) {
uint32_t Instr = read32<E>(Loc);
- write32<E>(Loc, (Instr & 0xffff0000) | mipsHigh(V));
+ uint16_t Res = ((V + 0x8000) >> 16) & 0xffff;
+ write32<E>(Loc, (Instr & 0xffff0000) | Res);
}
-template <endianness E>
-static void writeMipsLo16(uint8_t *Loc, uint64_t V) {
+template <endianness E> static void writeMipsHigher(uint8_t *Loc, uint64_t V) {
+ uint32_t Instr = read32<E>(Loc);
+ uint16_t Res = ((V + 0x80008000) >> 32) & 0xffff;
+ write32<E>(Loc, (Instr & 0xffff0000) | Res);
+}
+
+template <endianness E> static void writeMipsHighest(uint8_t *Loc, uint64_t V) {
+ uint32_t Instr = read32<E>(Loc);
+ uint16_t Res = ((V + 0x800080008000) >> 48) & 0xffff;
+ write32<E>(Loc, (Instr & 0xffff0000) | Res);
+}
+
+template <endianness E> static void writeMipsLo16(uint8_t *Loc, uint64_t V) {
uint32_t Instr = read32<E>(Loc);
write32<E>(Loc, (Instr & 0xffff0000) | (V & 0xffff));
}
+template <class ELFT> static bool isMipsR6() {
+ const auto &FirstObj = cast<ELFFileBase<ELFT>>(*Config->FirstElf);
+ uint32_t Arch = FirstObj.getObj().getHeader()->e_flags & EF_MIPS_ARCH;
+ return Arch == EF_MIPS_ARCH_32R6 || Arch == EF_MIPS_ARCH_64R6;
+}
+
template <class ELFT>
void MipsTargetInfo<ELFT>::writePltHeader(uint8_t *Buf) const {
const endianness E = ELFT::TargetEndianness;
- write32<E>(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0])
- write32<E>(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28)
- write32<E>(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
- write32<E>(Buf + 12, 0x031cc023); // subu $24, $24, $28
+ if (Config->MipsN32Abi) {
+ write32<E>(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
+ write32<E>(Buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14)
+ write32<E>(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
+ write32<E>(Buf + 12, 0x030ec023); // subu $24, $24, $14
+ } else {
+ write32<E>(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0])
+ write32<E>(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28)
+ write32<E>(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
+ write32<E>(Buf + 12, 0x031cc023); // subu $24, $24, $28
+ }
write32<E>(Buf + 16, 0x03e07825); // move $15, $31
write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2
write32<E>(Buf + 24, 0x0320f809); // jalr $25
write32<E>(Buf + 28, 0x2718fffe); // subu $24, $24, 2
- uint64_t Got = Out<ELFT>::GotPlt->getVA();
+ uint64_t Got = In<ELFT>::GotPlt->getVA();
writeMipsHi16<E>(Buf, Got);
writeMipsLo16<E>(Buf + 4, Got);
writeMipsLo16<E>(Buf + 8, Got);
@@ -1944,9 +2157,10 @@ void MipsTargetInfo<ELFT>::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
uint64_t PltEntryAddr, int32_t Index,
unsigned RelOff) const {
const endianness E = ELFT::TargetEndianness;
- write32<E>(Buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry)
- write32<E>(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15)
- write32<E>(Buf + 8, 0x03200008); // jr $25
+ write32<E>(Buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry)
+ write32<E>(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15)
+ // jr $25
+ write32<E>(Buf + 8, isMipsR6<ELFT>() ? 0x03200009 : 0x03200008);
write32<E>(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry)
writeMipsHi16<E>(Buf, GotEntryAddr);
writeMipsLo16<E>(Buf + 4, GotEntryAddr);
@@ -1971,14 +2185,9 @@ RelExpr MipsTargetInfo<ELFT>::getThunkExpr(RelExpr Expr, uint32_t Type,
if (F->getObj().getHeader()->e_flags & EF_MIPS_PIC)
return Expr;
auto *D = dyn_cast<DefinedRegular<ELFT>>(&S);
- if (!D || !D->Section)
- return Expr;
// LA25 is required if target file has PIC code
// or target symbol is a PIC symbol.
- const ELFFile<ELFT> &DefFile = D->Section->getFile()->getObj();
- bool PicFile = DefFile.getHeader()->e_flags & EF_MIPS_PIC;
- bool PicSym = (D->StOther & STO_MIPS_MIPS16) == STO_MIPS_PIC;
- return (PicFile || PicSym) ? R_THUNK_ABS : Expr;
+ return D && D->isMipsPIC() ? R_THUNK_ABS : Expr;
}
template <class ELFT>
@@ -1990,12 +2199,14 @@ uint64_t MipsTargetInfo<ELFT>::getImplicitAddend(const uint8_t *Buf,
return 0;
case R_MIPS_32:
case R_MIPS_GPREL32:
+ case R_MIPS_TLS_DTPREL32:
+ case R_MIPS_TLS_TPREL32:
return read32<E>(Buf);
case R_MIPS_26:
// FIXME (simon): If the relocation target symbol is not a PLT entry
// we should use another expression for calculation:
// ((A << 2) | (P & 0xf0000000)) >> 2
- return SignExtend64<28>(read32<E>(Buf) << 2);
+ return SignExtend64<28>((read32<E>(Buf) & 0x3ffffff) << 2);
case R_MIPS_GPREL16:
case R_MIPS_LO16:
case R_MIPS_PCLO16:
@@ -2017,8 +2228,8 @@ uint64_t MipsTargetInfo<ELFT>::getImplicitAddend(const uint8_t *Buf,
}
}
-static std::pair<uint32_t, uint64_t> calculateMips64RelChain(uint32_t Type,
- uint64_t Val) {
+static std::pair<uint32_t, uint64_t>
+calculateMipsRelChain(uint8_t *Loc, uint32_t Type, uint64_t Val) {
// MIPS N64 ABI packs multiple relocations into the single relocation
// record. In general, all up to three relocations can have arbitrary
// types. In fact, Clang and GCC uses only a few combinations. For now,
@@ -2039,7 +2250,8 @@ static std::pair<uint32_t, uint64_t> calculateMips64RelChain(uint32_t Type,
return std::make_pair(Type2, Val);
if (Type2 == R_MIPS_SUB && (Type3 == R_MIPS_HI16 || Type3 == R_MIPS_LO16))
return std::make_pair(Type3, -Val);
- error("unsupported relocations combination " + Twine(Type));
+ error(getErrorLocation(Loc) + "unsupported relocations combination " +
+ Twine(Type));
return std::make_pair(Type & 0xff, Val);
}
@@ -2049,22 +2261,28 @@ void MipsTargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
const endianness E = ELFT::TargetEndianness;
// Thread pointer and DRP offsets from the start of TLS data area.
// https://www.linux-mips.org/wiki/NPTL
- if (Type == R_MIPS_TLS_DTPREL_HI16 || Type == R_MIPS_TLS_DTPREL_LO16)
+ if (Type == R_MIPS_TLS_DTPREL_HI16 || Type == R_MIPS_TLS_DTPREL_LO16 ||
+ Type == R_MIPS_TLS_DTPREL32 || Type == R_MIPS_TLS_DTPREL64)
Val -= 0x8000;
- else if (Type == R_MIPS_TLS_TPREL_HI16 || Type == R_MIPS_TLS_TPREL_LO16)
+ else if (Type == R_MIPS_TLS_TPREL_HI16 || Type == R_MIPS_TLS_TPREL_LO16 ||
+ Type == R_MIPS_TLS_TPREL32 || Type == R_MIPS_TLS_TPREL64)
Val -= 0x7000;
- if (ELFT::Is64Bits)
- std::tie(Type, Val) = calculateMips64RelChain(Type, Val);
+ if (ELFT::Is64Bits || Config->MipsN32Abi)
+ std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val);
switch (Type) {
case R_MIPS_32:
case R_MIPS_GPREL32:
+ case R_MIPS_TLS_DTPREL32:
+ case R_MIPS_TLS_TPREL32:
write32<E>(Loc, Val);
break;
case R_MIPS_64:
+ case R_MIPS_TLS_DTPREL64:
+ case R_MIPS_TLS_TPREL64:
write64<E>(Loc, Val);
break;
case R_MIPS_26:
- write32<E>(Loc, (read32<E>(Loc) & ~0x3ffffff) | (Val >> 2));
+ write32<E>(Loc, (read32<E>(Loc) & ~0x3ffffff) | ((Val >> 2) & 0x3ffffff));
break;
case R_MIPS_GOT_DISP:
case R_MIPS_GOT_PAGE:
@@ -2072,9 +2290,11 @@ void MipsTargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
case R_MIPS_GPREL16:
case R_MIPS_TLS_GD:
case R_MIPS_TLS_LDM:
- checkInt<16>(Val, Type);
+ checkInt<16>(Loc, Val, Type);
// fallthrough
case R_MIPS_CALL16:
+ case R_MIPS_CALL_LO16:
+ case R_MIPS_GOT_LO16:
case R_MIPS_GOT_OFST:
case R_MIPS_LO16:
case R_MIPS_PCLO16:
@@ -2083,12 +2303,20 @@ void MipsTargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
case R_MIPS_TLS_TPREL_LO16:
writeMipsLo16<E>(Loc, Val);
break;
+ case R_MIPS_CALL_HI16:
+ case R_MIPS_GOT_HI16:
case R_MIPS_HI16:
case R_MIPS_PCHI16:
case R_MIPS_TLS_DTPREL_HI16:
case R_MIPS_TLS_TPREL_HI16:
writeMipsHi16<E>(Loc, Val);
break;
+ case R_MIPS_HIGHER:
+ writeMipsHigher<E>(Loc, Val);
+ break;
+ case R_MIPS_HIGHEST:
+ writeMipsHighest<E>(Loc, Val);
+ break;
case R_MIPS_JALR:
// Ignore this optimization relocation for now
break;
@@ -2108,7 +2336,7 @@ void MipsTargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
applyMipsPcReloc<E, 32, 0>(Loc, Type, Val);
break;
default:
- fatal("unrecognized reloc " + Twine(Type));
+ error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
}
}
diff --git a/ELF/Target.h b/ELF/Target.h
index d335c1e051b7..156a2c023230 100644
--- a/ELF/Target.h
+++ b/ELF/Target.h
@@ -26,9 +26,11 @@ public:
virtual bool isTlsInitialExecRel(uint32_t Type) const;
virtual bool isTlsLocalDynamicRel(uint32_t Type) const;
virtual bool isTlsGlobalDynamicRel(uint32_t Type) const;
+ virtual bool isPicRel(uint32_t Type) const { return true; }
virtual uint32_t getDynRel(uint32_t Type) const { return Type; }
virtual void writeGotPltHeader(uint8_t *Buf) const {}
virtual void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const {};
+ virtual void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const;
virtual uint64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const;
// If lazy binding is supported, the first entry of the PLT has code
@@ -62,13 +64,12 @@ public:
unsigned TlsGdRelaxSkip = 1;
unsigned PageSize = 4096;
+ unsigned DefaultMaxPageSize = 4096;
- // On freebsd x86_64 the first page cannot be mmaped.
- // On linux that is controled by vm.mmap_min_addr. At least on some x86_64
+ // On FreeBSD x86_64 the first page cannot be mmaped.
+ // On Linux that is controled by vm.mmap_min_addr. At least on some x86_64
// installs that is 65536, so the first 15 pages cannot be used.
// Given that, the smallest value that can be used in here is 0x10000.
- // If using 2MB pages, the smallest page aligned address that works is
- // 0x200000, but it looks like every OS uses 4k pages for executables.
uint64_t DefaultImageBase = 0x10000;
uint32_t CopyRel;
@@ -80,8 +81,8 @@ public:
uint32_t TlsGotRel;
uint32_t TlsModuleIndexRel;
uint32_t TlsOffsetRel;
- unsigned GotEntrySize;
- unsigned GotPltEntrySize;
+ unsigned GotEntrySize = 0;
+ unsigned GotPltEntrySize = 0;
unsigned PltEntrySize;
unsigned PltHeaderSize;
@@ -92,6 +93,8 @@ public:
// Set to 0 for variant 2
unsigned TcbSize = 0;
+ bool NeedsThunks = false;
+
virtual RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
RelExpr Expr) const;
virtual void relaxGot(uint8_t *Loc, uint64_t Val) const;
@@ -101,10 +104,9 @@ public:
virtual void relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
};
-StringRef getRelName(uint32_t Type);
+std::string toString(uint32_t RelType);
uint64_t getPPC64TocBase();
-
-const unsigned MipsGPOffset = 0x7ff0;
+uint64_t getAArch64Page(uint64_t Expr);
extern TargetInfo *Target;
TargetInfo *createTarget();
diff --git a/ELF/Threads.h b/ELF/Threads.h
new file mode 100644
index 000000000000..c03e15253e15
--- /dev/null
+++ b/ELF/Threads.h
@@ -0,0 +1,90 @@
+//===- Threads.h ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// LLD supports threads to distribute workloads to multiple cores. Using
+// multicore is most effective when more than one core are idle. At the
+// last step of a build, it is often the case that a linker is the only
+// active process on a computer. So, we are naturally interested in using
+// threads wisely to reduce latency to deliver results to users.
+//
+// That said, we don't want to do "too clever" things using threads.
+// Complex multi-threaded algorithms are sometimes extremely hard to
+// justify the correctness and can easily mess up the entire design.
+//
+// Fortunately, when a linker links large programs (when the link time is
+// most critical), it spends most of the time to work on massive number of
+// small pieces of data of the same kind, and there are opportunities for
+// large parallelism there. Here are examples:
+//
+// - We have hundreds of thousands of input sections that need to be
+// copied to a result file at the last step of link. Once we fix a file
+// layout, each section can be copied to its destination and its
+// relocations can be applied independently.
+//
+// - We have tens of millions of small strings when constructing a
+// mergeable string section.
+//
+// For the cases such as the former, we can just use parallel_for_each
+// instead of std::for_each (or a plain for loop). Because tasks are
+// completely independent from each other, we can run them in parallel
+// without any coordination between them. That's very easy to understand
+// and justify.
+//
+// For the cases such as the latter, we can use parallel algorithms to
+// deal with massive data. We have to write code for a tailored algorithm
+// for each problem, but the complexity of multi-threading is isolated in
+// a single pass and doesn't affect the linker's overall design.
+//
+// The above approach seems to be working fairly well. As an example, when
+// linking Chromium (output size 1.6 GB), using 4 cores reduces latency to
+// 75% compared to single core (from 12.66 seconds to 9.55 seconds) on my
+// Ivy Bridge Xeon 2.8 GHz machine. Using 40 cores reduces it to 63% (from
+// 12.66 seconds to 7.95 seconds). Because of the Amdahl's law, the
+// speedup is not linear, but as you add more cores, it gets faster.
+//
+// On a final note, if you are trying to optimize, keep the axiom "don't
+// guess, measure!" in mind. Some important passes of the linker are not
+// that slow. For example, resolving all symbols is not a very heavy pass,
+// although it would be very hard to parallelize it. You want to first
+// identify a slow pass and then optimize it.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_THREADS_H
+#define LLD_ELF_THREADS_H
+
+#include "Config.h"
+
+#include "lld/Core/Parallel.h"
+#include <algorithm>
+#include <functional>
+
+namespace lld {
+namespace elf {
+
+template <class IterTy, class FuncTy>
+void forEach(IterTy Begin, IterTy End, FuncTy Fn) {
+ if (Config->Threads)
+ parallel_for_each(Begin, End, Fn);
+ else
+ std::for_each(Begin, End, Fn);
+}
+
+inline void forLoop(size_t Begin, size_t End, std::function<void(size_t)> Fn) {
+ if (Config->Threads) {
+ parallel_for(Begin, End, Fn);
+ } else {
+ for (size_t I = Begin; I < End; ++I)
+ Fn(I);
+ }
+}
+}
+}
+
+#endif
diff --git a/ELF/Thunks.cpp b/ELF/Thunks.cpp
index 1ebbb17f3032..34b630ac2510 100644
--- a/ELF/Thunks.cpp
+++ b/ELF/Thunks.cpp
@@ -22,17 +22,20 @@
//===---------------------------------------------------------------------===//
#include "Thunks.h"
+#include "Config.h"
#include "Error.h"
-#include "InputFiles.h"
#include "InputSection.h"
+#include "Memory.h"
#include "OutputSections.h"
#include "Symbols.h"
#include "Target.h"
-#include "llvm/Support/Allocator.h"
-
-#include "llvm/Object/ELF.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/ELF.h"
#include "llvm/Support/Endian.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/MathExtras.h"
+#include <cstdint>
+#include <cstring>
using namespace llvm;
using namespace llvm::object;
@@ -43,6 +46,7 @@ namespace lld {
namespace elf {
namespace {
+
// Specific ARM Thunk implementations. The naming convention is:
// Source State, TargetState, Target Requirement, ABS or PI, Range
template <class ELFT>
@@ -96,11 +100,13 @@ public:
uint32_t size() const override { return 16; }
void writeTo(uint8_t *Buf) const override;
};
-} // anonymous namespace
+
+} // end anonymous namespace
// ARM Target Thunks
template <class ELFT> static uint64_t getARMThunkDestVA(const SymbolBody &S) {
- return S.isInPlt() ? S.getPltVA<ELFT>() : S.getVA<ELFT>();
+ uint64_t V = S.isInPlt() ? S.getPltVA<ELFT>() : S.getVA<ELFT>();
+ return SignExtend64<32>(V);
}
template <class ELFT>
@@ -177,10 +183,10 @@ Thunk<ELFT>::Thunk(const SymbolBody &D, const InputSection<ELFT> &O)
: Destination(D), Owner(O), Offset(O.getThunkOff() + O.getThunksSize()) {}
template <class ELFT> typename ELFT::uint Thunk<ELFT>::getVA() const {
- return Owner.OutSec->getVA() + Owner.OutSecOff + Offset;
+ return Owner.OutSec->Addr + Owner.OutSecOff + Offset;
}
-template <class ELFT> Thunk<ELFT>::~Thunk() {}
+template <class ELFT> Thunk<ELFT>::~Thunk() = default;
// Creates a thunk for Thumb-ARM interworking.
template <class ELFT>
@@ -189,19 +195,18 @@ static Thunk<ELFT> *createThunkArm(uint32_t Reloc, SymbolBody &S,
// ARM relocations need ARM to Thumb interworking Thunks.
// Thumb relocations need Thumb to ARM relocations.
// Use position independent Thunks if we require position independent code.
- BumpPtrAllocator &Alloc = IS.getFile()->Alloc;
switch (Reloc) {
case R_ARM_PC24:
case R_ARM_PLT32:
case R_ARM_JUMP24:
if (Config->Pic)
- return new (Alloc) ARMToThumbV7PILongThunk<ELFT>(S, IS);
- return new (Alloc) ARMToThumbV7ABSLongThunk<ELFT>(S, IS);
+ return new (BAlloc) ARMToThumbV7PILongThunk<ELFT>(S, IS);
+ return new (BAlloc) ARMToThumbV7ABSLongThunk<ELFT>(S, IS);
case R_ARM_THM_JUMP19:
case R_ARM_THM_JUMP24:
if (Config->Pic)
- return new (Alloc) ThumbToARMV7PILongThunk<ELFT>(S, IS);
- return new (Alloc) ThumbToARMV7ABSLongThunk<ELFT>(S, IS);
+ return new (BAlloc) ThumbToARMV7PILongThunk<ELFT>(S, IS);
+ return new (BAlloc) ThumbToARMV7ABSLongThunk<ELFT>(S, IS);
}
fatal("unrecognized relocation type");
}
@@ -235,7 +240,7 @@ static void addThunkMips(uint32_t RelocType, SymbolBody &S,
// Mips Thunks are added to the InputSection defining S.
auto *R = cast<DefinedRegular<ELFT>>(&S);
auto *Sec = cast<InputSection<ELFT>>(R->Section);
- auto *T = new (IS.getFile()->Alloc) MipsThunk<ELFT>(S, *Sec);
+ auto *T = new (BAlloc) MipsThunk<ELFT>(S, *Sec);
Sec->addThunk(T);
R->ThunkData = T;
}
@@ -264,5 +269,5 @@ template class Thunk<ELF32BE>;
template class Thunk<ELF64LE>;
template class Thunk<ELF64BE>;
-} // namespace elf
-} // namespace lld
+} // end namespace elf
+} // end namespace lld
diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp
index 387bec3d8fb2..e056384fbd44 100644
--- a/ELF/Writer.cpp
+++ b/ELF/Writer.cpp
@@ -10,21 +10,26 @@
#include "Writer.h"
#include "Config.h"
#include "LinkerScript.h"
+#include "Memory.h"
#include "OutputSections.h"
#include "Relocations.h"
#include "Strings.h"
#include "SymbolTable.h"
+#include "SyntheticSections.h"
#include "Target.h"
-
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/FileOutputBuffer.h"
-#include "llvm/Support/StringSaver.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/raw_ostream.h"
+#include <climits>
+#include <thread>
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
+using namespace llvm::support;
+using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
@@ -40,32 +45,25 @@ public:
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::SymRange Elf_Sym_Range;
typedef typename ELFT::Rela Elf_Rela;
- Writer(SymbolTable<ELFT> &S) : Symtab(S) {}
void run();
private:
- // This describes a program header entry.
- // Each contains type, access flags and range of output sections that will be
- // placed in it.
- struct Phdr {
- Phdr(unsigned Type, unsigned Flags) {
- H.p_type = Type;
- H.p_flags = Flags;
- }
- Elf_Phdr H = {};
- OutputSectionBase<ELFT> *First = nullptr;
- OutputSectionBase<ELFT> *Last = nullptr;
- };
-
+ void createSyntheticSections();
void copyLocalSymbols();
void addReservedSymbols();
+ void addInputSec(InputSectionBase<ELFT> *S);
void createSections();
+ void forEachRelSec(std::function<void(InputSectionBase<ELFT> &)> Fn);
+ void sortSections();
+ void finalizeSections();
void addPredefinedSections();
- bool needsGot();
- void createPhdrs();
+ std::vector<PhdrEntry> createPhdrs();
+ void removeEmptyPTLoad();
+ void addPtArmExid(std::vector<PhdrEntry> &Phdrs);
void assignAddresses();
void assignFileOffsets();
+ void assignFileOffsetsBinary();
void setPhdrs();
void fixHeaders();
void fixSectionAlignments();
@@ -73,214 +71,310 @@ private:
void openFile();
void writeHeader();
void writeSections();
+ void writeSectionsBinary();
void writeBuildId();
- bool needsInterpSection() const {
- return !Symtab.getSharedFiles().empty() && !Config->DynamicLinker.empty();
- }
- bool isOutputDynamic() const {
- return !Symtab.getSharedFiles().empty() || Config->Pic;
- }
-
- void addCommonSymbols(std::vector<DefinedCommon *> &Syms);
std::unique_ptr<FileOutputBuffer> Buffer;
- BumpPtrAllocator Alloc;
- std::vector<OutputSectionBase<ELFT> *> OutputSections;
- std::vector<std::unique_ptr<OutputSectionBase<ELFT>>> OwningSections;
+ std::vector<OutputSectionBase *> OutputSections;
+ OutputSectionFactory<ELFT> Factory;
void addRelIpltSymbols();
void addStartEndSymbols();
- void addStartStopSymbols(OutputSectionBase<ELFT> *Sec);
+ void addStartStopSymbols(OutputSectionBase *Sec);
+ uintX_t getEntryAddr();
+ OutputSectionBase *findSection(StringRef Name);
- SymbolTable<ELFT> &Symtab;
- std::vector<Phdr> Phdrs;
+ std::vector<PhdrEntry> Phdrs;
uintX_t FileSize;
uintX_t SectionHeaderOff;
+ bool AllocateHeader = true;
};
} // anonymous namespace
-template <class ELFT>
-StringRef elf::getOutputSectionName(InputSectionBase<ELFT> *S) {
- StringRef Dest = Script<ELFT>::X->getOutputSection(S);
- if (!Dest.empty())
- return Dest;
-
- StringRef Name = S->getSectionName();
- for (StringRef V : {".text.", ".rodata.", ".data.rel.ro.", ".data.", ".bss.",
- ".init_array.", ".fini_array.", ".ctors.", ".dtors.",
- ".tbss.", ".gcc_except_table.", ".tdata."})
- if (Name.startswith(V))
- return V.drop_back();
+StringRef elf::getOutputSectionName(StringRef Name) {
+ if (Config->Relocatable)
+ return Name;
+
+ for (StringRef V :
+ {".text.", ".rodata.", ".data.rel.ro.", ".data.", ".bss.",
+ ".init_array.", ".fini_array.", ".ctors.", ".dtors.", ".tbss.",
+ ".gcc_except_table.", ".tdata.", ".ARM.exidx."}) {
+ StringRef Prefix = V.drop_back();
+ if (Name.startswith(V) || Name == Prefix)
+ return Prefix;
+ }
+
+ // CommonSection is identified as "COMMON" in linker scripts.
+ // By default, it should go to .bss section.
+ if (Name == "COMMON")
+ return ".bss";
+
+ // ".zdebug_" is a prefix for ZLIB-compressed sections.
+ // Because we decompressed input sections, we want to remove 'z'.
+ if (Name.startswith(".zdebug_"))
+ return Saver.save(Twine(".") + Name.substr(2));
return Name;
}
-template <class ELFT>
-void elf::reportDiscarded(InputSectionBase<ELFT> *IS,
- const std::unique_ptr<elf::ObjectFile<ELFT>> &File) {
- if (!Config->PrintGcSections || !IS || IS->Live)
+template <class ELFT> void elf::reportDiscarded(InputSectionBase<ELFT> *IS) {
+ if (!Config->PrintGcSections)
return;
- errs() << "removing unused section from '" << IS->getSectionName()
- << "' in file '" << File->getName() << "'\n";
+ errs() << "removing unused section from '" << IS->Name << "' in file '"
+ << IS->getFile()->getName() << "'\n";
}
-template <class ELFT> void elf::writeResult(SymbolTable<ELFT> *Symtab) {
- typedef typename ELFT::uint uintX_t;
- typedef typename ELFT::Ehdr Elf_Ehdr;
+template <class ELFT> static bool needsInterpSection() {
+ return !Symtab<ELFT>::X->getSharedFiles().empty() &&
+ !Config->DynamicLinker.empty() &&
+ !Script<ELFT>::X->ignoreInterpSection();
+}
- // Create singleton output sections.
- OutputSection<ELFT> Bss(".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
- DynamicSection<ELFT> Dynamic;
- EhOutputSection<ELFT> EhFrame;
- GotSection<ELFT> Got;
- InterpSection<ELFT> Interp;
- PltSection<ELFT> Plt;
- RelocationSection<ELFT> RelaDyn(Config->Rela ? ".rela.dyn" : ".rel.dyn",
- Config->ZCombreloc);
- StringTableSection<ELFT> DynStrTab(".dynstr", true);
- StringTableSection<ELFT> ShStrTab(".shstrtab", false);
- SymbolTableSection<ELFT> DynSymTab(DynStrTab);
- VersionTableSection<ELFT> VerSym;
- VersionNeedSection<ELFT> VerNeed;
-
- OutputSectionBase<ELFT> ElfHeader("", 0, SHF_ALLOC);
- ElfHeader.setSize(sizeof(Elf_Ehdr));
- OutputSectionBase<ELFT> ProgramHeaders("", 0, SHF_ALLOC);
- ProgramHeaders.updateAlignment(sizeof(uintX_t));
-
- // Instantiate optional output sections if they are needed.
- std::unique_ptr<BuildIdSection<ELFT>> BuildId;
- std::unique_ptr<EhFrameHeader<ELFT>> EhFrameHdr;
- std::unique_ptr<GnuHashTableSection<ELFT>> GnuHashTab;
- std::unique_ptr<GotPltSection<ELFT>> GotPlt;
- std::unique_ptr<HashTableSection<ELFT>> HashTab;
- std::unique_ptr<RelocationSection<ELFT>> RelaPlt;
- std::unique_ptr<StringTableSection<ELFT>> StrTab;
- std::unique_ptr<SymbolTableSection<ELFT>> SymTabSec;
- std::unique_ptr<OutputSection<ELFT>> MipsRldMap;
- std::unique_ptr<VersionDefinitionSection<ELFT>> VerDef;
-
- if (Config->BuildId == BuildIdKind::Fnv1)
- BuildId.reset(new BuildIdFnv1<ELFT>);
- else if (Config->BuildId == BuildIdKind::Md5)
- BuildId.reset(new BuildIdMd5<ELFT>);
- else if (Config->BuildId == BuildIdKind::Sha1)
- BuildId.reset(new BuildIdSha1<ELFT>);
- else if (Config->BuildId == BuildIdKind::Hexstring)
- BuildId.reset(new BuildIdHexstring<ELFT>);
-
- if (Config->EhFrameHdr)
- EhFrameHdr.reset(new EhFrameHeader<ELFT>);
-
- if (Config->GnuHash)
- GnuHashTab.reset(new GnuHashTableSection<ELFT>);
- if (Config->SysvHash)
- HashTab.reset(new HashTableSection<ELFT>);
- StringRef S = Config->Rela ? ".rela.plt" : ".rel.plt";
- GotPlt.reset(new GotPltSection<ELFT>);
- RelaPlt.reset(new RelocationSection<ELFT>(S, false /*Sort*/));
- if (!Config->StripAll) {
- StrTab.reset(new StringTableSection<ELFT>(".strtab", false));
- SymTabSec.reset(new SymbolTableSection<ELFT>(*StrTab));
- }
- if (Config->EMachine == EM_MIPS && !Config->Shared) {
- // This is a MIPS specific section to hold a space within the data segment
- // of executable file which is pointed to by the DT_MIPS_RLD_MAP entry.
- // See "Dynamic section" in Chapter 5 in the following document:
- // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- MipsRldMap.reset(new OutputSection<ELFT>(".rld_map", SHT_PROGBITS,
- SHF_ALLOC | SHF_WRITE));
- MipsRldMap->setSize(sizeof(uintX_t));
- MipsRldMap->updateAlignment(sizeof(uintX_t));
- }
- if (!Config->VersionDefinitions.empty())
- VerDef.reset(new VersionDefinitionSection<ELFT>());
-
- Out<ELFT>::Bss = &Bss;
- Out<ELFT>::BuildId = BuildId.get();
- Out<ELFT>::DynStrTab = &DynStrTab;
- Out<ELFT>::DynSymTab = &DynSymTab;
- Out<ELFT>::Dynamic = &Dynamic;
- Out<ELFT>::EhFrame = &EhFrame;
- Out<ELFT>::EhFrameHdr = EhFrameHdr.get();
- Out<ELFT>::GnuHashTab = GnuHashTab.get();
- Out<ELFT>::Got = &Got;
- Out<ELFT>::GotPlt = GotPlt.get();
- Out<ELFT>::HashTab = HashTab.get();
- Out<ELFT>::Interp = &Interp;
- Out<ELFT>::Plt = &Plt;
- Out<ELFT>::RelaDyn = &RelaDyn;
- Out<ELFT>::RelaPlt = RelaPlt.get();
- Out<ELFT>::ShStrTab = &ShStrTab;
- Out<ELFT>::StrTab = StrTab.get();
- Out<ELFT>::SymTab = SymTabSec.get();
- Out<ELFT>::VerDef = VerDef.get();
- Out<ELFT>::VerSym = &VerSym;
- Out<ELFT>::VerNeed = &VerNeed;
- Out<ELFT>::MipsRldMap = MipsRldMap.get();
- Out<ELFT>::Opd = nullptr;
- Out<ELFT>::OpdBuf = nullptr;
- Out<ELFT>::TlsPhdr = nullptr;
- Out<ELFT>::ElfHeader = &ElfHeader;
- Out<ELFT>::ProgramHeaders = &ProgramHeaders;
-
- Writer<ELFT>(*Symtab).run();
+template <class ELFT> void elf::writeResult() { Writer<ELFT>().run(); }
+
+template <class ELFT> void Writer<ELFT>::removeEmptyPTLoad() {
+ auto I = std::remove_if(Phdrs.begin(), Phdrs.end(), [&](const PhdrEntry &P) {
+ if (P.p_type != PT_LOAD)
+ return false;
+ if (!P.First)
+ return true;
+ uintX_t Size = P.Last->Addr + P.Last->Size - P.First->Addr;
+ return Size == 0;
+ });
+ Phdrs.erase(I, Phdrs.end());
}
// The main function of the writer.
template <class ELFT> void Writer<ELFT>::run() {
- if (!Config->DiscardAll)
+ // Create linker-synthesized sections such as .got or .plt.
+ // Such sections are of type input section.
+ createSyntheticSections();
+
+ // We need to create some reserved symbols such as _end. Create them.
+ if (!Config->Relocatable)
+ addReservedSymbols();
+
+ // Some architectures use small displacements for jump instructions.
+ // It is linker's responsibility to create thunks containing long
+ // jump instructions if jump targets are too far. Create thunks.
+ if (Target->NeedsThunks)
+ forEachRelSec(createThunks<ELFT>);
+
+ // Create output sections.
+ Script<ELFT>::X->OutputSections = &OutputSections;
+ if (ScriptConfig->HasSections) {
+ // If linker script contains SECTIONS commands, let it create sections.
+ Script<ELFT>::X->processCommands(Factory);
+
+ // Linker scripts may have left some input sections unassigned.
+ // Assign such sections using the default rule.
+ Script<ELFT>::X->addOrphanSections(Factory);
+ } else {
+ // If linker script does not contain SECTIONS commands, create
+ // output sections by default rules. We still need to give the
+ // linker script a chance to run, because it might contain
+ // non-SECTIONS commands such as ASSERT.
+ createSections();
+ Script<ELFT>::X->processCommands(Factory);
+ }
+
+ if (Config->Discard != DiscardPolicy::All)
copyLocalSymbols();
- addReservedSymbols();
- createSections();
- if (HasError)
+
+ // Now that we have a complete set of output sections. This function
+ // completes section contents. For example, we need to add strings
+ // to the string table, and add entries to .got and .plt.
+ // finalizeSections does that.
+ finalizeSections();
+ if (ErrorCount)
return;
if (Config->Relocatable) {
assignFileOffsets();
} else {
- createPhdrs();
- fixHeaders();
- if (ScriptConfig->DoLayout) {
- Script<ELFT>::X->assignAddresses(OutputSections);
+ if (ScriptConfig->HasSections) {
+ Script<ELFT>::X->assignAddresses(Phdrs);
} else {
fixSectionAlignments();
assignAddresses();
}
- assignFileOffsets();
+
+ // Remove empty PT_LOAD to avoid causing the dynamic linker to try to mmap a
+ // 0 sized region. This has to be done late since only after assignAddresses
+ // we know the size of the sections.
+ removeEmptyPTLoad();
+
+ if (!Config->OFormatBinary)
+ assignFileOffsets();
+ else
+ assignFileOffsetsBinary();
+
setPhdrs();
fixAbsoluteSymbols();
}
+ // Write the result down to a file.
openFile();
- if (HasError)
+ if (ErrorCount)
return;
- writeHeader();
- writeSections();
+ if (!Config->OFormatBinary) {
+ writeHeader();
+ writeSections();
+ } else {
+ writeSectionsBinary();
+ }
+
+ // Backfill .note.gnu.build-id section content. This is done at last
+ // because the content is usually a hash value of the entire output file.
writeBuildId();
- if (HasError)
+ if (ErrorCount)
return;
+
if (auto EC = Buffer->commit())
error(EC, "failed to write to the output file");
+
+ // Flush the output streams and exit immediately. A full shutdown
+ // is a good test that we are keeping track of all allocated memory,
+ // but actually freeing it is a waste of time in a regular linker run.
+ if (Config->ExitEarly)
+ exitLld(0);
}
-template <class ELFT>
-static void reportUndefined(SymbolTable<ELFT> &Symtab, SymbolBody *Sym) {
- if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore)
- return;
+// Initialize Out<ELFT> members.
+template <class ELFT> void Writer<ELFT>::createSyntheticSections() {
+ // Initialize all pointers with NULL. This is needed because
+ // you can call lld::elf::main more than once as a library.
+ memset(&Out<ELFT>::First, 0, sizeof(Out<ELFT>));
- if (Config->Shared && Sym->symbol()->Visibility == STV_DEFAULT &&
- Config->UnresolvedSymbols != UnresolvedPolicy::NoUndef)
- return;
+ // Create singleton output sections.
+ Out<ELFT>::Bss =
+ make<OutputSection<ELFT>>(".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
+ In<ELFT>::DynStrTab = make<StringTableSection<ELFT>>(".dynstr", true);
+ In<ELFT>::Dynamic = make<DynamicSection<ELFT>>();
+ Out<ELFT>::EhFrame = make<EhOutputSection<ELFT>>();
+ In<ELFT>::RelaDyn = make<RelocationSection<ELFT>>(
+ Config->Rela ? ".rela.dyn" : ".rel.dyn", Config->ZCombreloc);
+ In<ELFT>::ShStrTab = make<StringTableSection<ELFT>>(".shstrtab", false);
+
+ Out<ELFT>::ElfHeader = make<OutputSectionBase>("", 0, SHF_ALLOC);
+ Out<ELFT>::ElfHeader->Size = sizeof(Elf_Ehdr);
+ Out<ELFT>::ProgramHeaders = make<OutputSectionBase>("", 0, SHF_ALLOC);
+ Out<ELFT>::ProgramHeaders->updateAlignment(sizeof(uintX_t));
+
+ if (needsInterpSection<ELFT>()) {
+ In<ELFT>::Interp = createInterpSection<ELFT>();
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::Interp);
+ } else {
+ In<ELFT>::Interp = nullptr;
+ }
- std::string Msg = "undefined symbol: " + Sym->getName().str();
- if (Sym->File)
- Msg += " in " + getFilename(Sym->File);
- if (Config->UnresolvedSymbols == UnresolvedPolicy::Warn)
- warning(Msg);
- else
- error(Msg);
+ if (!Config->Relocatable)
+ Symtab<ELFT>::X->Sections.push_back(createCommentSection<ELFT>());
+
+ if (Config->Strip != StripPolicy::All) {
+ In<ELFT>::StrTab = make<StringTableSection<ELFT>>(".strtab", false);
+ In<ELFT>::SymTab = make<SymbolTableSection<ELFT>>(*In<ELFT>::StrTab);
+ }
+
+ if (Config->BuildId != BuildIdKind::None) {
+ In<ELFT>::BuildId = make<BuildIdSection<ELFT>>();
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::BuildId);
+ }
+
+ InputSection<ELFT> *Common = createCommonSection<ELFT>();
+ if (!Common->Data.empty()) {
+ In<ELFT>::Common = Common;
+ Symtab<ELFT>::X->Sections.push_back(Common);
+ }
+
+ // Add MIPS-specific sections.
+ bool HasDynSymTab = !Symtab<ELFT>::X->getSharedFiles().empty() || Config->Pic;
+ if (Config->EMachine == EM_MIPS) {
+ if (!Config->Shared && HasDynSymTab) {
+ In<ELFT>::MipsRldMap = make<MipsRldMapSection<ELFT>>();
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::MipsRldMap);
+ }
+ if (auto *Sec = MipsAbiFlagsSection<ELFT>::create())
+ Symtab<ELFT>::X->Sections.push_back(Sec);
+ if (auto *Sec = MipsOptionsSection<ELFT>::create())
+ Symtab<ELFT>::X->Sections.push_back(Sec);
+ if (auto *Sec = MipsReginfoSection<ELFT>::create())
+ Symtab<ELFT>::X->Sections.push_back(Sec);
+ }
+
+ if (HasDynSymTab) {
+ In<ELFT>::DynSymTab = make<SymbolTableSection<ELFT>>(*In<ELFT>::DynStrTab);
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::DynSymTab);
+
+ In<ELFT>::VerSym = make<VersionTableSection<ELFT>>();
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::VerSym);
+
+ if (!Config->VersionDefinitions.empty()) {
+ In<ELFT>::VerDef = make<VersionDefinitionSection<ELFT>>();
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::VerDef);
+ }
+
+ In<ELFT>::VerNeed = make<VersionNeedSection<ELFT>>();
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::VerNeed);
+
+ if (Config->GnuHash) {
+ In<ELFT>::GnuHashTab = make<GnuHashTableSection<ELFT>>();
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::GnuHashTab);
+ }
+
+ if (Config->SysvHash) {
+ In<ELFT>::HashTab = make<HashTableSection<ELFT>>();
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::HashTab);
+ }
+
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::Dynamic);
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::DynStrTab);
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::RelaDyn);
+ }
+
+ // Add .got. MIPS' .got is so different from the other archs,
+ // it has its own class.
+ if (Config->EMachine == EM_MIPS) {
+ In<ELFT>::MipsGot = make<MipsGotSection<ELFT>>();
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::MipsGot);
+ } else {
+ In<ELFT>::Got = make<GotSection<ELFT>>();
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::Got);
+ }
+
+ In<ELFT>::GotPlt = make<GotPltSection<ELFT>>();
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::GotPlt);
+ In<ELFT>::IgotPlt = make<IgotPltSection<ELFT>>();
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::IgotPlt);
+
+ if (Config->GdbIndex) {
+ In<ELFT>::GdbIndex = make<GdbIndexSection<ELFT>>();
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::GdbIndex);
+ }
+
+ // We always need to add rel[a].plt to output if it has entries.
+ // Even for static linking it can contain R_[*]_IRELATIVE relocations.
+ In<ELFT>::RelaPlt = make<RelocationSection<ELFT>>(
+ Config->Rela ? ".rela.plt" : ".rel.plt", false /*Sort*/);
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::RelaPlt);
+
+ // The RelaIplt immediately follows .rel.plt (.rel.dyn for ARM) to ensure
+ // that the IRelative relocations are processed last by the dynamic loader
+ In<ELFT>::RelaIplt = make<RelocationSection<ELFT>>(
+ (Config->EMachine == EM_ARM) ? ".rel.dyn" : In<ELFT>::RelaPlt->Name,
+ false /*Sort*/);
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::RelaIplt);
+
+ In<ELFT>::Plt = make<PltSection<ELFT>>();
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::Plt);
+ In<ELFT>::Iplt = make<IpltSection<ELFT>>();
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::Iplt);
+
+ if (Config->EhFrameHdr) {
+ In<ELFT>::EhFrameHdr = make<EhFrameHeader<ELFT>>();
+ Symtab<ELFT>::X->Sections.push_back(In<ELFT>::EhFrameHdr);
+ }
}
template <class ELFT>
@@ -297,7 +391,7 @@ static bool shouldKeepInSymtab(InputSectionBase<ELFT> *Sec, StringRef SymName,
if (Sec == &InputSection<ELFT>::Discarded)
return false;
- if (Config->DiscardNone)
+ if (Config->Discard == DiscardPolicy::None)
return true;
// In ELF assembly .L symbols are normally discarded by the assembler.
@@ -308,16 +402,22 @@ static bool shouldKeepInSymtab(InputSectionBase<ELFT> *Sec, StringRef SymName,
if (!SymName.startswith(".L") && !SymName.empty())
return true;
- if (Config->DiscardLocals)
+ if (Config->Discard == DiscardPolicy::Locals)
return false;
- return !(Sec->getSectionHdr()->sh_flags & SHF_MERGE);
+ return !Sec || !(Sec->Flags & SHF_MERGE);
}
template <class ELFT> static bool includeInSymtab(const SymbolBody &B) {
if (!B.isLocal() && !B.symbol()->IsUsedInRegularObj)
return false;
+ // If --retain-symbols-file is given, we'll keep only symbols listed in that
+ // file.
+ if (Config->Discard == DiscardPolicy::RetainFile &&
+ !Config->RetainSymbolsFile.count(B.getName()))
+ return false;
+
if (auto *D = dyn_cast<DefinedRegular<ELFT>>(&B)) {
// Always include absolute symbols.
if (!D->Section)
@@ -335,27 +435,29 @@ template <class ELFT> static bool includeInSymtab(const SymbolBody &B) {
// Local symbols are not in the linker's symbol table. This function scans
// each object file's symbol table to copy local symbols to the output.
template <class ELFT> void Writer<ELFT>::copyLocalSymbols() {
- if (!Out<ELFT>::SymTab)
+ if (!In<ELFT>::SymTab)
return;
- for (const std::unique_ptr<elf::ObjectFile<ELFT>> &F :
- Symtab.getObjectFiles()) {
- const char *StrTab = F->getStringTable().data();
+ for (elf::ObjectFile<ELFT> *F : Symtab<ELFT>::X->getObjectFiles()) {
for (SymbolBody *B : F->getLocalSymbols()) {
+ if (!B->IsLocal)
+ fatal(toString(F) +
+ ": broken object: getLocalSymbols returns a non-local symbol");
auto *DR = dyn_cast<DefinedRegular<ELFT>>(B);
+
// No reason to keep local undefined symbol in symtab.
if (!DR)
continue;
if (!includeInSymtab<ELFT>(*B))
continue;
- StringRef SymName(StrTab + B->getNameOffset());
+
InputSectionBase<ELFT> *Sec = DR->Section;
- if (!shouldKeepInSymtab<ELFT>(Sec, SymName, *B))
+ if (!shouldKeepInSymtab<ELFT>(Sec, B->getName(), *B))
continue;
- ++Out<ELFT>::SymTab->NumLocals;
+ ++In<ELFT>::SymTab->NumLocals;
if (Config->Relocatable)
- B->DynsymIndex = Out<ELFT>::SymTab->NumLocals;
- F->KeptLocalSyms.push_back(
- std::make_pair(DR, Out<ELFT>::SymTab->StrTabSec.addString(SymName)));
+ B->DynsymIndex = In<ELFT>::SymTab->NumLocals;
+ F->KeptLocalSyms.push_back(std::make_pair(
+ DR, In<ELFT>::SymTab->StrTabSec.addString(B->getName())));
}
}
}
@@ -376,65 +478,82 @@ static int getPPC64SectionRank(StringRef SectionName) {
.Default(1);
}
-template <class ELFT> static bool isRelroSection(OutputSectionBase<ELFT> *Sec) {
+template <class ELFT> bool elf::isRelroSection(const OutputSectionBase *Sec) {
if (!Config->ZRelro)
return false;
- typename ELFT::uint Flags = Sec->getFlags();
+ uint64_t Flags = Sec->Flags;
if (!(Flags & SHF_ALLOC) || !(Flags & SHF_WRITE))
return false;
if (Flags & SHF_TLS)
return true;
- uint32_t Type = Sec->getType();
+ uint32_t Type = Sec->Type;
if (Type == SHT_INIT_ARRAY || Type == SHT_FINI_ARRAY ||
Type == SHT_PREINIT_ARRAY)
return true;
- if (Sec == Out<ELFT>::GotPlt)
+ if (Sec == In<ELFT>::GotPlt->OutSec)
return Config->ZNow;
- if (Sec == Out<ELFT>::Dynamic || Sec == Out<ELFT>::Got)
+ if (Sec == In<ELFT>::Dynamic->OutSec)
+ return true;
+ if (In<ELFT>::Got && Sec == In<ELFT>::Got->OutSec)
+ return true;
+ if (In<ELFT>::MipsGot && Sec == In<ELFT>::MipsGot->OutSec)
return true;
StringRef S = Sec->getName();
return S == ".data.rel.ro" || S == ".ctors" || S == ".dtors" || S == ".jcr" ||
- S == ".eh_frame";
+ S == ".eh_frame" || S == ".openbsd.randomdata";
}
-// Output section ordering is determined by this function.
template <class ELFT>
-static bool compareSections(OutputSectionBase<ELFT> *A,
- OutputSectionBase<ELFT> *B) {
- typedef typename ELFT::uint uintX_t;
-
- int Comp = Script<ELFT>::X->compareSections(A->getName(), B->getName());
- if (Comp != 0)
- return Comp < 0;
-
- uintX_t AFlags = A->getFlags();
- uintX_t BFlags = B->getFlags();
+static bool compareSectionsNonScript(const OutputSectionBase *A,
+ const OutputSectionBase *B) {
+ // Put .interp first because some loaders want to see that section
+ // on the first page of the executable file when loaded into memory.
+ bool AIsInterp = A->getName() == ".interp";
+ bool BIsInterp = B->getName() == ".interp";
+ if (AIsInterp != BIsInterp)
+ return AIsInterp;
// Allocatable sections go first to reduce the total PT_LOAD size and
// so debug info doesn't change addresses in actual code.
- bool AIsAlloc = AFlags & SHF_ALLOC;
- bool BIsAlloc = BFlags & SHF_ALLOC;
+ bool AIsAlloc = A->Flags & SHF_ALLOC;
+ bool BIsAlloc = B->Flags & SHF_ALLOC;
if (AIsAlloc != BIsAlloc)
return AIsAlloc;
- // We don't have any special requirements for the relative order of
- // two non allocatable sections.
+ // We don't have any special requirements for the relative order of two non
+ // allocatable sections.
if (!AIsAlloc)
return false;
+ // We want to put section specified by -T option first, so we
+ // can start assigning VA starting from them later.
+ auto AAddrSetI = Config->SectionStartMap.find(A->getName());
+ auto BAddrSetI = Config->SectionStartMap.find(B->getName());
+ bool AHasAddrSet = AAddrSetI != Config->SectionStartMap.end();
+ bool BHasAddrSet = BAddrSetI != Config->SectionStartMap.end();
+ if (AHasAddrSet != BHasAddrSet)
+ return AHasAddrSet;
+ if (AHasAddrSet)
+ return AAddrSetI->second < BAddrSetI->second;
+
// We want the read only sections first so that they go in the PT_LOAD
// covering the program headers at the start of the file.
- bool AIsWritable = AFlags & SHF_WRITE;
- bool BIsWritable = BFlags & SHF_WRITE;
+ bool AIsWritable = A->Flags & SHF_WRITE;
+ bool BIsWritable = B->Flags & SHF_WRITE;
if (AIsWritable != BIsWritable)
return BIsWritable;
- // For a corresponding reason, put non exec sections first (the program
- // header PT_LOAD is not executable).
- bool AIsExec = AFlags & SHF_EXECINSTR;
- bool BIsExec = BFlags & SHF_EXECINSTR;
- if (AIsExec != BIsExec)
- return BIsExec;
+ if (!Config->SingleRoRx) {
+ // For a corresponding reason, put non exec sections first (the program
+ // header PT_LOAD is not executable).
+ // We only do that if we are not using linker scripts, since with linker
+ // scripts ro and rx sections are in the same PT_LOAD, so their relative
+ // order is not important. The same applies for -no-rosegment.
+ bool AIsExec = A->Flags & SHF_EXECINSTR;
+ bool BIsExec = B->Flags & SHF_EXECINSTR;
+ if (AIsExec != BIsExec)
+ return BIsExec;
+ }
// If we got here we know that both A and B are in the same PT_LOAD.
@@ -442,8 +561,8 @@ static bool compareSections(OutputSectionBase<ELFT> *A,
// PT_LOAD, so stick TLS sections directly before R/W sections. The TLS NOBITS
// sections are placed here as they don't take up virtual address space in the
// PT_LOAD.
- bool AIsTls = AFlags & SHF_TLS;
- bool BIsTls = BFlags & SHF_TLS;
+ bool AIsTls = A->Flags & SHF_TLS;
+ bool BIsTls = B->Flags & SHF_TLS;
if (AIsTls != BIsTls)
return AIsTls;
@@ -452,14 +571,14 @@ static bool compareSections(OutputSectionBase<ELFT> *A,
// them is a p_memsz that is larger than p_filesz. Seeing that it
// zeros the end of the PT_LOAD, so that has to correspond to the
// nobits sections.
- bool AIsNoBits = A->getType() == SHT_NOBITS;
- bool BIsNoBits = B->getType() == SHT_NOBITS;
+ bool AIsNoBits = A->Type == SHT_NOBITS;
+ bool BIsNoBits = B->Type == SHT_NOBITS;
if (AIsNoBits != BIsNoBits)
return BIsNoBits;
// We place RelRo section before plain r/w ones.
- bool AIsRelRo = isRelroSection(A);
- bool BIsRelRo = isRelroSection(B);
+ bool AIsRelRo = isRelroSection<ELFT>(A);
+ bool BIsRelRo = isRelroSection<ELFT>(B);
if (AIsRelRo != BIsRelRo)
return AIsRelRo;
@@ -472,40 +591,70 @@ static bool compareSections(OutputSectionBase<ELFT> *A,
return false;
}
-// Until this function is called, common symbols do not belong to any section.
-// This function adds them to end of BSS section.
+// Output section ordering is determined by this function.
template <class ELFT>
-void Writer<ELFT>::addCommonSymbols(std::vector<DefinedCommon *> &Syms) {
- if (Syms.empty())
- return;
+static bool compareSections(const OutputSectionBase *A,
+ const OutputSectionBase *B) {
+ // For now, put sections mentioned in a linker script first.
+ int AIndex = Script<ELFT>::X->getSectionIndex(A->getName());
+ int BIndex = Script<ELFT>::X->getSectionIndex(B->getName());
+ bool AInScript = AIndex != INT_MAX;
+ bool BInScript = BIndex != INT_MAX;
+ if (AInScript != BInScript)
+ return AInScript;
+ // If both are in the script, use that order.
+ if (AInScript)
+ return AIndex < BIndex;
+
+ return compareSectionsNonScript<ELFT>(A, B);
+}
- // Sort the common symbols by alignment as an heuristic to pack them better.
- std::stable_sort(Syms.begin(), Syms.end(),
- [](const DefinedCommon *A, const DefinedCommon *B) {
- return A->Alignment > B->Alignment;
- });
-
- uintX_t Off = Out<ELFT>::Bss->getSize();
- for (DefinedCommon *C : Syms) {
- Off = alignTo(Off, C->Alignment);
- Out<ELFT>::Bss->updateAlignment(C->Alignment);
- C->OffsetInBss = Off;
- Off += C->Size;
- }
+// Program header entry
+PhdrEntry::PhdrEntry(unsigned Type, unsigned Flags) {
+ p_type = Type;
+ p_flags = Flags;
+}
+
+void PhdrEntry::add(OutputSectionBase *Sec) {
+ Last = Sec;
+ if (!First)
+ First = Sec;
+ p_align = std::max(p_align, Sec->Addralign);
+ if (p_type == PT_LOAD)
+ Sec->FirstInPtLoad = First;
+}
- Out<ELFT>::Bss->setSize(Off);
+template <class ELFT>
+static Symbol *addOptionalSynthetic(StringRef Name, OutputSectionBase *Sec,
+ typename ELFT::uint Val,
+ uint8_t StOther = STV_HIDDEN) {
+ SymbolBody *S = Symtab<ELFT>::X->find(Name);
+ if (!S)
+ return nullptr;
+ if (!S->isUndefined() && !S->isShared())
+ return S->symbol();
+ return Symtab<ELFT>::X->addSynthetic(Name, Sec, Val, StOther);
+}
+
+template <class ELFT>
+static Symbol *addRegular(StringRef Name, InputSectionBase<ELFT> *Sec,
+ typename ELFT::uint Value) {
+ // The linker generated symbols are added as STB_WEAK to allow user defined
+ // ones to override them.
+ return Symtab<ELFT>::X->addRegular(Name, STV_HIDDEN, STT_NOTYPE, Value,
+ /*Size=*/0, STB_WEAK, Sec,
+ /*File=*/nullptr);
}
template <class ELFT>
-static Symbol *addOptionalSynthetic(SymbolTable<ELFT> &Table, StringRef Name,
- OutputSectionBase<ELFT> *Sec,
- typename ELFT::uint Val) {
- SymbolBody *S = Table.find(Name);
+static Symbol *addOptionalRegular(StringRef Name, InputSectionBase<ELFT> *IS,
+ typename ELFT::uint Value) {
+ SymbolBody *S = Symtab<ELFT>::X->find(Name);
if (!S)
return nullptr;
if (!S->isUndefined() && !S->isShared())
return S->symbol();
- return Table.addSynthetic(Name, Sec, Val);
+ return addRegular(Name, IS, Value);
}
// The beginning and the ending of .rel[a].plt section are marked
@@ -515,14 +664,13 @@ static Symbol *addOptionalSynthetic(SymbolTable<ELFT> &Table, StringRef Name,
// need these symbols, since IRELATIVE relocs are resolved through GOT
// and PLT. For details, see http://www.airs.com/blog/archives/403.
template <class ELFT> void Writer<ELFT>::addRelIpltSymbols() {
- if (isOutputDynamic() || !Out<ELFT>::RelaPlt)
+ if (In<ELFT>::DynSymTab)
return;
StringRef S = Config->Rela ? "__rela_iplt_start" : "__rel_iplt_start";
- addOptionalSynthetic(Symtab, S, Out<ELFT>::RelaPlt, 0);
+ addOptionalRegular<ELFT>(S, In<ELFT>::RelaIplt, 0);
S = Config->Rela ? "__rela_iplt_end" : "__rel_iplt_end";
- addOptionalSynthetic(Symtab, S, Out<ELFT>::RelaPlt,
- DefinedSynthetic<ELFT>::SectionEnd);
+ addOptionalRegular<ELFT>(S, In<ELFT>::RelaIplt, -1);
}
// The linker is expected to define some symbols depending on
@@ -530,24 +678,28 @@ template <class ELFT> void Writer<ELFT>::addRelIpltSymbols() {
template <class ELFT> void Writer<ELFT>::addReservedSymbols() {
if (Config->EMachine == EM_MIPS) {
// Define _gp for MIPS. st_value of _gp symbol will be updated by Writer
- // so that it points to an absolute address which is relative to GOT.
+ // so that it points to an absolute address which by default is relative
+ // to GOT. Default offset is 0x7ff0.
// See "Global Data Symbols" in Chapter 6 in the following document:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- Symtab.addSynthetic("_gp", Out<ELFT>::Got, MipsGPOffset);
+ ElfSym<ELFT>::MipsGp =
+ Symtab<ELFT>::X->addAbsolute("_gp", STV_HIDDEN, STB_LOCAL);
// On MIPS O32 ABI, _gp_disp is a magic symbol designates offset between
- // start of function and 'gp' pointer into GOT.
- Symbol *Sym =
- addOptionalSynthetic(Symtab, "_gp_disp", Out<ELFT>::Got, MipsGPOffset);
- if (Sym)
- ElfSym<ELFT>::MipsGpDisp = Sym->body();
+ // start of function and 'gp' pointer into GOT. To simplify relocation
+ // calculation we assign _gp value to it and calculate corresponding
+ // relocations as relative to this value.
+ if (Symtab<ELFT>::X->find("_gp_disp"))
+ ElfSym<ELFT>::MipsGpDisp =
+ Symtab<ELFT>::X->addAbsolute("_gp_disp", STV_HIDDEN, STB_LOCAL);
// The __gnu_local_gp is a magic symbol equal to the current value of 'gp'
// pointer. This symbol is used in the code generated by .cpload pseudo-op
// in case of using -mno-shared option.
// https://sourceware.org/ml/binutils/2004-12/msg00094.html
- addOptionalSynthetic(Symtab, "__gnu_local_gp", Out<ELFT>::Got,
- MipsGPOffset);
+ if (Symtab<ELFT>::X->find("__gnu_local_gp"))
+ ElfSym<ELFT>::MipsLocalGp =
+ Symtab<ELFT>::X->addAbsolute("__gnu_local_gp", STV_HIDDEN, STB_LOCAL);
}
// In the assembly for 32 bit x86 the _GLOBAL_OFFSET_TABLE_ symbol
@@ -562,27 +714,35 @@ template <class ELFT> void Writer<ELFT>::addReservedSymbols() {
// an undefined symbol in the .o files.
// Given that the symbol is effectively unused, we just create a dummy
// hidden one to avoid the undefined symbol error.
- if (!Config->Relocatable)
- Symtab.addIgnored("_GLOBAL_OFFSET_TABLE_");
+ Symtab<ELFT>::X->addIgnored("_GLOBAL_OFFSET_TABLE_");
// __tls_get_addr is defined by the dynamic linker for dynamic ELFs. For
// static linking the linker is required to optimize away any references to
// __tls_get_addr, so it's not defined anywhere. Create a hidden definition
- // to avoid the undefined symbol error.
- if (!isOutputDynamic())
- Symtab.addIgnored("__tls_get_addr");
+ // to avoid the undefined symbol error. As usual special cases are ARM and
+ // MIPS - the libc for these targets defines __tls_get_addr itself because
+ // there are no TLS optimizations for these targets.
+ if (!In<ELFT>::DynSymTab &&
+ (Config->EMachine != EM_MIPS && Config->EMachine != EM_ARM))
+ Symtab<ELFT>::X->addIgnored("__tls_get_addr");
+
+ // If linker script do layout we do not need to create any standart symbols.
+ if (ScriptConfig->HasSections)
+ return;
+
+ ElfSym<ELFT>::EhdrStart = Symtab<ELFT>::X->addIgnored("__ehdr_start");
auto Define = [this](StringRef S, DefinedRegular<ELFT> *&Sym1,
DefinedRegular<ELFT> *&Sym2) {
- Sym1 = Symtab.addIgnored(S, STV_DEFAULT);
+ Sym1 = Symtab<ELFT>::X->addIgnored(S, STV_DEFAULT);
// The name without the underscore is not a reserved name,
// so it is defined only when there is a reference against it.
assert(S.startswith("_"));
S = S.substr(1);
- if (SymbolBody *B = Symtab.find(S))
+ if (SymbolBody *B = Symtab<ELFT>::X->find(S))
if (B->isUndefined())
- Sym2 = Symtab.addAbsolute(S, STV_DEFAULT);
+ Sym2 = Symtab<ELFT>::X->addAbsolute(S, STV_DEFAULT);
};
Define("_end", ElfSym<ELFT>::End, ElfSym<ELFT>::End2);
@@ -592,65 +752,239 @@ template <class ELFT> void Writer<ELFT>::addReservedSymbols() {
// Sort input sections by section name suffixes for
// __attribute__((init_priority(N))).
-template <class ELFT> static void sortInitFini(OutputSectionBase<ELFT> *S) {
+template <class ELFT> static void sortInitFini(OutputSectionBase *S) {
if (S)
reinterpret_cast<OutputSection<ELFT> *>(S)->sortInitFini();
}
// Sort input sections by the special rule for .ctors and .dtors.
-template <class ELFT> static void sortCtorsDtors(OutputSectionBase<ELFT> *S) {
+template <class ELFT> static void sortCtorsDtors(OutputSectionBase *S) {
if (S)
reinterpret_cast<OutputSection<ELFT> *>(S)->sortCtorsDtors();
}
-// Create output section objects and add them to OutputSections.
-template <class ELFT> void Writer<ELFT>::createSections() {
- // Create output sections for input object file sections.
- std::vector<OutputSectionBase<ELFT> *> RegularSections;
- OutputSectionFactory<ELFT> Factory;
- for (const std::unique_ptr<elf::ObjectFile<ELFT>> &F :
- Symtab.getObjectFiles()) {
- for (InputSectionBase<ELFT> *C : F->getSections()) {
- if (isDiscarded(C)) {
- reportDiscarded(C, F);
+// Sort input sections using the list provided by --symbol-ordering-file.
+template <class ELFT>
+static void sortBySymbolsOrder(ArrayRef<OutputSectionBase *> OutputSections) {
+ if (Config->SymbolOrderingFile.empty())
+ return;
+
+ // Build a map from symbols to their priorities. Symbols that didn't
+ // appear in the symbol ordering file have the lowest priority 0.
+ // All explicitly mentioned symbols have negative (higher) priorities.
+ DenseMap<StringRef, int> SymbolOrder;
+ int Priority = -Config->SymbolOrderingFile.size();
+ for (StringRef S : Config->SymbolOrderingFile)
+ SymbolOrder.insert({S, Priority++});
+
+ // Build a map from sections to their priorities.
+ DenseMap<InputSectionBase<ELFT> *, int> SectionOrder;
+ for (elf::ObjectFile<ELFT> *File : Symtab<ELFT>::X->getObjectFiles()) {
+ for (SymbolBody *Body : File->getSymbols()) {
+ auto *D = dyn_cast<DefinedRegular<ELFT>>(Body);
+ if (!D || !D->Section)
continue;
- }
- OutputSectionBase<ELFT> *Sec;
- bool IsNew;
- std::tie(Sec, IsNew) = Factory.create(C, getOutputSectionName(C));
- if (IsNew) {
- OwningSections.emplace_back(Sec);
- OutputSections.push_back(Sec);
- RegularSections.push_back(Sec);
- }
- Sec->addSection(C);
+ int &Priority = SectionOrder[D->Section];
+ Priority = std::min(Priority, SymbolOrder.lookup(D->getName()));
}
}
- // If we have a .opd section (used under PPC64 for function descriptors),
- // store a pointer to it here so that we can use it later when processing
- // relocations.
- Out<ELFT>::Opd = Factory.lookup(".opd", SHT_PROGBITS, SHF_WRITE | SHF_ALLOC);
+ // Sort sections by priority.
+ for (OutputSectionBase *Base : OutputSections)
+ if (auto *Sec = dyn_cast<OutputSection<ELFT>>(Base))
+ Sec->sort([&](InputSection<ELFT> *S) { return SectionOrder.lookup(S); });
+}
+
+template <class ELFT>
+void Writer<ELFT>::forEachRelSec(
+ std::function<void(InputSectionBase<ELFT> &)> Fn) {
+ for (InputSectionBase<ELFT> *IS : Symtab<ELFT>::X->Sections) {
+ if (!IS->Live)
+ continue;
+ // Scan all relocations. Each relocation goes through a series
+ // of tests to determine if it needs special treatment, such as
+ // creating GOT, PLT, copy relocations, etc.
+ // Note that relocations for non-alloc sections are directly
+ // processed by InputSection::relocateNonAlloc.
+ if (!(IS->Flags & SHF_ALLOC))
+ continue;
+ if (isa<InputSection<ELFT>>(IS) || isa<EhInputSection<ELFT>>(IS))
+ Fn(*IS);
+ }
+}
+
+template <class ELFT>
+void Writer<ELFT>::addInputSec(InputSectionBase<ELFT> *IS) {
+ if (!IS)
+ return;
+
+ if (!IS->Live) {
+ reportDiscarded(IS);
+ return;
+ }
+ OutputSectionBase *Sec;
+ bool IsNew;
+ StringRef OutsecName = getOutputSectionName(IS->Name);
+ std::tie(Sec, IsNew) = Factory.create(IS, OutsecName);
+ if (IsNew)
+ OutputSections.push_back(Sec);
+ Sec->addSection(IS);
+}
+
+template <class ELFT> void Writer<ELFT>::createSections() {
+ for (InputSectionBase<ELFT> *IS : Symtab<ELFT>::X->Sections)
+ addInputSec(IS);
+
+ sortBySymbolsOrder<ELFT>(OutputSections);
+ sortInitFini<ELFT>(findSection(".init_array"));
+ sortInitFini<ELFT>(findSection(".fini_array"));
+ sortCtorsDtors<ELFT>(findSection(".ctors"));
+ sortCtorsDtors<ELFT>(findSection(".dtors"));
+
+ for (OutputSectionBase *Sec : OutputSections)
+ Sec->assignOffsets();
+}
+
+template <class ELFT>
+static bool canSharePtLoad(const OutputSectionBase &S1,
+ const OutputSectionBase &S2) {
+ if (!(S1.Flags & SHF_ALLOC) || !(S2.Flags & SHF_ALLOC))
+ return false;
+
+ bool S1IsWrite = S1.Flags & SHF_WRITE;
+ bool S2IsWrite = S2.Flags & SHF_WRITE;
+ if (S1IsWrite != S2IsWrite)
+ return false;
+
+ if (!S1IsWrite)
+ return true; // RO and RX share a PT_LOAD with linker scripts.
+ return (S1.Flags & SHF_EXECINSTR) == (S2.Flags & SHF_EXECINSTR);
+}
+
+template <class ELFT> void Writer<ELFT>::sortSections() {
+ // Don't sort if using -r. It is not necessary and we want to preserve the
+ // relative order for SHF_LINK_ORDER sections.
+ if (Config->Relocatable)
+ return;
+ if (!ScriptConfig->HasSections) {
+ std::stable_sort(OutputSections.begin(), OutputSections.end(),
+ compareSectionsNonScript<ELFT>);
+ return;
+ }
+ Script<ELFT>::X->adjustSectionsBeforeSorting();
+
+ // The order of the sections in the script is arbitrary and may not agree with
+ // compareSectionsNonScript. This means that we cannot easily define a
+ // strict weak ordering. To see why, consider a comparison of a section in the
+ // script and one not in the script. We have a two simple options:
+ // * Make them equivalent (a is not less than b, and b is not less than a).
+ // The problem is then that equivalence has to be transitive and we can
+ // have sections a, b and c with only b in a script and a less than c
+ // which breaks this property.
+ // * Use compareSectionsNonScript. Given that the script order doesn't have
+ // to match, we can end up with sections a, b, c, d where b and c are in the
+ // script and c is compareSectionsNonScript less than b. In which case d
+ // can be equivalent to c, a to b and d < a. As a concrete example:
+ // .a (rx) # not in script
+ // .b (rx) # in script
+ // .c (ro) # in script
+ // .d (ro) # not in script
+ //
+ // The way we define an order then is:
+ // * First put script sections at the start and sort the script and
+ // non-script sections independently.
+ // * Move each non-script section to its preferred position. We try
+ // to put each section in the last position where it it can share
+ // a PT_LOAD.
+
+ std::stable_sort(OutputSections.begin(), OutputSections.end(),
+ compareSections<ELFT>);
+
+ auto I = OutputSections.begin();
+ auto E = OutputSections.end();
+ auto NonScriptI =
+ std::find_if(OutputSections.begin(), E, [](OutputSectionBase *S) {
+ return Script<ELFT>::X->getSectionIndex(S->getName()) == INT_MAX;
+ });
+ while (NonScriptI != E) {
+ auto BestPos = std::max_element(
+ I, NonScriptI, [&](OutputSectionBase *&A, OutputSectionBase *&B) {
+ bool ACanSharePtLoad = canSharePtLoad<ELFT>(**NonScriptI, *A);
+ bool BCanSharePtLoad = canSharePtLoad<ELFT>(**NonScriptI, *B);
+ if (ACanSharePtLoad != BCanSharePtLoad)
+ return BCanSharePtLoad;
+
+ bool ACmp = compareSectionsNonScript<ELFT>(*NonScriptI, A);
+ bool BCmp = compareSectionsNonScript<ELFT>(*NonScriptI, B);
+ if (ACmp != BCmp)
+ return BCmp; // FIXME: missing test
+
+ size_t PosA = &A - &OutputSections[0];
+ size_t PosB = &B - &OutputSections[0];
+ return ACmp ? PosA > PosB : PosA < PosB;
+ });
+
+ // max_element only returns NonScriptI if the range is empty. If the range
+ // is not empty we should consider moving the the element forward one
+ // position.
+ if (BestPos != NonScriptI &&
+ !compareSectionsNonScript<ELFT>(*NonScriptI, *BestPos))
+ ++BestPos;
+ std::rotate(BestPos, NonScriptI, NonScriptI + 1);
+ ++NonScriptI;
+ }
+
+ Script<ELFT>::X->adjustSectionsAfterSorting();
+}
+
+template <class ELFT>
+static void
+finalizeSynthetic(const std::vector<SyntheticSection<ELFT> *> &Sections) {
+ for (SyntheticSection<ELFT> *SS : Sections)
+ if (SS && SS->OutSec && !SS->empty()) {
+ SS->finalize();
+ SS->OutSec->Size = 0;
+ SS->OutSec->assignOffsets();
+ }
+}
+
+// We need to add input synthetic sections early in createSyntheticSections()
+// to make them visible from linkescript side. But not all sections are always
+// required to be in output. For example we don't need dynamic section content
+// sometimes. This function filters out such unused sections from output.
+template <class ELFT>
+static void removeUnusedSyntheticSections(std::vector<OutputSectionBase *> &V) {
+ // Input synthetic sections are placed after all regular ones. We iterate over
+ // them all and exit at first non-synthetic.
+ for (InputSectionBase<ELFT> *S : llvm::reverse(Symtab<ELFT>::X->Sections)) {
+ SyntheticSection<ELFT> *SS = dyn_cast<SyntheticSection<ELFT>>(S);
+ if (!SS)
+ return;
+ if (!SS->empty() || !SS->OutSec)
+ continue;
- Out<ELFT>::Dynamic->PreInitArraySec = Factory.lookup(
- ".preinit_array", SHT_PREINIT_ARRAY, SHF_WRITE | SHF_ALLOC);
- Out<ELFT>::Dynamic->InitArraySec =
- Factory.lookup(".init_array", SHT_INIT_ARRAY, SHF_WRITE | SHF_ALLOC);
- Out<ELFT>::Dynamic->FiniArraySec =
- Factory.lookup(".fini_array", SHT_FINI_ARRAY, SHF_WRITE | SHF_ALLOC);
+ OutputSection<ELFT> *OutSec = cast<OutputSection<ELFT>>(SS->OutSec);
+ OutSec->Sections.erase(
+ std::find(OutSec->Sections.begin(), OutSec->Sections.end(), SS));
+ // If there is no other sections in output section, remove it from output.
+ if (OutSec->Sections.empty())
+ V.erase(std::find(V.begin(), V.end(), OutSec));
+ }
+}
- // Sort section contents for __attribute__((init_priority(N)).
- sortInitFini(Out<ELFT>::Dynamic->InitArraySec);
- sortInitFini(Out<ELFT>::Dynamic->FiniArraySec);
- sortCtorsDtors(Factory.lookup(".ctors", SHT_PROGBITS, SHF_WRITE | SHF_ALLOC));
- sortCtorsDtors(Factory.lookup(".dtors", SHT_PROGBITS, SHF_WRITE | SHF_ALLOC));
+// Create output section objects and add them to OutputSections.
+template <class ELFT> void Writer<ELFT>::finalizeSections() {
+ Out<ELFT>::DebugInfo = findSection(".debug_info");
+ Out<ELFT>::PreinitArray = findSection(".preinit_array");
+ Out<ELFT>::InitArray = findSection(".init_array");
+ Out<ELFT>::FiniArray = findSection(".fini_array");
// The linker needs to define SECNAME_start, SECNAME_end and SECNAME_stop
// symbols for sections, so that the runtime can get the start and end
// addresses of each section by section name. Add such symbols.
if (!Config->Relocatable) {
addStartEndSymbols();
- for (OutputSectionBase<ELFT> *Sec : RegularSections)
+ for (OutputSectionBase *Sec : OutputSections)
addStartStopSymbols(Sec);
}
@@ -658,16 +992,12 @@ template <class ELFT> void Writer<ELFT>::createSections() {
// It should be okay as no one seems to care about the type.
// Even the author of gold doesn't remember why gold behaves that way.
// https://sourceware.org/ml/binutils/2002-03/msg00360.html
- if (isOutputDynamic())
- Symtab.addSynthetic("_DYNAMIC", Out<ELFT>::Dynamic, 0);
+ if (In<ELFT>::DynSymTab)
+ addRegular("_DYNAMIC", In<ELFT>::Dynamic, 0);
// Define __rel[a]_iplt_{start,end} symbols if needed.
addRelIpltSymbols();
- // Add scripted symbols with zero values now.
- // Real values will be assigned later
- Script<ELFT>::X->addScriptedSymbols();
-
if (!Out<ELFT>::EhFrame->empty()) {
OutputSections.push_back(Out<ELFT>::EhFrame);
Out<ELFT>::EhFrame->finalize();
@@ -675,190 +1005,102 @@ template <class ELFT> void Writer<ELFT>::createSections() {
// Scan relocations. This must be done after every symbol is declared so that
// we can correctly decide if a dynamic relocation is needed.
- for (const std::unique_ptr<elf::ObjectFile<ELFT>> &F :
- Symtab.getObjectFiles()) {
- for (InputSectionBase<ELFT> *C : F->getSections()) {
- if (isDiscarded(C))
- continue;
- if (auto *S = dyn_cast<InputSection<ELFT>>(C)) {
- scanRelocations(*S);
- continue;
- }
- if (auto *S = dyn_cast<EhInputSection<ELFT>>(C))
- if (S->RelocSection)
- scanRelocations(*S, *S->RelocSection);
- }
- }
-
- for (OutputSectionBase<ELFT> *Sec : OutputSections)
- Sec->assignOffsets();
+ forEachRelSec(scanRelocations<ELFT>);
// Now that we have defined all possible symbols including linker-
// synthesized ones. Visit all symbols to give the finishing touches.
- std::vector<DefinedCommon *> CommonSymbols;
- for (Symbol *S : Symtab.getSymbols()) {
+ for (Symbol *S : Symtab<ELFT>::X->getSymbols()) {
SymbolBody *Body = S->body();
- // We only report undefined symbols in regular objects. This means that we
- // will accept an undefined reference in bitcode if it can be optimized out.
- if (S->IsUsedInRegularObj && Body->isUndefined() && !S->isWeak())
- reportUndefined<ELFT>(Symtab, Body);
-
- if (auto *C = dyn_cast<DefinedCommon>(Body))
- CommonSymbols.push_back(C);
-
if (!includeInSymtab<ELFT>(*Body))
continue;
- if (Out<ELFT>::SymTab)
- Out<ELFT>::SymTab->addSymbol(Body);
+ if (In<ELFT>::SymTab)
+ In<ELFT>::SymTab->addSymbol(Body);
- if (isOutputDynamic() && S->includeInDynsym()) {
- Out<ELFT>::DynSymTab->addSymbol(Body);
+ if (In<ELFT>::DynSymTab && S->includeInDynsym()) {
+ In<ELFT>::DynSymTab->addSymbol(Body);
if (auto *SS = dyn_cast<SharedSymbol<ELFT>>(Body))
if (SS->file()->isNeeded())
- Out<ELFT>::VerNeed->addSymbol(SS);
+ In<ELFT>::VerNeed->addSymbol(SS);
}
}
// Do not proceed if there was an undefined symbol.
- if (HasError)
+ if (ErrorCount)
return;
- addCommonSymbols(CommonSymbols);
-
// So far we have added sections from input object files.
// This function adds linker-created Out<ELFT>::* sections.
addPredefinedSections();
+ removeUnusedSyntheticSections<ELFT>(OutputSections);
- std::stable_sort(OutputSections.begin(), OutputSections.end(),
- compareSections<ELFT>);
+ sortSections();
unsigned I = 1;
- for (OutputSectionBase<ELFT> *Sec : OutputSections) {
+ for (OutputSectionBase *Sec : OutputSections) {
Sec->SectionIndex = I++;
- Sec->setSHName(Out<ELFT>::ShStrTab->addString(Sec->getName()));
+ Sec->ShName = In<ELFT>::ShStrTab->addString(Sec->getName());
}
- // Finalizers fix each section's size.
- // .dynsym is finalized early since that may fill up .gnu.hash.
- if (isOutputDynamic())
- Out<ELFT>::DynSymTab->finalize();
+ // Binary and relocatable output does not have PHDRS.
+ // The headers have to be created before finalize as that can influence the
+ // image base and the dynamic section on mips includes the image base.
+ if (!Config->Relocatable && !Config->OFormatBinary) {
+ Phdrs = Script<ELFT>::X->hasPhdrsCommands() ? Script<ELFT>::X->createPhdrs()
+ : createPhdrs();
+ addPtArmExid(Phdrs);
+ fixHeaders();
+ }
// Fill other section headers. The dynamic table is finalized
// at the end because some tags like RELSZ depend on result
- // of finalizing other sections. The dynamic string table is
- // finalized once the .dynamic finalizer has added a few last
- // strings. See DynamicSection::finalize()
- for (OutputSectionBase<ELFT> *Sec : OutputSections)
- if (Sec != Out<ELFT>::DynStrTab && Sec != Out<ELFT>::Dynamic)
- Sec->finalize();
-
- if (isOutputDynamic())
- Out<ELFT>::Dynamic->finalize();
-
- // Now that all output offsets are fixed. Finalize mergeable sections
- // to fix their maps from input offsets to output offsets.
- for (OutputSectionBase<ELFT> *Sec : OutputSections)
- Sec->finalizePieces();
+ // of finalizing other sections.
+ for (OutputSectionBase *Sec : OutputSections)
+ Sec->finalize();
+
+ // Dynamic section must be the last one in this list and dynamic
+ // symbol table section (DynSymTab) must be the first one.
+ finalizeSynthetic<ELFT>(
+ {In<ELFT>::DynSymTab, In<ELFT>::GnuHashTab, In<ELFT>::HashTab,
+ In<ELFT>::SymTab, In<ELFT>::ShStrTab, In<ELFT>::StrTab,
+ In<ELFT>::VerDef, In<ELFT>::DynStrTab, In<ELFT>::GdbIndex,
+ In<ELFT>::Got, In<ELFT>::MipsGot, In<ELFT>::IgotPlt,
+ In<ELFT>::GotPlt, In<ELFT>::RelaDyn, In<ELFT>::RelaIplt,
+ In<ELFT>::RelaPlt, In<ELFT>::Plt, In<ELFT>::Iplt,
+ In<ELFT>::Plt, In<ELFT>::EhFrameHdr, In<ELFT>::VerSym,
+ In<ELFT>::VerNeed, In<ELFT>::Dynamic});
}
-template <class ELFT> bool Writer<ELFT>::needsGot() {
- if (!Out<ELFT>::Got->empty())
- return true;
-
- // We add the .got section to the result for dynamic MIPS target because
- // its address and properties are mentioned in the .dynamic section.
- if (Config->EMachine == EM_MIPS)
- return true;
-
- // If we have a relocation that is relative to GOT (such as GOTOFFREL),
- // we need to emit a GOT even if it's empty.
- return Out<ELFT>::Got->HasGotOffRel;
-}
-
-// This function add Out<ELFT>::* sections to OutputSections.
template <class ELFT> void Writer<ELFT>::addPredefinedSections() {
- auto Add = [&](OutputSectionBase<ELFT> *C) {
- if (C)
- OutputSections.push_back(C);
- };
-
- // A core file does not usually contain unmodified segments except
- // the first page of the executable. Add the build ID section to beginning of
- // the file so that the section is included in the first page.
- if (Out<ELFT>::BuildId)
- OutputSections.insert(OutputSections.begin(), Out<ELFT>::BuildId);
-
- // Add .interp at first because some loaders want to see that section
- // on the first page of the executable file when loaded into memory.
- if (needsInterpSection())
- OutputSections.insert(OutputSections.begin(), Out<ELFT>::Interp);
-
- // This order is not the same as the final output order
- // because we sort the sections using their attributes below.
- Add(Out<ELFT>::SymTab);
- Add(Out<ELFT>::ShStrTab);
- Add(Out<ELFT>::StrTab);
- if (isOutputDynamic()) {
- Add(Out<ELFT>::DynSymTab);
-
- bool HasVerNeed = Out<ELFT>::VerNeed->getNeedNum() != 0;
- if (Out<ELFT>::VerDef || HasVerNeed)
- Add(Out<ELFT>::VerSym);
- Add(Out<ELFT>::VerDef);
- if (HasVerNeed)
- Add(Out<ELFT>::VerNeed);
-
- Add(Out<ELFT>::GnuHashTab);
- Add(Out<ELFT>::HashTab);
- Add(Out<ELFT>::Dynamic);
- Add(Out<ELFT>::DynStrTab);
- if (Out<ELFT>::RelaDyn->hasRelocs())
- Add(Out<ELFT>::RelaDyn);
- Add(Out<ELFT>::MipsRldMap);
- }
+ if (Out<ELFT>::Bss->Size > 0)
+ OutputSections.push_back(Out<ELFT>::Bss);
- // We always need to add rel[a].plt to output if it has entries.
- // Even during static linking it can contain R_[*]_IRELATIVE relocations.
- if (Out<ELFT>::RelaPlt && Out<ELFT>::RelaPlt->hasRelocs()) {
- Add(Out<ELFT>::RelaPlt);
- Out<ELFT>::RelaPlt->Static = !isOutputDynamic();
- }
+ auto OS = dyn_cast_or_null<OutputSection<ELFT>>(findSection(".ARM.exidx"));
+ if (OS && !OS->Sections.empty() && !Config->Relocatable)
+ OS->addSection(make<ARMExidxSentinelSection<ELFT>>());
- if (needsGot())
- Add(Out<ELFT>::Got);
- if (Out<ELFT>::GotPlt && !Out<ELFT>::GotPlt->empty())
- Add(Out<ELFT>::GotPlt);
- if (!Out<ELFT>::Plt->empty())
- Add(Out<ELFT>::Plt);
- if (!Out<ELFT>::EhFrame->empty())
- Add(Out<ELFT>::EhFrameHdr);
- if (Out<ELFT>::Bss->getSize() > 0)
- Add(Out<ELFT>::Bss);
+ addInputSec(In<ELFT>::SymTab);
+ addInputSec(In<ELFT>::ShStrTab);
+ addInputSec(In<ELFT>::StrTab);
}
// The linker is expected to define SECNAME_start and SECNAME_end
// symbols for a few sections. This function defines them.
template <class ELFT> void Writer<ELFT>::addStartEndSymbols() {
- auto Define = [&](StringRef Start, StringRef End,
- OutputSectionBase<ELFT> *OS) {
- if (OS) {
- this->Symtab.addSynthetic(Start, OS, 0);
- this->Symtab.addSynthetic(End, OS, DefinedSynthetic<ELFT>::SectionEnd);
- } else {
- addOptionalSynthetic(this->Symtab, Start,
- (OutputSectionBase<ELFT> *)nullptr, 0);
- addOptionalSynthetic(this->Symtab, End,
- (OutputSectionBase<ELFT> *)nullptr, 0);
- }
+ auto Define = [&](StringRef Start, StringRef End, OutputSectionBase *OS) {
+ // These symbols resolve to the image base if the section does not exist.
+ // A special value -1 indicates end of the section.
+ addOptionalSynthetic<ELFT>(Start, OS, 0);
+ addOptionalSynthetic<ELFT>(End, OS, OS ? -1 : 0);
};
Define("__preinit_array_start", "__preinit_array_end",
- Out<ELFT>::Dynamic->PreInitArraySec);
- Define("__init_array_start", "__init_array_end",
- Out<ELFT>::Dynamic->InitArraySec);
- Define("__fini_array_start", "__fini_array_end",
- Out<ELFT>::Dynamic->FiniArraySec);
+ Out<ELFT>::PreinitArray);
+ Define("__init_array_start", "__init_array_end", Out<ELFT>::InitArray);
+ Define("__fini_array_start", "__fini_array_end", Out<ELFT>::FiniArray);
+
+ if (OutputSectionBase *Sec = findSection(".ARM.exidx"))
+ Define("__exidx_start", "__exidx_end", Sec);
}
// If a section name is valid as a C identifier (which is rare because of
@@ -867,145 +1109,181 @@ template <class ELFT> void Writer<ELFT>::addStartEndSymbols() {
// respectively. This is not requested by the ELF standard, but GNU ld and
// gold provide the feature, and used by many programs.
template <class ELFT>
-void Writer<ELFT>::addStartStopSymbols(OutputSectionBase<ELFT> *Sec) {
+void Writer<ELFT>::addStartStopSymbols(OutputSectionBase *Sec) {
StringRef S = Sec->getName();
if (!isValidCIdentifier(S))
return;
- StringSaver Saver(Alloc);
- StringRef Start = Saver.save("__start_" + S);
- StringRef Stop = Saver.save("__stop_" + S);
- if (SymbolBody *B = Symtab.find(Start))
- if (B->isUndefined())
- Symtab.addSynthetic(Start, Sec, 0);
- if (SymbolBody *B = Symtab.find(Stop))
- if (B->isUndefined())
- Symtab.addSynthetic(Stop, Sec, DefinedSynthetic<ELFT>::SectionEnd);
+ addOptionalSynthetic<ELFT>(Saver.save("__start_" + S), Sec, 0, STV_DEFAULT);
+ addOptionalSynthetic<ELFT>(Saver.save("__stop_" + S), Sec, -1, STV_DEFAULT);
+}
+
+template <class ELFT>
+OutputSectionBase *Writer<ELFT>::findSection(StringRef Name) {
+ for (OutputSectionBase *Sec : OutputSections)
+ if (Sec->getName() == Name)
+ return Sec;
+ return nullptr;
}
-template <class ELFT> static bool needsPtLoad(OutputSectionBase<ELFT> *Sec) {
- if (!(Sec->getFlags() & SHF_ALLOC))
+template <class ELFT> static bool needsPtLoad(OutputSectionBase *Sec) {
+ if (!(Sec->Flags & SHF_ALLOC))
return false;
// Don't allocate VA space for TLS NOBITS sections. The PT_TLS PHDR is
// responsible for allocating space for them, not the PT_LOAD that
// contains the TLS initialization image.
- if (Sec->getFlags() & SHF_TLS && Sec->getType() == SHT_NOBITS)
+ if (Sec->Flags & SHF_TLS && Sec->Type == SHT_NOBITS)
return false;
return true;
}
-static uint32_t toPhdrFlags(uint64_t Flags) {
- uint32_t Ret = PF_R;
- if (Flags & SHF_WRITE)
- Ret |= PF_W;
- if (Flags & SHF_EXECINSTR)
- Ret |= PF_X;
- return Ret;
+// Linker scripts are responsible for aligning addresses. Unfortunately, most
+// linker scripts are designed for creating two PT_LOADs only, one RX and one
+// RW. This means that there is no alignment in the RO to RX transition and we
+// cannot create a PT_LOAD there.
+template <class ELFT>
+static typename ELFT::uint computeFlags(typename ELFT::uint F) {
+ if (Config->OMagic)
+ return PF_R | PF_W | PF_X;
+ if (Config->SingleRoRx && !(F & PF_W))
+ return F | PF_X;
+ return F;
}
// Decide which program headers to create and which sections to include in each
// one.
-template <class ELFT> void Writer<ELFT>::createPhdrs() {
- auto AddHdr = [this](unsigned Type, unsigned Flags) {
- return &*Phdrs.emplace(Phdrs.end(), Type, Flags);
- };
-
- auto AddSec = [](Phdr &Hdr, OutputSectionBase<ELFT> *Sec) {
- Hdr.Last = Sec;
- if (!Hdr.First)
- Hdr.First = Sec;
- Hdr.H.p_align = std::max<uintX_t>(Hdr.H.p_align, Sec->getAlignment());
+template <class ELFT> std::vector<PhdrEntry> Writer<ELFT>::createPhdrs() {
+ std::vector<PhdrEntry> Ret;
+ auto AddHdr = [&](unsigned Type, unsigned Flags) -> PhdrEntry * {
+ Ret.emplace_back(Type, Flags);
+ return &Ret.back();
};
// The first phdr entry is PT_PHDR which describes the program header itself.
- Phdr &Hdr = *AddHdr(PT_PHDR, PF_R);
- AddSec(Hdr, Out<ELFT>::ProgramHeaders);
+ PhdrEntry &Hdr = *AddHdr(PT_PHDR, PF_R);
+ Hdr.add(Out<ELFT>::ProgramHeaders);
// PT_INTERP must be the second entry if exists.
- if (needsInterpSection()) {
- Phdr &Hdr = *AddHdr(PT_INTERP, toPhdrFlags(Out<ELFT>::Interp->getFlags()));
- AddSec(Hdr, Out<ELFT>::Interp);
+ if (OutputSectionBase *Sec = findSection(".interp")) {
+ PhdrEntry &Hdr = *AddHdr(PT_INTERP, Sec->getPhdrFlags());
+ Hdr.add(Sec);
}
// Add the first PT_LOAD segment for regular output sections.
- uintX_t Flags = PF_R;
- Phdr *Load = AddHdr(PT_LOAD, Flags);
- AddSec(*Load, Out<ELFT>::ElfHeader);
- AddSec(*Load, Out<ELFT>::ProgramHeaders);
-
- Phdr TlsHdr(PT_TLS, PF_R);
- Phdr RelRo(PT_GNU_RELRO, PF_R);
- Phdr Note(PT_NOTE, PF_R);
- for (OutputSectionBase<ELFT> *Sec : OutputSections) {
- if (!(Sec->getFlags() & SHF_ALLOC))
+ uintX_t Flags = computeFlags<ELFT>(PF_R);
+ PhdrEntry *Load = AddHdr(PT_LOAD, Flags);
+
+ PhdrEntry TlsHdr(PT_TLS, PF_R);
+ PhdrEntry RelRo(PT_GNU_RELRO, PF_R);
+ PhdrEntry Note(PT_NOTE, PF_R);
+ for (OutputSectionBase *Sec : OutputSections) {
+ if (!(Sec->Flags & SHF_ALLOC))
break;
// If we meet TLS section then we create TLS header
- // and put all TLS sections inside for futher use when
+ // and put all TLS sections inside for further use when
// assign addresses.
- if (Sec->getFlags() & SHF_TLS)
- AddSec(TlsHdr, Sec);
+ if (Sec->Flags & SHF_TLS)
+ TlsHdr.add(Sec);
if (!needsPtLoad<ELFT>(Sec))
continue;
- // If flags changed then we want new load segment.
- uintX_t NewFlags = toPhdrFlags(Sec->getFlags());
- if (Flags != NewFlags) {
+ // Segments are contiguous memory regions that has the same attributes
+ // (e.g. executable or writable). There is one phdr for each segment.
+ // Therefore, we need to create a new phdr when the next section has
+ // different flags or is loaded at a discontiguous address using AT linker
+ // script command.
+ uintX_t NewFlags = computeFlags<ELFT>(Sec->getPhdrFlags());
+ if (Script<ELFT>::X->hasLMA(Sec->getName()) || Flags != NewFlags) {
Load = AddHdr(PT_LOAD, NewFlags);
Flags = NewFlags;
}
- AddSec(*Load, Sec);
+ Load->add(Sec);
- if (isRelroSection(Sec))
- AddSec(RelRo, Sec);
- if (Sec->getType() == SHT_NOTE)
- AddSec(Note, Sec);
+ if (isRelroSection<ELFT>(Sec))
+ RelRo.add(Sec);
+ if (Sec->Type == SHT_NOTE)
+ Note.add(Sec);
}
// Add the TLS segment unless it's empty.
if (TlsHdr.First)
- Phdrs.push_back(std::move(TlsHdr));
+ Ret.push_back(std::move(TlsHdr));
// Add an entry for .dynamic.
- if (isOutputDynamic()) {
- Phdr &H = *AddHdr(PT_DYNAMIC, toPhdrFlags(Out<ELFT>::Dynamic->getFlags()));
- AddSec(H, Out<ELFT>::Dynamic);
+ if (In<ELFT>::DynSymTab) {
+ PhdrEntry &H =
+ *AddHdr(PT_DYNAMIC, In<ELFT>::Dynamic->OutSec->getPhdrFlags());
+ H.add(In<ELFT>::Dynamic->OutSec);
}
// PT_GNU_RELRO includes all sections that should be marked as
// read-only by dynamic linker after proccessing relocations.
if (RelRo.First)
- Phdrs.push_back(std::move(RelRo));
+ Ret.push_back(std::move(RelRo));
// PT_GNU_EH_FRAME is a special section pointing on .eh_frame_hdr.
- if (!Out<ELFT>::EhFrame->empty() && Out<ELFT>::EhFrameHdr) {
- Phdr &Hdr = *AddHdr(PT_GNU_EH_FRAME,
- toPhdrFlags(Out<ELFT>::EhFrameHdr->getFlags()));
- AddSec(Hdr, Out<ELFT>::EhFrameHdr);
+ if (!Out<ELFT>::EhFrame->empty() && In<ELFT>::EhFrameHdr) {
+ PhdrEntry &Hdr =
+ *AddHdr(PT_GNU_EH_FRAME, In<ELFT>::EhFrameHdr->OutSec->getPhdrFlags());
+ Hdr.add(In<ELFT>::EhFrameHdr->OutSec);
+ }
+
+ // PT_OPENBSD_RANDOMIZE specifies the location and size of a part of the
+ // memory image of the program that must be filled with random data before any
+ // code in the object is executed.
+ if (OutputSectionBase *Sec = findSection(".openbsd.randomdata")) {
+ PhdrEntry &Hdr = *AddHdr(PT_OPENBSD_RANDOMIZE, Sec->getPhdrFlags());
+ Hdr.add(Sec);
}
// PT_GNU_STACK is a special section to tell the loader to make the
// pages for the stack non-executable.
- if (!Config->ZExecStack)
- AddHdr(PT_GNU_STACK, PF_R | PF_W);
+ if (!Config->ZExecstack) {
+ PhdrEntry &Hdr = *AddHdr(PT_GNU_STACK, PF_R | PF_W);
+ if (Config->ZStackSize != uint64_t(-1))
+ Hdr.p_memsz = Config->ZStackSize;
+ }
+
+ // PT_OPENBSD_WXNEEDED is a OpenBSD-specific header to mark the executable
+ // is expected to perform W^X violations, such as calling mprotect(2) or
+ // mmap(2) with PROT_WRITE | PROT_EXEC, which is prohibited by default on
+ // OpenBSD.
+ if (Config->ZWxneeded)
+ AddHdr(PT_OPENBSD_WXNEEDED, PF_X);
if (Note.First)
- Phdrs.push_back(std::move(Note));
+ Ret.push_back(std::move(Note));
+ return Ret;
+}
+
+template <class ELFT>
+void Writer<ELFT>::addPtArmExid(std::vector<PhdrEntry> &Phdrs) {
+ if (Config->EMachine != EM_ARM)
+ return;
+ auto I = std::find_if(
+ OutputSections.begin(), OutputSections.end(),
+ [](OutputSectionBase *Sec) { return Sec->Type == SHT_ARM_EXIDX; });
+ if (I == OutputSections.end())
+ return;
- Out<ELFT>::ProgramHeaders->setSize(sizeof(Elf_Phdr) * Phdrs.size());
+ // PT_ARM_EXIDX is the ARM EHABI equivalent of PT_GNU_EH_FRAME
+ PhdrEntry ARMExidx(PT_ARM_EXIDX, PF_R);
+ ARMExidx.add(*I);
+ Phdrs.push_back(ARMExidx);
}
// The first section of each PT_LOAD and the first section after PT_GNU_RELRO
// have to be page aligned so that the dynamic linker can set the permissions.
template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
- for (const Phdr &P : Phdrs)
- if (P.H.p_type == PT_LOAD)
+ for (const PhdrEntry &P : Phdrs)
+ if (P.p_type == PT_LOAD && P.First)
P.First->PageAlign = true;
- for (const Phdr &P : Phdrs) {
- if (P.H.p_type != PT_GNU_RELRO)
+ for (const PhdrEntry &P : Phdrs) {
+ if (P.p_type != PT_GNU_RELRO)
continue;
// Find the first section after PT_GNU_RELRO. If it is in a PT_LOAD we
// have to align it to a page.
@@ -1013,43 +1291,86 @@ template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
auto I = std::find(OutputSections.begin(), End, P.Last);
if (I == End || (I + 1) == End)
continue;
- OutputSectionBase<ELFT> *Sec = *(I + 1);
- if (needsPtLoad(Sec))
+ OutputSectionBase *Sec = *(I + 1);
+ if (needsPtLoad<ELFT>(Sec))
Sec->PageAlign = true;
}
}
+template <class ELFT>
+void elf::allocateHeaders(MutableArrayRef<PhdrEntry> Phdrs,
+ ArrayRef<OutputSectionBase *> OutputSections) {
+ auto FirstPTLoad =
+ std::find_if(Phdrs.begin(), Phdrs.end(),
+ [](const PhdrEntry &E) { return E.p_type == PT_LOAD; });
+ if (FirstPTLoad == Phdrs.end())
+ return;
+ if (FirstPTLoad->First)
+ for (OutputSectionBase *Sec : OutputSections)
+ if (Sec->FirstInPtLoad == FirstPTLoad->First)
+ Sec->FirstInPtLoad = Out<ELFT>::ElfHeader;
+ FirstPTLoad->First = Out<ELFT>::ElfHeader;
+ if (!FirstPTLoad->Last)
+ FirstPTLoad->Last = Out<ELFT>::ProgramHeaders;
+}
+
// We should set file offsets and VAs for elf header and program headers
// sections. These are special, we do not include them into output sections
// list, but have them to simplify the code.
template <class ELFT> void Writer<ELFT>::fixHeaders() {
- uintX_t BaseVA = ScriptConfig->DoLayout ? 0 : Config->ImageBase;
- Out<ELFT>::ElfHeader->setVA(BaseVA);
- uintX_t Off = Out<ELFT>::ElfHeader->getSize();
- Out<ELFT>::ProgramHeaders->setVA(Off + BaseVA);
+ Out<ELFT>::ProgramHeaders->Size = sizeof(Elf_Phdr) * Phdrs.size();
+ // If the script has SECTIONS, assignAddresses will compute the values.
+ if (ScriptConfig->HasSections)
+ return;
+
+ uintX_t HeaderSize = getHeaderSize<ELFT>();
+ // When -T<section> option is specified, lower the base to make room for those
+ // sections.
+ if (!Config->SectionStartMap.empty()) {
+ uint64_t Min = -1;
+ for (const auto &P : Config->SectionStartMap)
+ Min = std::min(Min, P.second);
+ if (HeaderSize < Min)
+ Min -= HeaderSize;
+ else
+ AllocateHeader = false;
+ if (Min < Config->ImageBase)
+ Config->ImageBase = alignDown(Min, Config->MaxPageSize);
+ }
+
+ if (AllocateHeader)
+ allocateHeaders<ELFT>(Phdrs, OutputSections);
+
+ uintX_t BaseVA = Config->ImageBase;
+ Out<ELFT>::ElfHeader->Addr = BaseVA;
+ Out<ELFT>::ProgramHeaders->Addr = BaseVA + Out<ELFT>::ElfHeader->Size;
}
// Assign VAs (addresses at run-time) to output sections.
template <class ELFT> void Writer<ELFT>::assignAddresses() {
- uintX_t VA = Config->ImageBase + Out<ELFT>::ElfHeader->getSize() +
- Out<ELFT>::ProgramHeaders->getSize();
-
+ uintX_t VA = Config->ImageBase;
+ if (AllocateHeader)
+ VA += getHeaderSize<ELFT>();
uintX_t ThreadBssOffset = 0;
- for (OutputSectionBase<ELFT> *Sec : OutputSections) {
- uintX_t Alignment = Sec->getAlignment();
+ for (OutputSectionBase *Sec : OutputSections) {
+ uintX_t Alignment = Sec->Addralign;
if (Sec->PageAlign)
- Alignment = std::max<uintX_t>(Alignment, Target->PageSize);
+ Alignment = std::max<uintX_t>(Alignment, Config->MaxPageSize);
+
+ auto I = Config->SectionStartMap.find(Sec->getName());
+ if (I != Config->SectionStartMap.end())
+ VA = I->second;
// We only assign VAs to allocated sections.
if (needsPtLoad<ELFT>(Sec)) {
VA = alignTo(VA, Alignment);
- Sec->setVA(VA);
- VA += Sec->getSize();
- } else if (Sec->getFlags() & SHF_TLS && Sec->getType() == SHT_NOBITS) {
+ Sec->Addr = VA;
+ VA += Sec->Size;
+ } else if (Sec->Flags & SHF_TLS && Sec->Type == SHT_NOBITS) {
uintX_t TVA = VA + ThreadBssOffset;
TVA = alignTo(TVA, Alignment);
- Sec->setVA(TVA);
- ThreadBssOffset = TVA - VA + Sec->getSize();
+ Sec->Addr = TVA;
+ ThreadBssOffset = TVA - VA + Sec->Size;
}
}
}
@@ -1059,38 +1380,50 @@ template <class ELFT> void Writer<ELFT>::assignAddresses() {
// virtual address (modulo the page size) so that the loader can load
// executables without any address adjustment.
template <class ELFT, class uintX_t>
-static uintX_t getFileAlignment(uintX_t Off, OutputSectionBase<ELFT> *Sec) {
- uintX_t Alignment = Sec->getAlignment();
- if (Sec->PageAlign)
- Alignment = std::max<uintX_t>(Alignment, Target->PageSize);
- Off = alignTo(Off, Alignment);
-
- // Relocatable output does not have program headers
- // and does not need any other offset adjusting.
- if (Config->Relocatable || !(Sec->getFlags() & SHF_ALLOC))
- return Off;
- return alignTo(Off, Target->PageSize, Sec->getVA());
+static uintX_t getFileAlignment(uintX_t Off, OutputSectionBase *Sec) {
+ OutputSectionBase *First = Sec->FirstInPtLoad;
+ // If the section is not in a PT_LOAD, we just have to align it.
+ if (!First)
+ return alignTo(Off, Sec->Addralign);
+
+ // The first section in a PT_LOAD has to have congruent offset and address
+ // module the page size.
+ if (Sec == First)
+ return alignTo(Off, Config->MaxPageSize, Sec->Addr);
+
+ // If two sections share the same PT_LOAD the file offset is calculated
+ // using this formula: Off2 = Off1 + (VA2 - VA1).
+ return First->Offset + Sec->Addr - First->Addr;
+}
+
+template <class ELFT, class uintX_t>
+void setOffset(OutputSectionBase *Sec, uintX_t &Off) {
+ if (Sec->Type == SHT_NOBITS) {
+ Sec->Offset = Off;
+ return;
+ }
+
+ Off = getFileAlignment<ELFT>(Off, Sec);
+ Sec->Offset = Off;
+ Off += Sec->Size;
+}
+
+template <class ELFT> void Writer<ELFT>::assignFileOffsetsBinary() {
+ uintX_t Off = 0;
+ for (OutputSectionBase *Sec : OutputSections)
+ if (Sec->Flags & SHF_ALLOC)
+ setOffset<ELFT>(Sec, Off);
+ FileSize = alignTo(Off, sizeof(uintX_t));
}
// Assign file offsets to output sections.
template <class ELFT> void Writer<ELFT>::assignFileOffsets() {
uintX_t Off = 0;
+ setOffset<ELFT>(Out<ELFT>::ElfHeader, Off);
+ setOffset<ELFT>(Out<ELFT>::ProgramHeaders, Off);
- auto Set = [&](OutputSectionBase<ELFT> *Sec) {
- if (Sec->getType() == SHT_NOBITS) {
- Sec->setFileOffset(Off);
- return;
- }
-
- Off = getFileAlignment<ELFT>(Off, Sec);
- Sec->setFileOffset(Off);
- Off += Sec->getSize();
- };
-
- Set(Out<ELFT>::ElfHeader);
- Set(Out<ELFT>::ProgramHeaders);
- for (OutputSectionBase<ELFT> *Sec : OutputSections)
- Set(Sec);
+ for (OutputSectionBase *Sec : OutputSections)
+ setOffset<ELFT>(Sec, Off);
SectionHeaderOff = alignTo(Off, sizeof(uintX_t));
FileSize = SectionHeaderOff + (OutputSections.size() + 1) * sizeof(Elf_Shdr);
@@ -1099,50 +1432,62 @@ template <class ELFT> void Writer<ELFT>::assignFileOffsets() {
// Finalize the program headers. We call this function after we assign
// file offsets and VAs to all sections.
template <class ELFT> void Writer<ELFT>::setPhdrs() {
- for (Phdr &P : Phdrs) {
- Elf_Phdr &H = P.H;
- OutputSectionBase<ELFT> *First = P.First;
- OutputSectionBase<ELFT> *Last = P.Last;
+ for (PhdrEntry &P : Phdrs) {
+ OutputSectionBase *First = P.First;
+ OutputSectionBase *Last = P.Last;
if (First) {
- H.p_filesz = Last->getFileOff() - First->getFileOff();
- if (Last->getType() != SHT_NOBITS)
- H.p_filesz += Last->getSize();
- H.p_memsz = Last->getVA() + Last->getSize() - First->getVA();
- H.p_offset = First->getFileOff();
- H.p_vaddr = First->getVA();
+ P.p_filesz = Last->Offset - First->Offset;
+ if (Last->Type != SHT_NOBITS)
+ P.p_filesz += Last->Size;
+ P.p_memsz = Last->Addr + Last->Size - First->Addr;
+ P.p_offset = First->Offset;
+ P.p_vaddr = First->Addr;
+ if (!P.HasLMA)
+ P.p_paddr = First->getLMA();
}
- if (H.p_type == PT_LOAD)
- H.p_align = Target->PageSize;
- else if (H.p_type == PT_GNU_RELRO)
- H.p_align = 1;
- H.p_paddr = H.p_vaddr;
+ if (P.p_type == PT_LOAD)
+ P.p_align = Config->MaxPageSize;
+ else if (P.p_type == PT_GNU_RELRO)
+ P.p_align = 1;
// The TLS pointer goes after PT_TLS. At least glibc will align it,
// so round up the size to make sure the offsets are correct.
- if (H.p_type == PT_TLS) {
- Out<ELFT>::TlsPhdr = &H;
- H.p_memsz = alignTo(H.p_memsz, H.p_align);
+ if (P.p_type == PT_TLS) {
+ Out<ELFT>::TlsPhdr = &P;
+ if (P.p_memsz)
+ P.p_memsz = alignTo(P.p_memsz, P.p_align);
}
}
}
-static uint32_t getMipsEFlags(bool Is64Bits) {
- // FIXME: In fact ELF flags depends on ELF flags of input object files
- // and selected emulation. For now just use hard coded values.
- if (Is64Bits)
- return EF_MIPS_CPIC | EF_MIPS_PIC | EF_MIPS_ARCH_64R2;
-
- uint32_t V = EF_MIPS_CPIC | EF_MIPS_ABI_O32 | EF_MIPS_ARCH_32R2;
- if (Config->Shared)
- V |= EF_MIPS_PIC;
- return V;
-}
+// The entry point address is chosen in the following ways.
+//
+// 1. the '-e' entry command-line option;
+// 2. the ENTRY(symbol) command in a linker control script;
+// 3. the value of the symbol start, if present;
+// 4. the address of the first byte of the .text section, if present;
+// 5. the address 0.
+template <class ELFT> typename ELFT::uint Writer<ELFT>::getEntryAddr() {
+ // Case 1, 2 or 3. As a special case, if the symbol is actually
+ // a number, we'll use that number as an address.
+ if (SymbolBody *B = Symtab<ELFT>::X->find(Config->Entry))
+ return B->getVA<ELFT>();
+ uint64_t Addr;
+ if (!Config->Entry.getAsInteger(0, Addr))
+ return Addr;
+
+ // Case 4
+ if (OutputSectionBase *Sec = findSection(".text")) {
+ if (Config->WarnMissingEntry)
+ warn("cannot find entry symbol " + Config->Entry + "; defaulting to 0x" +
+ utohexstr(Sec->Addr));
+ return Sec->Addr;
+ }
-template <class ELFT> static typename ELFT::uint getEntryAddr() {
- if (Symbol *S = Config->EntrySym)
- return S->body()->getVA<ELFT>();
- if (Config->EntryAddr != uint64_t(-1))
- return Config->EntryAddr;
+ // Case 5
+ if (Config->WarnMissingEntry)
+ warn("cannot find entry symbol " + Config->Entry +
+ "; not setting start address");
return 0;
}
@@ -1164,6 +1509,10 @@ static uint16_t getELFType() {
// to each section. This function fixes some predefined absolute
// symbol values that depend on section address and size.
template <class ELFT> void Writer<ELFT>::fixAbsoluteSymbols() {
+ // __ehdr_start is the location of program headers.
+ if (ElfSym<ELFT>::EhdrStart)
+ ElfSym<ELFT>::EhdrStart->Value = Out<ELFT>::ProgramHeaders->Addr;
+
auto Set = [](DefinedRegular<ELFT> *S1, DefinedRegular<ELFT> *S2, uintX_t V) {
if (S1)
S1->Value = V;
@@ -1174,45 +1523,66 @@ template <class ELFT> void Writer<ELFT>::fixAbsoluteSymbols() {
// _etext is the first location after the last read-only loadable segment.
// _edata is the first location after the last read-write loadable segment.
// _end is the first location after the uninitialized data region.
- for (Phdr &P : Phdrs) {
- Elf_Phdr &H = P.H;
- if (H.p_type != PT_LOAD)
+ for (PhdrEntry &P : Phdrs) {
+ if (P.p_type != PT_LOAD)
continue;
- Set(ElfSym<ELFT>::End, ElfSym<ELFT>::End2, H.p_vaddr + H.p_memsz);
+ Set(ElfSym<ELFT>::End, ElfSym<ELFT>::End2, P.p_vaddr + P.p_memsz);
- uintX_t Val = H.p_vaddr + H.p_filesz;
- if (H.p_flags & PF_W)
+ uintX_t Val = P.p_vaddr + P.p_filesz;
+ if (P.p_flags & PF_W)
Set(ElfSym<ELFT>::Edata, ElfSym<ELFT>::Edata2, Val);
else
Set(ElfSym<ELFT>::Etext, ElfSym<ELFT>::Etext2, Val);
}
+
+ // Setup MIPS _gp_disp/__gnu_local_gp symbols which should
+ // be equal to the _gp symbol's value.
+ if (Config->EMachine == EM_MIPS) {
+ if (!ElfSym<ELFT>::MipsGp->Value) {
+ // Find GP-relative section with the lowest address
+ // and use this address to calculate default _gp value.
+ uintX_t Gp = -1;
+ for (const OutputSectionBase * OS : OutputSections)
+ if ((OS->Flags & SHF_MIPS_GPREL) && OS->Addr < Gp)
+ Gp = OS->Addr;
+ if (Gp != (uintX_t)-1)
+ ElfSym<ELFT>::MipsGp->Value = Gp + 0x7ff0;
+ }
+ if (ElfSym<ELFT>::MipsGpDisp)
+ ElfSym<ELFT>::MipsGpDisp->Value = ElfSym<ELFT>::MipsGp->Value;
+ if (ElfSym<ELFT>::MipsLocalGp)
+ ElfSym<ELFT>::MipsLocalGp->Value = ElfSym<ELFT>::MipsGp->Value;
+ }
}
template <class ELFT> void Writer<ELFT>::writeHeader() {
uint8_t *Buf = Buffer->getBufferStart();
memcpy(Buf, "\177ELF", 4);
- auto &FirstObj = cast<ELFFileBase<ELFT>>(*Config->FirstElf);
-
// Write the ELF header.
auto *EHdr = reinterpret_cast<Elf_Ehdr *>(Buf);
EHdr->e_ident[EI_CLASS] = ELFT::Is64Bits ? ELFCLASS64 : ELFCLASS32;
EHdr->e_ident[EI_DATA] = getELFEncoding<ELFT>();
EHdr->e_ident[EI_VERSION] = EV_CURRENT;
- EHdr->e_ident[EI_OSABI] = FirstObj.getOSABI();
+ EHdr->e_ident[EI_OSABI] = Config->OSABI;
EHdr->e_type = getELFType();
- EHdr->e_machine = FirstObj.EMachine;
+ EHdr->e_machine = Config->EMachine;
EHdr->e_version = EV_CURRENT;
- EHdr->e_entry = getEntryAddr<ELFT>();
+ EHdr->e_entry = getEntryAddr();
EHdr->e_shoff = SectionHeaderOff;
EHdr->e_ehsize = sizeof(Elf_Ehdr);
EHdr->e_phnum = Phdrs.size();
EHdr->e_shentsize = sizeof(Elf_Shdr);
EHdr->e_shnum = OutputSections.size() + 1;
- EHdr->e_shstrndx = Out<ELFT>::ShStrTab->SectionIndex;
+ EHdr->e_shstrndx = In<ELFT>::ShStrTab->OutSec->SectionIndex;
- if (Config->EMachine == EM_MIPS)
- EHdr->e_flags = getMipsEFlags(ELFT::Is64Bits);
+ if (Config->EMachine == EM_ARM)
+ // We don't currently use any features incompatible with EF_ARM_EABI_VER5,
+ // but we don't have any firm guarantees of conformance. Linux AArch64
+ // kernels (as of 2016) require an EABI version to be set.
+ EHdr->e_flags = EF_ARM_EABI_VER5;
+ else if (Config->EMachine == EM_MIPS)
+ EHdr->e_flags = getMipsEFlags<ELFT>();
if (!Config->Relocatable) {
EHdr->e_phoff = sizeof(Elf_Ehdr);
@@ -1221,63 +1591,131 @@ template <class ELFT> void Writer<ELFT>::writeHeader() {
// Write the program header table.
auto *HBuf = reinterpret_cast<Elf_Phdr *>(Buf + EHdr->e_phoff);
- for (Phdr &P : Phdrs)
- *HBuf++ = P.H;
+ for (PhdrEntry &P : Phdrs) {
+ HBuf->p_type = P.p_type;
+ HBuf->p_flags = P.p_flags;
+ HBuf->p_offset = P.p_offset;
+ HBuf->p_vaddr = P.p_vaddr;
+ HBuf->p_paddr = P.p_paddr;
+ HBuf->p_filesz = P.p_filesz;
+ HBuf->p_memsz = P.p_memsz;
+ HBuf->p_align = P.p_align;
+ ++HBuf;
+ }
// Write the section header table. Note that the first table entry is null.
auto *SHdrs = reinterpret_cast<Elf_Shdr *>(Buf + EHdr->e_shoff);
- for (OutputSectionBase<ELFT> *Sec : OutputSections)
- Sec->writeHeaderTo(++SHdrs);
+ for (OutputSectionBase *Sec : OutputSections)
+ Sec->writeHeaderTo<ELFT>(++SHdrs);
+}
+
+// Removes a given file asynchronously. This is a performance hack,
+// so remove this when operating systems are improved.
+//
+// On Linux (and probably on other Unix-like systems), unlink(2) is a
+// noticeably slow system call. As of 2016, unlink takes 250
+// milliseconds to remove a 1 GB file on ext4 filesystem on my machine.
+//
+// To create a new result file, we first remove existing file. So, if
+// you repeatedly link a 1 GB program in a regular compile-link-debug
+// cycle, every cycle wastes 250 milliseconds only to remove a file.
+// Since LLD can link a 1 GB binary in about 5 seconds, that waste
+// actually counts.
+//
+// This function spawns a background thread to call unlink.
+// The calling thread returns almost immediately.
+static void unlinkAsync(StringRef Path) {
+ if (!Config->Threads || !sys::fs::exists(Config->OutputFile))
+ return;
+
+ // First, rename Path to avoid race condition. We cannot remove
+ // Path from a different thread because we are now going to create
+ // Path as a new file. If we do that in a different thread, the new
+ // thread can remove the new file.
+ SmallString<128> TempPath;
+ if (auto EC = sys::fs::createUniqueFile(Path + "tmp%%%%%%%%", TempPath))
+ fatal(EC, "createUniqueFile failed");
+ if (auto EC = sys::fs::rename(Path, TempPath))
+ fatal(EC, "rename failed");
+
+ // Remove TempPath in background.
+ std::thread([=] { ::remove(TempPath.str().str().c_str()); }).detach();
}
+// Open a result file.
template <class ELFT> void Writer<ELFT>::openFile() {
+ unlinkAsync(Config->OutputFile);
ErrorOr<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
FileOutputBuffer::create(Config->OutputFile, FileSize,
FileOutputBuffer::F_executable);
+
if (auto EC = BufferOrErr.getError())
error(EC, "failed to open " + Config->OutputFile);
else
Buffer = std::move(*BufferOrErr);
}
+template <class ELFT> void Writer<ELFT>::writeSectionsBinary() {
+ uint8_t *Buf = Buffer->getBufferStart();
+ for (OutputSectionBase *Sec : OutputSections)
+ if (Sec->Flags & SHF_ALLOC)
+ Sec->writeTo(Buf + Sec->Offset);
+}
+
// Write section contents to a mmap'ed file.
template <class ELFT> void Writer<ELFT>::writeSections() {
uint8_t *Buf = Buffer->getBufferStart();
- // PPC64 needs to process relocations in the .opd section before processing
- // relocations in code-containing sections.
- if (OutputSectionBase<ELFT> *Sec = Out<ELFT>::Opd) {
- Out<ELFT>::OpdBuf = Buf + Sec->getFileOff();
- Sec->writeTo(Buf + Sec->getFileOff());
+ // PPC64 needs to process relocations in the .opd section
+ // before processing relocations in code-containing sections.
+ Out<ELFT>::Opd = findSection(".opd");
+ if (Out<ELFT>::Opd) {
+ Out<ELFT>::OpdBuf = Buf + Out<ELFT>::Opd->Offset;
+ Out<ELFT>::Opd->writeTo(Buf + Out<ELFT>::Opd->Offset);
}
- for (OutputSectionBase<ELFT> *Sec : OutputSections)
- if (Sec != Out<ELFT>::Opd)
- Sec->writeTo(Buf + Sec->getFileOff());
+ OutputSectionBase *EhFrameHdr =
+ In<ELFT>::EhFrameHdr ? In<ELFT>::EhFrameHdr->OutSec : nullptr;
+ for (OutputSectionBase *Sec : OutputSections)
+ if (Sec != Out<ELFT>::Opd && Sec != EhFrameHdr)
+ Sec->writeTo(Buf + Sec->Offset);
+
+ // The .eh_frame_hdr depends on .eh_frame section contents, therefore
+ // it should be written after .eh_frame is written.
+ if (!Out<ELFT>::EhFrame->empty() && EhFrameHdr)
+ EhFrameHdr->writeTo(Buf + EhFrameHdr->Offset);
}
template <class ELFT> void Writer<ELFT>::writeBuildId() {
- if (!Out<ELFT>::BuildId)
+ if (!In<ELFT>::BuildId || !In<ELFT>::BuildId->OutSec)
return;
- // Compute a hash of all sections except .debug_* sections.
- // We skip debug sections because they tend to be very large
- // and their contents are very likely to be the same as long as
- // other sections are the same.
+ // Compute a hash of all sections of the output file.
uint8_t *Start = Buffer->getBufferStart();
- uint8_t *Last = Start;
- std::vector<ArrayRef<uint8_t>> Regions;
- for (OutputSectionBase<ELFT> *Sec : OutputSections) {
- uint8_t *End = Start + Sec->getFileOff();
- if (!Sec->getName().startswith(".debug_"))
- Regions.push_back({Last, End});
- Last = End;
- }
- Regions.push_back({Last, Start + FileSize});
- Out<ELFT>::BuildId->writeBuildId(Regions);
+ uint8_t *End = Start + FileSize;
+ In<ELFT>::BuildId->writeBuildId({Start, End});
}
-template void elf::writeResult<ELF32LE>(SymbolTable<ELF32LE> *Symtab);
-template void elf::writeResult<ELF32BE>(SymbolTable<ELF32BE> *Symtab);
-template void elf::writeResult<ELF64LE>(SymbolTable<ELF64LE> *Symtab);
-template void elf::writeResult<ELF64BE>(SymbolTable<ELF64BE> *Symtab);
+template void elf::writeResult<ELF32LE>();
+template void elf::writeResult<ELF32BE>();
+template void elf::writeResult<ELF64LE>();
+template void elf::writeResult<ELF64BE>();
+
+template void elf::allocateHeaders<ELF32LE>(MutableArrayRef<PhdrEntry>,
+ ArrayRef<OutputSectionBase *>);
+template void elf::allocateHeaders<ELF32BE>(MutableArrayRef<PhdrEntry>,
+ ArrayRef<OutputSectionBase *>);
+template void elf::allocateHeaders<ELF64LE>(MutableArrayRef<PhdrEntry>,
+ ArrayRef<OutputSectionBase *>);
+template void elf::allocateHeaders<ELF64BE>(MutableArrayRef<PhdrEntry>,
+ ArrayRef<OutputSectionBase *>);
+
+template bool elf::isRelroSection<ELF32LE>(const OutputSectionBase *);
+template bool elf::isRelroSection<ELF32BE>(const OutputSectionBase *);
+template bool elf::isRelroSection<ELF64LE>(const OutputSectionBase *);
+template bool elf::isRelroSection<ELF64BE>(const OutputSectionBase *);
+
+template void elf::reportDiscarded<ELF32LE>(InputSectionBase<ELF32LE> *);
+template void elf::reportDiscarded<ELF32BE>(InputSectionBase<ELF32BE> *);
+template void elf::reportDiscarded<ELF64LE>(InputSectionBase<ELF64LE> *);
+template void elf::reportDiscarded<ELF64BE>(InputSectionBase<ELF64BE> *);
diff --git a/ELF/Writer.h b/ELF/Writer.h
index df25d8e404c2..718e3139a809 100644
--- a/ELF/Writer.h
+++ b/ELF/Writer.h
@@ -10,28 +10,56 @@
#ifndef LLD_ELF_WRITER_H
#define LLD_ELF_WRITER_H
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include <cstdint>
#include <memory>
-namespace llvm {
- class StringRef;
-}
-
namespace lld {
namespace elf {
+class InputFile;
+class OutputSectionBase;
template <class ELFT> class InputSectionBase;
template <class ELFT> class ObjectFile;
template <class ELFT> class SymbolTable;
+template <class ELFT> void writeResult();
+template <class ELFT> void markLive();
+template <class ELFT> bool isRelroSection(const OutputSectionBase *Sec);
-template <class ELFT> void writeResult(SymbolTable<ELFT> *Symtab);
+// This describes a program header entry.
+// Each contains type, access flags and range of output sections that will be
+// placed in it.
+struct PhdrEntry {
+ PhdrEntry(unsigned Type, unsigned Flags);
+ void add(OutputSectionBase *Sec);
-template <class ELFT> void markLive();
+ uint64_t p_paddr = 0;
+ uint64_t p_vaddr = 0;
+ uint64_t p_align = 0;
+ uint64_t p_memsz = 0;
+ uint64_t p_filesz = 0;
+ uint64_t p_offset = 0;
+ uint32_t p_type = 0;
+ uint32_t p_flags = 0;
-template <class ELFT>
-llvm::StringRef getOutputSectionName(InputSectionBase<ELFT> *S);
+ OutputSectionBase *First = nullptr;
+ OutputSectionBase *Last = nullptr;
+ bool HasLMA = false;
+};
+
+llvm::StringRef getOutputSectionName(llvm::StringRef Name);
template <class ELFT>
-void reportDiscarded(InputSectionBase<ELFT> *IS,
- const std::unique_ptr<elf::ObjectFile<ELFT>> &File);
+void allocateHeaders(llvm::MutableArrayRef<PhdrEntry>,
+ llvm::ArrayRef<OutputSectionBase *>);
+template <class ELFT> void reportDiscarded(InputSectionBase<ELFT> *IS);
+
+template <class ELFT> uint32_t getMipsEFlags();
+
+uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
+ llvm::StringRef FileName);
+
+bool isMipsN32Abi(const InputFile *F);
}
}
diff --git a/cmake/modules/AddLLD.cmake b/cmake/modules/AddLLD.cmake
new file mode 100644
index 000000000000..752ca7f6b43c
--- /dev/null
+++ b/cmake/modules/AddLLD.cmake
@@ -0,0 +1,45 @@
+macro(add_lld_library name)
+ add_llvm_library(${name} ${ARGN})
+ set_target_properties(${name} PROPERTIES FOLDER "lld libraries")
+endmacro(add_lld_library)
+
+macro(add_lld_executable name)
+ add_llvm_executable(${name} ${ARGN})
+ set_target_properties(${name} PROPERTIES FOLDER "lld executables")
+endmacro(add_lld_executable)
+
+macro(add_lld_tool name)
+ if (NOT LLD_BUILD_TOOLS)
+ set(EXCLUDE_FROM_ALL ON)
+ endif()
+
+ add_lld_executable(${name} ${ARGN})
+
+ if (LLD_BUILD_TOOLS)
+ if(${name} IN_LIST LLVM_DISTRIBUTION_COMPONENTS OR
+ NOT LLVM_DISTRIBUTION_COMPONENTS)
+ set(export_to_lldtargets EXPORT lldTargets)
+ set_property(GLOBAL PROPERTY LLD_HAS_EXPORTS True)
+ endif()
+
+ install(TARGETS ${name}
+ ${export_to_lldtargets}
+ RUNTIME DESTINATION bin
+ COMPONENT ${name})
+
+ if(NOT CMAKE_CONFIGURATION_TYPES)
+ add_custom_target(install-${name}
+ DEPENDS ${name}
+ COMMAND "${CMAKE_COMMAND}"
+ -DCMAKE_INSTALL_COMPONENT=${name}
+ -P "${CMAKE_BINARY_DIR}/cmake_install.cmake")
+ endif()
+ set_property(GLOBAL APPEND PROPERTY LLD_EXPORTS ${name})
+ endif()
+endmacro()
+
+macro(add_lld_symlink name dest)
+ add_llvm_tool_symlink(${name} ${dest} ALWAYS_GENERATE)
+ # Always generate install targets
+ llvm_install_symlink(${name} ${dest} ALWAYS_GENERATE)
+endmacro()
diff --git a/docs/Driver.rst b/docs/Driver.rst
index 5f2d946d36bd..f1105ffb32ce 100644
--- a/docs/Driver.rst
+++ b/docs/Driver.rst
@@ -68,7 +68,7 @@ Adding a Flavor
#. Add an entry in :file:`lib/Driver/UniversalDriver.cpp` to
:cpp:func:`lld::Driver::strToFlavor` and
:cpp:func:`lld::UniversalDriver::link`.
- This allows the flavor to be selected via symlink and :option:`-flavor`.
+ This allows the flavor to be selected via symlink and `-flavor`.
#. Add a tablegen file called :file:`lib/Driver/{flavor}Options.td` that
describes the options. If the options are a superset of another driver, that
diff --git a/docs/NewLLD.rst b/docs/NewLLD.rst
index 1f9bf87f10e3..cffea7c02dea 100644
--- a/docs/NewLLD.rst
+++ b/docs/NewLLD.rst
@@ -18,9 +18,9 @@ We expect that FreeBSD is going to be the first large system
to adopt LLD as the system linker.
We are working on it in collaboration with the FreeBSD project.
-The linkers are notably small; as of June 2016,
-the COFF linker is under 7k lines and the ELF linker is about 13k lines,
-while gold is 146K lines.
+The linkers are notably small; as of October 2016,
+the COFF linker is about 7k lines and the ELF linker is about 18k lines,
+while gold is 165K lines.
The linkers are designed to be as fast and simple as possible.
Because it is simple, it is easy to extend to support new features.
diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst
index 5344fc4d2dad..8bd73db363d7 100644
--- a/docs/ReleaseNotes.rst
+++ b/docs/ReleaseNotes.rst
@@ -1,102 +1,35 @@
-======================
-LLD 3.9 Release Notes
-======================
+=======================
+LLD 4.0.0 Release Notes
+=======================
.. contents::
:local:
+.. warning::
+ These are in-progress notes for the upcoming LLVM 4.0.0 release.
+
Introduction
============
-This document contains the release notes for the LLD linker, release 3.9.
+This document contains the release notes for the LLD linker, release 4.0.0.
Here we describe the status of LLD, including major improvements
from the previous release. All LLD releases may be downloaded
from the `LLVM releases web site <http://llvm.org/releases/>`_.
-What's new in ELF Support?
-==========================
-
-LLD 3.9 is a major milestone for us. It is the first release that can
-link real-world large userland programs, including LLVM/Clang/LLD
-themselves. In fact, for example, it can now be used to produce most
-userland programs distributed as part of FreeBSD.
-
-Many contributors have joined to the project to develop new features,
-port it to new architectures and fix issues since the last release.
-
-Link-Time Optimization
-----------------------
-
-Initial support for LTO has been added. It is compatible with
-`the LLVM gold plugin <http://llvm.org/docs/GoldPlugin.html>`_ in terms of
-command line flags and input file format so that LLD is usable as a
-drop-in replacement for GNU gold. LTO is implemented as a native
-feature unlike the GNU gold's plugin mechanism.
-
-Identical Code Folding
-----------------------
-
-LLD 3.9 can now merge identical code sections to produce smaller
-output files. It is expected to be used with ``-ffunction-sections``.
-
-Symbol Versioning
------------------
-
-LLD 3.9 is able to link against versioned symbols as well as produce
-versioned symbols. Both the original Sun's symbol versioning scheme
-and the GNU extension are supported.
-
-New Targets
------------
+Non-comprehensive list of changes in this release
+=================================================
-LLD has expanded support for new targets, including ARM/Thumb, the x32
-ABI and MIPS N64 ABI, in addition to the existing support for x86,
-x86-64, MIPS, PowerPC and PPC64.
-
-TLS Relocation Optimizations
-----------------------------
-
-The ELF ABI specification of the thread-local variable define a few
-peephole optimizations linkers can do by rewriting instructions at the
-link-time to reduce run-time overhead to access TLS variables. That
-feature has been implemented.
-
-New Linker Flags
+ELF Improvements
----------------
-Many command line options have been added in this release, including:
-
-- Symbol resolution and output options: ``-Bsymbolic-functions``,
- ``-export-dynamic-symbol``, ``-image-base``, ``-pie``, ``-end-lib``,
- ``-start-lib``, ``-build-id={md5,sha1,none,0x<hexstring>}``.
-
-- Symbol versioning option: ``-dynamic-list``.
-
-- LTO options: ``-lto-O``, ``-lto-aa-pipeline``, ``-lto-jobs``,
- ``-lto-newpm-passes``, ``-plugin``, ``-plugin-eq``, ``-plugin-opt``,
- ``-plugin-opt-eq``, ``-disable-verify``, ``-mllvm``.
+* Initial support for LTO.
-- Driver optionss: ``-help``, ``-version``, ``-unresolved-symbols``.
-
-- Debug options: ``-demangle``, ``-reproduce``, ``-save-temps``,
- ``-strip-debug``, ``-trace``, ``-trace-symbol``,
- ``-warn-execstack``.
-
-- Exception handling option: ``-eh-frame-hdr``.
-
-- Identical Code Folding option: ``-icf``.
-
-Changes to the MIPS Target
---------------------------
+COFF Improvements
+-----------------
-* Added support for MIPS N64 ABI.
-* Added support for TLS relocations for both O32 and N64 MIPS ABIs.
+* Item 1.
-Building LLVM Toolchain with LLD
---------------------------------
+MachO Improvements
+------------------
-A new CMake variable, ``LLVM_ENABLE_LLD``, has been added to use LLD
-to build the LLVM toolchain. If the varaible is true, ``-fuse-ld=lld``
-option will be added to linker flags so that ``ld.lld`` is used
-instead of default ``ld``. Because ``-fuse-ld=lld`` is a new compiler
-driver option, you need Clang 3.8 or newer to use the feature.
+* Item 1.
diff --git a/docs/conf.py b/docs/conf.py
index 88fcdd8afcae..6faed2fc9801 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -48,9 +48,9 @@ copyright = u'2011-%d, LLVM Project' % date.today().year
# built documents.
#
# The short X.Y version.
-version = '3.2'
+version = '4.0'
# The full version, including alpha/beta/rc tags.
-release = '3.2'
+release = '4.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/windows_support.rst b/docs/windows_support.rst
index c4c338a0fba1..2d66109d2444 100644
--- a/docs/windows_support.rst
+++ b/docs/windows_support.rst
@@ -81,7 +81,7 @@ Using Visual Studio IDE/MSBuild
Alternatively, you can use msbuild if you don't like to work in an IDE::
msbuild LLVM.sln /m /target:"lld executables\lld"
-
+
MSBuild.exe had been shipped as a component of the .NET framework, but since
2013 it's part of Visual Studio. You can find it at "C:\\Program Files
(x86)\\msbuild".
diff --git a/include/lld/Config/Version.h b/include/lld/Config/Version.h
index 41433c1175ef..1cec3cc7678c 100644
--- a/include/lld/Config/Version.h
+++ b/include/lld/Config/Version.h
@@ -6,11 +6,9 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
-///
-/// \file
-/// \brief Defines version macros and version-related utility functions
-/// for lld.
-///
+//
+// Defines a version-related utility function.
+//
//===----------------------------------------------------------------------===//
#ifndef LLD_VERSION_H
@@ -18,34 +16,10 @@
#include "lld/Config/Version.inc"
#include "llvm/ADT/StringRef.h"
-#include <string>
-
-/// \brief Helper macro for LLD_VERSION_STRING.
-#define LLD_MAKE_VERSION_STRING2(X) #X
-
-/// \brief Helper macro for LLD_VERSION_STRING.
-#define LLD_MAKE_VERSION_STRING(X, Y) LLD_MAKE_VERSION_STRING2(X.Y)
-
-/// \brief A string that describes the lld version number, e.g., "1.0".
-#define LLD_VERSION_STRING \
- LLD_MAKE_VERSION_STRING(LLD_VERSION_MAJOR, LLD_VERSION_MINOR)
namespace lld {
-/// \brief Retrieves the repository path (e.g., Subversion path) that
-/// identifies the particular lld branch, tag, or trunk from which this
-/// lld was built.
-llvm::StringRef getLLDRepositoryPath();
-
-/// \brief Retrieves the repository revision number (or identifer) from which
-/// this lld was built.
-llvm::StringRef getLLDRevision();
-
-/// \brief Retrieves the full repository version that is an amalgamation of
-/// the information in getLLDRepositoryPath() and getLLDRevision().
-std::string getLLDRepositoryVersion();
-
/// \brief Retrieves a string representing the complete lld version.
-llvm::StringRef getLLDVersion();
+std::string getLLDVersion();
}
#endif // LLD_VERSION_H
diff --git a/include/lld/Config/Version.inc.in b/include/lld/Config/Version.inc.in
index c893a56686c0..2789a5c46089 100644
--- a/include/lld/Config/Version.inc.in
+++ b/include/lld/Config/Version.inc.in
@@ -1,4 +1,5 @@
#define LLD_VERSION @LLD_VERSION@
+#define LLD_VERSION_STRING "@LLD_VERSION@"
#define LLD_VERSION_MAJOR @LLD_VERSION_MAJOR@
#define LLD_VERSION_MINOR @LLD_VERSION_MINOR@
#define LLD_REVISION_STRING "@LLD_REVISION@"
diff --git a/include/lld/Core/Atom.h b/include/lld/Core/Atom.h
index 42ca2bb8af8c..156a5d4a736f 100644
--- a/include/lld/Core/Atom.h
+++ b/include/lld/Core/Atom.h
@@ -1,4 +1,4 @@
-//===- Core/Atom.h - A node in linking graph ------------------------------===//
+//===- Core/Atom.h - A node in linking graph --------------------*- C++ -*-===//
//
// The LLVM Linker
//
@@ -11,6 +11,7 @@
#define LLD_CORE_ATOM_H
#include "lld/Core/LLVM.h"
+#include "llvm/ADT/StringRef.h"
namespace lld {
@@ -28,6 +29,7 @@ class OwningAtomPtr;
///
class Atom {
template<typename T> friend class OwningAtomPtr;
+
public:
/// Whether this atom is defined or a proxy for an undefined symbol
enum Definition {
@@ -47,7 +49,6 @@ public:
/// loader (e.g. visibility=default).
};
-
/// file - returns the File that produced/owns this Atom
virtual const File& file() const = 0;
@@ -69,7 +70,7 @@ protected:
/// object. Therefore, no one but the owning File object should call
/// delete on an Atom. In fact, some File objects may bulk allocate
/// an array of Atoms, so they cannot be individually deleted by anyone.
- virtual ~Atom() {}
+ virtual ~Atom() = default;
private:
Definition _definition;
@@ -81,9 +82,10 @@ template<typename T>
class OwningAtomPtr {
private:
OwningAtomPtr(const OwningAtomPtr &) = delete;
- void operator=(const OwningAtomPtr&) = delete;
+ void operator=(const OwningAtomPtr &) = delete;
+
public:
- OwningAtomPtr() : atom(nullptr) { }
+ OwningAtomPtr() = default;
OwningAtomPtr(T *atom) : atom(atom) { }
~OwningAtomPtr() {
@@ -121,9 +123,9 @@ public:
}
private:
- T *atom;
+ T *atom = nullptr;
};
-} // namespace lld
+} // end namespace lld
#endif // LLD_CORE_ATOM_H
diff --git a/include/lld/Core/DefinedAtom.h b/include/lld/Core/DefinedAtom.h
index e3193f8aaf2e..7f623d2ea5e6 100644
--- a/include/lld/Core/DefinedAtom.h
+++ b/include/lld/Core/DefinedAtom.h
@@ -354,10 +354,6 @@ public:
return atomContentType == typeCFI;
}
- // Returns true if lhs should be placed before rhs in the final output.
- static bool compareByPosition(const DefinedAtom *lhs,
- const DefinedAtom *rhs);
-
protected:
// DefinedAtom is an abstract base class. Only subclasses can access
// constructor.
diff --git a/include/lld/Core/LinkingContext.h b/include/lld/Core/LinkingContext.h
index 7e4edaf22cf3..b3a999b00fbd 100644
--- a/include/lld/Core/LinkingContext.h
+++ b/include/lld/Core/LinkingContext.h
@@ -1,4 +1,4 @@
-//===- lld/Core/LinkingContext.h - Linker Target Info Interface -----------===//
+//===- lld/Core/LinkingContext.h - Linker Target Info Interface -*- C++ -*-===//
//
// The LLVM Linker
//
@@ -10,17 +10,21 @@
#ifndef LLD_CORE_LINKING_CONTEXT_H
#define LLD_CORE_LINKING_CONTEXT_H
-#include "lld/Core/Error.h"
-#include "lld/Core/LLVM.h"
#include "lld/Core/Node.h"
-#include "lld/Core/Reference.h"
#include "lld/Core/Reader.h"
-#include "llvm/Support/ErrorOr.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <cstdint>
+#include <memory>
#include <string>
#include <vector>
namespace lld {
+
class PassManager;
class File;
class Writer;
@@ -117,12 +121,15 @@ public:
void setDeadStripping(bool enable) { _deadStrip = enable; }
void setGlobalsAreDeadStripRoots(bool v) { _globalsAreDeadStripRoots = v; }
+
void setPrintRemainingUndefines(bool print) {
_printRemainingUndefines = print;
}
+
void setAllowRemainingUndefines(bool allow) {
_allowRemainingUndefines = allow;
}
+
void setAllowShlibUndefines(bool allow) { _allowShlibUndefines = allow; }
void setLogInputFiles(bool log) { _logInputFiles = log; }
@@ -149,7 +156,7 @@ public:
/// during link. Flavors can override this function in their LinkingContext
/// to add more internal files. These internal files are positioned before
/// the actual input files.
- virtual void createInternalFiles(std::vector<std::unique_ptr<File> > &) const;
+ virtual void createInternalFiles(std::vector<std::unique_ptr<File>> &) const;
/// Return the list of undefined symbols that are specified in the
/// linker command line, using the -u option.
@@ -248,4 +255,4 @@ private:
} // end namespace lld
-#endif
+#endif // LLD_CORE_LINKING_CONTEXT_H
diff --git a/include/lld/Core/Node.h b/include/lld/Core/Node.h
index 8de0ecdbba6a..c30482409e7a 100644
--- a/include/lld/Core/Node.h
+++ b/include/lld/Core/Node.h
@@ -1,4 +1,4 @@
-//===- lld/Core/Node.h - Input file class ---------------------------------===//
+//===- lld/Core/Node.h - Input file class -----------------------*- C++ -*-===//
//
// The LLVM Linker
//
@@ -17,9 +17,8 @@
#define LLD_CORE_NODE_H
#include "lld/Core/File.h"
-#include "llvm/Option/ArgList.h"
+#include <algorithm>
#include <memory>
-#include <vector>
namespace lld {
@@ -29,8 +28,10 @@ namespace lld {
class Node {
public:
enum class Kind { File, GroupEnd };
+
explicit Node(Kind type) : _kind(type) {}
- virtual ~Node() {}
+ virtual ~Node() = default;
+
virtual Kind kind() const { return _kind; }
private:
@@ -69,6 +70,6 @@ protected:
std::unique_ptr<File> _file;
};
-} // namespace lld
+} // end namespace lld
#endif // LLD_CORE_NODE_H
diff --git a/include/lld/Core/Parallel.h b/include/lld/Core/Parallel.h
index 2dde97d9e3f0..f241453a4d39 100644
--- a/include/lld/Core/Parallel.h
+++ b/include/lld/Core/Parallel.h
@@ -121,7 +121,7 @@ public:
// Spawn all but one of the threads in another thread as spawning threads
// can take a while.
std::thread([&, threadCount] {
- for (std::size_t i = 1; i < threadCount; ++i) {
+ for (size_t i = 1; i < threadCount; ++i) {
std::thread([=] {
work();
}).detach();
@@ -270,26 +270,65 @@ template <class T> void parallel_sort(T *start, T *end) {
}
#if !defined(LLVM_ENABLE_THREADS) || LLVM_ENABLE_THREADS == 0
-template <class Iterator, class Func>
-void parallel_for_each(Iterator begin, Iterator end, Func func) {
- std::for_each(begin, end, func);
+template <class IterTy, class FuncTy>
+void parallel_for_each(IterTy Begin, IterTy End, FuncTy Fn) {
+ std::for_each(Begin, End, Fn);
+}
+
+template <class IndexTy, class FuncTy>
+void parallel_for(IndexTy Begin, IndexTy End, FuncTy Fn) {
+ for (IndexTy I = Begin; I != End; ++I)
+ Fn(I);
}
#elif defined(_MSC_VER)
// Use ppl parallel_for_each on Windows.
-template <class Iterator, class Func>
-void parallel_for_each(Iterator begin, Iterator end, Func func) {
- concurrency::parallel_for_each(begin, end, func);
+template <class IterTy, class FuncTy>
+void parallel_for_each(IterTy Begin, IterTy End, FuncTy Fn) {
+ concurrency::parallel_for_each(Begin, End, Fn);
+}
+
+template <class IndexTy, class FuncTy>
+void parallel_for(IndexTy Begin, IndexTy End, FuncTy Fn) {
+ concurrency::parallel_for(Begin, End, Fn);
}
#else
-template <class Iterator, class Func>
-void parallel_for_each(Iterator begin, Iterator end, Func func) {
- TaskGroup tg;
- ptrdiff_t taskSize = 1024;
- while (taskSize <= std::distance(begin, end)) {
- tg.spawn([=, &func] { std::for_each(begin, begin + taskSize, func); });
- begin += taskSize;
+template <class IterTy, class FuncTy>
+void parallel_for_each(IterTy Begin, IterTy End, FuncTy Fn) {
+ // TaskGroup has a relatively high overhead, so we want to reduce
+ // the number of spawn() calls. We'll create up to 1024 tasks here.
+ // (Note that 1024 is an arbitrary number. This code probably needs
+ // improving to take the number of available cores into account.)
+ ptrdiff_t TaskSize = std::distance(Begin, End) / 1024;
+ if (TaskSize == 0)
+ TaskSize = 1;
+
+ TaskGroup Tg;
+ while (TaskSize <= std::distance(Begin, End)) {
+ Tg.spawn([=, &Fn] { std::for_each(Begin, Begin + TaskSize, Fn); });
+ Begin += TaskSize;
}
- std::for_each(begin, end, func);
+ Tg.spawn([=, &Fn] { std::for_each(Begin, End, Fn); });
+}
+
+template <class IndexTy, class FuncTy>
+void parallel_for(IndexTy Begin, IndexTy End, FuncTy Fn) {
+ ptrdiff_t TaskSize = (End - Begin) / 1024;
+ if (TaskSize == 0)
+ TaskSize = 1;
+
+ TaskGroup Tg;
+ IndexTy I = Begin;
+ for (; I < End; I += TaskSize) {
+ Tg.spawn([=, &Fn] {
+ for (IndexTy J = I, E = I + TaskSize; J != E; ++J)
+ Fn(J);
+ });
+ Begin += TaskSize;
+ }
+ Tg.spawn([=, &Fn] {
+ for (IndexTy J = I; J < End; ++J)
+ Fn(J);
+ });
}
#endif
} // end namespace lld
diff --git a/include/lld/Core/Pass.h b/include/lld/Core/Pass.h
index 0527f02cd362..bfe3f9b10e0c 100644
--- a/include/lld/Core/Pass.h
+++ b/include/lld/Core/Pass.h
@@ -1,4 +1,4 @@
-//===------ Core/Pass.h - Base class for linker passes --------------------===//
+//===------ Core/Pass.h - Base class for linker passes ----------*- C++ -*-===//
//
// The LLVM Linker
//
@@ -10,13 +10,10 @@
#ifndef LLD_CORE_PASS_H
#define LLD_CORE_PASS_H
-#include "lld/Core/Atom.h"
-#include "lld/Core/File.h"
-#include "lld/Core/Reference.h"
#include "llvm/Support/Error.h"
-#include <vector>
namespace lld {
+
class SimpleFile;
/// Once the core linking is done (which resolves references, coalesces atoms
@@ -31,16 +28,16 @@ class SimpleFile;
/// new Atoms to the graph using the File's addAtom() method.
class Pass {
public:
- virtual ~Pass() { }
+ virtual ~Pass() = default;
/// Do the actual work of the Pass.
virtual llvm::Error perform(SimpleFile &mergedFile) = 0;
protected:
// Only subclassess can be instantiated.
- Pass() { }
+ Pass() = default;
};
-} // namespace lld
+} // end namespace lld
#endif // LLD_CORE_PASS_H
diff --git a/include/lld/Core/PassManager.h b/include/lld/Core/PassManager.h
index 71a25cc7f3cd..09b417a2985d 100644
--- a/include/lld/Core/PassManager.h
+++ b/include/lld/Core/PassManager.h
@@ -36,7 +36,7 @@ public:
for (std::unique_ptr<Pass> &pass : _passes)
if (llvm::Error EC = pass->perform(file))
return EC;
- return llvm::Error();
+ return llvm::Error::success();
}
private:
diff --git a/include/lld/Core/Reader.h b/include/lld/Core/Reader.h
index 66df4380dc76..5105eb1aa2be 100644
--- a/include/lld/Core/Reader.h
+++ b/include/lld/Core/Reader.h
@@ -12,9 +12,10 @@
#include "lld/Core/LLVM.h"
#include "lld/Core/Reference.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/YAMLTraits.h"
-#include <functional>
+#include "llvm/Support/MemoryBuffer.h"
#include <memory>
#include <vector>
@@ -23,10 +24,11 @@ using llvm::sys::fs::file_magic;
namespace llvm {
namespace yaml {
class IO;
-}
-}
+} // end namespace yaml
+} // end namespace llvm
namespace lld {
+
class File;
class LinkingContext;
class MachOLinkingContext;
@@ -37,7 +39,7 @@ class MachOLinkingContext;
/// Each file format (e.g. mach-o, etc) has a concrete subclass of Reader.
class Reader {
public:
- virtual ~Reader() {}
+ virtual ~Reader() = default;
/// Sniffs the file to determine if this Reader can parse it.
/// The method is called with:
@@ -52,7 +54,6 @@ public:
loadFile(std::unique_ptr<MemoryBuffer> mb, const class Registry &) const = 0;
};
-
/// \brief An abstract class for handling alternate yaml representations
/// of object files.
///
@@ -74,7 +75,6 @@ public:
virtual bool handledDocTag(llvm::yaml::IO &io, const lld::File *&f) const = 0;
};
-
/// A registry to hold the list of currently registered Readers and
/// tables which map Reference kind values to strings.
/// The linker does not directly invoke Readers. Instead, it registers
@@ -127,7 +127,6 @@ public:
void addKindTable(Reference::KindNamespace ns, Reference::KindArch arch,
const KindStrings array[]);
-
private:
struct KindEntry {
Reference::KindNamespace ns;
@@ -154,4 +153,4 @@ private:
} // end namespace lld
-#endif
+#endif // LLD_CORE_READER_H
diff --git a/include/lld/Core/Reference.h b/include/lld/Core/Reference.h
index 86de4f6a4236..1d3003c84616 100644
--- a/include/lld/Core/Reference.h
+++ b/include/lld/Core/Reference.h
@@ -1,4 +1,4 @@
-//===- Core/References.h - A Reference to Another Atom --------------------===//
+//===- Core/References.h - A Reference to Another Atom ----------*- C++ -*-===//
//
// The LLVM Linker
//
@@ -10,10 +10,10 @@
#ifndef LLD_CORE_REFERENCES_H
#define LLD_CORE_REFERENCES_H
-#include "lld/Core/LLVM.h"
-#include "llvm/ADT/StringSwitch.h"
+#include <cstdint>
namespace lld {
+
class Atom;
///
@@ -107,13 +107,13 @@ protected:
/// object. Therefore, no one but the owning File object should call
/// delete on an Reference. In fact, some File objects may bulk allocate
/// an array of References, so they cannot be individually deleted by anyone.
- virtual ~Reference() {}
+ virtual ~Reference() = default;
KindValue _kindValue;
uint8_t _kindNamespace;
uint8_t _kindArch;
};
-} // namespace lld
+} // end namespace lld
#endif // LLD_CORE_REFERENCES_H
diff --git a/include/lld/Core/Reproduce.h b/include/lld/Core/Reproduce.h
new file mode 100644
index 000000000000..cf2747493834
--- /dev/null
+++ b/include/lld/Core/Reproduce.h
@@ -0,0 +1,73 @@
+//===- Reproduce.h - Utilities for creating reproducers ---------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_CORE_REPRODUCE_H
+#define LLD_CORE_REPRODUCE_H
+
+#include "lld/Core/LLVM.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+
+class raw_fd_ostream;
+
+namespace opt { class Arg; }
+
+}
+
+namespace lld {
+
+// This class creates a .cpio file for --reproduce (ELF) or /linkrepro (COFF).
+//
+// If "--reproduce foo" is given, we create a file "foo.cpio" and
+// copy all input files to the archive, along with a response file
+// to re-run the same command with the same inputs.
+// It is useful for reporting issues to LLD developers.
+//
+// Cpio as a file format is a deliberate choice. It's standardized in
+// POSIX and very easy to create. cpio command is available virtually
+// on all Unix systems. See
+// http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_07
+// for the format details.
+class CpioFile {
+public:
+ static ErrorOr<CpioFile *> create(StringRef OutputPath);
+ void append(StringRef Path, StringRef Data);
+
+private:
+ CpioFile(std::unique_ptr<llvm::raw_fd_ostream> OS, StringRef Basename);
+
+ std::unique_ptr<llvm::raw_fd_ostream> OS;
+ llvm::StringSet<> Seen;
+ std::string Basename;
+};
+
+// Makes a given pathname an absolute path first, and then remove
+// beginning /. For example, "../foo.o" is converted to "home/john/foo.o",
+// assuming that the current directory is "/home/john/bar".
+std::string relativeToRoot(StringRef Path);
+
+// Quote a given string if it contains a space character.
+std::string quote(StringRef S);
+
+// Rewrite the given path if a file exists with that pathname, otherwise
+// returns the original path.
+std::string rewritePath(StringRef S);
+
+// Returns the string form of the given argument.
+std::string stringize(llvm::opt::Arg *Arg);
+
+// Replaces backslashes with slashes if Windows.
+std::string convertToUnixPathSeparator(StringRef S);
+}
+
+#endif
diff --git a/include/lld/Core/Simple.h b/include/lld/Core/Simple.h
index f75b40327db4..3aa7abf5d12b 100644
--- a/include/lld/Core/Simple.h
+++ b/include/lld/Core/Simple.h
@@ -25,6 +25,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/ilist.h"
+#include "llvm/ADT/ilist_node.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
@@ -111,19 +112,17 @@ private:
AtomVector<AbsoluteAtom> _absolute;
};
-class SimpleReference : public Reference {
+class SimpleReference : public Reference,
+ public llvm::ilist_node<SimpleReference> {
public:
SimpleReference(Reference::KindNamespace ns, Reference::KindArch arch,
Reference::KindValue value, uint64_t off, const Atom *t,
Reference::Addend a)
- : Reference(ns, arch, value), _target(t), _offsetInAtom(off), _addend(a),
- _next(nullptr), _prev(nullptr) {
+ : Reference(ns, arch, value), _target(t), _offsetInAtom(off), _addend(a) {
}
SimpleReference()
: Reference(Reference::KindNamespace::all, Reference::KindArch::all, 0),
- _target(nullptr), _offsetInAtom(0), _addend(0), _next(nullptr),
- _prev(nullptr) {
- }
+ _target(nullptr), _offsetInAtom(0), _addend(0) {}
uint64_t offsetInAtom() const override { return _offsetInAtom; }
@@ -135,72 +134,17 @@ public:
Addend addend() const override { return _addend; }
void setAddend(Addend a) override { _addend = a; }
void setTarget(const Atom *newAtom) override { _target = newAtom; }
- SimpleReference *getNext() const { return _next; }
- SimpleReference *getPrev() const { return _prev; }
- void setNext(SimpleReference *n) { _next = n; }
- void setPrev(SimpleReference *p) { _prev = p; }
private:
const Atom *_target;
uint64_t _offsetInAtom;
Addend _addend;
- SimpleReference *_next;
- SimpleReference *_prev;
-};
-
-} // end namespace lld
-
-// ilist will lazily create a sentinal (so end() can return a node past the
-// end of the list). We need this trait so that the sentinal is allocated
-// via the BumpPtrAllocator.
-namespace llvm {
-
-template<>
-struct ilist_sentinel_traits<lld::SimpleReference> {
-
- ilist_sentinel_traits() : _allocator(nullptr) { }
-
- void setAllocator(llvm::BumpPtrAllocator *alloc) {
- _allocator = alloc;
- }
-
- lld::SimpleReference *createSentinel() const {
- return new (*_allocator) lld::SimpleReference();
- }
-
- static void destroySentinel(lld::SimpleReference*) {}
-
- static lld::SimpleReference *provideInitialHead() { return nullptr; }
-
- lld::SimpleReference *ensureHead(lld::SimpleReference *&head) const {
- if (!head) {
- head = createSentinel();
- noteHead(head, head);
- ilist_traits<lld::SimpleReference>::setNext(head, nullptr);
- return head;
- }
- return ilist_traits<lld::SimpleReference>::getPrev(head);
- }
-
- void noteHead(lld::SimpleReference *newHead,
- lld::SimpleReference *sentinel) const {
- ilist_traits<lld::SimpleReference>::setPrev(newHead, sentinel);
- }
-
-private:
- mutable llvm::BumpPtrAllocator *_allocator;
};
-} // end namespace llvm
-
-namespace lld {
-
class SimpleDefinedAtom : public DefinedAtom {
public:
explicit SimpleDefinedAtom(const File &f)
- : _file(f), _ordinal(f.getNextAtomOrdinalAndIncrement()) {
- _references.setAllocator(&f.allocator());
- }
+ : _file(f), _ordinal(f.getNextAtomOrdinalAndIncrement()) {}
~SimpleDefinedAtom() override {
_references.clearAndLeakNodesUnsafely();
@@ -232,23 +176,26 @@ public:
}
DefinedAtom::reference_iterator begin() const override {
- const void *it = reinterpret_cast<const void *>(&*_references.begin());
+ const void *it =
+ reinterpret_cast<const void *>(_references.begin().getNodePtr());
return reference_iterator(*this, it);
}
DefinedAtom::reference_iterator end() const override {
- const void *it = reinterpret_cast<const void *>(&*_references.end());
+ const void *it =
+ reinterpret_cast<const void *>(_references.end().getNodePtr());
return reference_iterator(*this, it);
}
const Reference *derefIterator(const void *it) const override {
- return reinterpret_cast<const Reference*>(it);
+ return &*RefList::const_iterator(
+ *reinterpret_cast<const llvm::ilist_node<SimpleReference> *>(it));
}
void incrementIterator(const void *&it) const override {
- const SimpleReference* node = reinterpret_cast<const SimpleReference*>(it);
- const SimpleReference* next = node->getNext();
- it = reinterpret_cast<const void*>(next);
+ RefList::const_iterator ref(
+ *reinterpret_cast<const llvm::ilist_node<SimpleReference> *>(it));
+ it = reinterpret_cast<const void *>(std::next(ref).getNodePtr());
}
void addReference(Reference::KindNamespace ns,
diff --git a/include/lld/Core/SymbolTable.h b/include/lld/Core/SymbolTable.h
index db610ad14066..ba4951e5bd13 100644
--- a/include/lld/Core/SymbolTable.h
+++ b/include/lld/Core/SymbolTable.h
@@ -46,22 +46,12 @@ public:
/// @brief add atom to symbol table
bool add(const AbsoluteAtom &);
- /// @brief checks if name is in symbol table and if so atom is not
- /// UndefinedAtom
- bool isDefined(StringRef sym);
-
/// @brief returns atom in symbol table for specified name (or nullptr)
const Atom *findByName(StringRef sym);
/// @brief returns vector of remaining UndefinedAtoms
std::vector<const UndefinedAtom *> undefines();
- /// returns vector of tentative definitions
- std::vector<StringRef> tentativeDefinitions();
-
- /// @brief add atom to replacement table
- void addReplacement(const Atom *replaced, const Atom *replacement);
-
/// @brief if atom has been coalesced away, return replacement, else return atom
const Atom *replacement(const Atom *);
diff --git a/include/lld/Driver/Driver.h b/include/lld/Driver/Driver.h
index 312f4f812b77..a3265c85716a 100644
--- a/include/lld/Driver/Driver.h
+++ b/include/lld/Driver/Driver.h
@@ -19,7 +19,7 @@ bool link(llvm::ArrayRef<const char *> Args);
}
namespace elf {
-bool link(llvm::ArrayRef<const char *> Args,
+bool link(llvm::ArrayRef<const char *> Args, bool CanExitEarly,
llvm::raw_ostream &Diag = llvm::errs());
}
diff --git a/include/lld/ReaderWriter/MachOLinkingContext.h b/include/lld/ReaderWriter/MachOLinkingContext.h
index 7b673f0dad3e..a9e80f50b23d 100644
--- a/include/lld/ReaderWriter/MachOLinkingContext.h
+++ b/include/lld/ReaderWriter/MachOLinkingContext.h
@@ -377,6 +377,10 @@ public:
uint32_t dylibCompatVersion(StringRef installName) const;
+ ArrayRef<mach_o::MachODylibFile*> allDylibs() const {
+ return _allDylibs;
+ }
+
/// Creates a copy (owned by this MachOLinkingContext) of a string.
StringRef copy(StringRef str) { return str.copy(_allocator); }
@@ -485,7 +489,7 @@ private:
mutable std::unique_ptr<Writer> _writer;
std::vector<SectionAlign> _sectAligns;
mutable llvm::StringMap<mach_o::MachODylibFile*> _pathToDylibMap;
- mutable std::set<mach_o::MachODylibFile*> _allDylibs;
+ mutable std::vector<mach_o::MachODylibFile*> _allDylibs;
mutable std::set<mach_o::MachODylibFile*> _upwardDylibs;
mutable std::vector<std::unique_ptr<File>> _indirectDylibs;
mutable std::mutex _dylibsMutex;
diff --git a/include/lld/Support/Memory.h b/include/lld/Support/Memory.h
new file mode 100644
index 000000000000..46db4a39f696
--- /dev/null
+++ b/include/lld/Support/Memory.h
@@ -0,0 +1,63 @@
+//===- Memory.h -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines arena allocators.
+//
+// Almost all large objects, such as files, sections or symbols, are
+// used for the entire lifetime of the linker once they are created.
+// This usage characteristic makes arena allocator an attractive choice
+// where the entire linker is one arena. With an arena, newly created
+// objects belong to the arena and freed all at once when everything is done.
+// Arena allocators are efficient and easy to understand.
+// Most objects are allocated using the arena allocators defined by this file.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MEMORY_H
+#define LLD_MEMORY_H
+
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/StringSaver.h"
+#include <vector>
+
+namespace lld {
+
+// Use this arena if your object doesn't have a destructor.
+extern llvm::BumpPtrAllocator BAlloc;
+extern llvm::StringSaver Saver;
+
+// These two classes are hack to keep track of all
+// SpecificBumpPtrAllocator instances.
+struct SpecificAllocBase {
+ SpecificAllocBase() { Instances.push_back(this); }
+ virtual ~SpecificAllocBase() = default;
+ virtual void reset() = 0;
+ static std::vector<SpecificAllocBase *> Instances;
+};
+
+template <class T> struct SpecificAlloc : public SpecificAllocBase {
+ void reset() override { Alloc.DestroyAll(); }
+ llvm::SpecificBumpPtrAllocator<T> Alloc;
+};
+
+// Use this arena if your object has a destructor.
+// Your destructor will be invoked from freeArena().
+template <typename T, typename... U> inline T *make(U &&... Args) {
+ static SpecificAlloc<T> Alloc;
+ return new (Alloc.Alloc.Allocate()) T(std::forward<U>(Args)...);
+}
+
+inline void freeArena() {
+ for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances)
+ Alloc->reset();
+ BAlloc.Reset();
+}
+}
+
+#endif
diff --git a/lib/Config/Version.cpp b/lib/Config/Version.cpp
index 60687b9d8940..25544756f8be 100644
--- a/lib/Config/Version.cpp
+++ b/lib/Config/Version.cpp
@@ -12,46 +12,32 @@
//===----------------------------------------------------------------------===//
#include "lld/Config/Version.h"
-#include "llvm/Support/raw_ostream.h"
using namespace llvm;
-namespace lld {
-
-StringRef getLLDRepositoryPath() {
-#ifdef LLD_REPOSITORY_STRING
- return LLD_REPOSITORY_STRING;
-#else
- return "";
-#endif
+// Returns an SVN repository path, which is usually "trunk".
+static std::string getRepositoryPath() {
+ StringRef S = LLD_REPOSITORY_STRING;
+ size_t Pos = S.find("lld/");
+ if (Pos != StringRef::npos)
+ return S.substr(Pos + 4);
+ return S;
}
-StringRef getLLDRevision() {
-#ifdef LLD_REVISION_STRING
- return LLD_REVISION_STRING;
-#else
- return "";
-#endif
-}
+// Returns an SVN repository name, e.g., " (trunk 284614)"
+// or an empty string if no repository info is available.
+static std::string getRepository() {
+ std::string Repo = getRepositoryPath();
+ std::string Rev = LLD_REVISION_STRING;
-std::string getLLDRepositoryVersion() {
- std::string S = getLLDRepositoryPath();
- std::string T = getLLDRevision();
- if (S.empty() && T.empty())
+ if (Repo.empty() && Rev.empty())
return "";
- if (!S.empty() && !T.empty())
- return "(" + S + " " + T + ")";
- if (!S.empty())
- return "(" + S + ")";
- return "(" + T + ")";
+ if (!Repo.empty() && !Rev.empty())
+ return " (" + Repo + " " + Rev + ")";
+ return " (" + Repo + Rev + ")";
}
-StringRef getLLDVersion() {
-#ifdef LLD_VERSION_STRING
- return LLD_VERSION_STRING;
-#else
- return "";
-#endif
+// Returns a version string, e.g., "LLD 4.0 (lld/trunk 284614)".
+std::string lld::getLLDVersion() {
+ return "LLD " + std::string(LLD_VERSION_STRING) + getRepository();
}
-
-} // end namespace lld
diff --git a/lib/Core/CMakeLists.txt b/lib/Core/CMakeLists.txt
index 41e0e7661b9c..d89ca4a63d72 100644
--- a/lib/Core/CMakeLists.txt
+++ b/lib/Core/CMakeLists.txt
@@ -4,6 +4,7 @@ add_lld_library(lldCore
File.cpp
LinkingContext.cpp
Reader.cpp
+ Reproduce.cpp
Resolver.cpp
SymbolTable.cpp
Writer.cpp
diff --git a/lib/Core/DefinedAtom.cpp b/lib/Core/DefinedAtom.cpp
index 8dc4d4a16f96..177cae7fcbf0 100644
--- a/lib/Core/DefinedAtom.cpp
+++ b/lib/Core/DefinedAtom.cpp
@@ -79,16 +79,4 @@ DefinedAtom::ContentPermissions DefinedAtom::permissions(ContentType type) {
llvm_unreachable("unknown content type");
}
-bool DefinedAtom::compareByPosition(const DefinedAtom *lhs,
- const DefinedAtom *rhs) {
- if (lhs == rhs)
- return false;
- const File *lhsFile = &lhs->file();
- const File *rhsFile = &rhs->file();
- if (lhsFile->ordinal() != rhsFile->ordinal())
- return lhsFile->ordinal() < rhsFile->ordinal();
- assert(lhs->ordinal() != rhs->ordinal());
- return lhs->ordinal() < rhs->ordinal();
-}
-
} // namespace
diff --git a/lib/Core/Error.cpp b/lib/Core/Error.cpp
index 4df1ce120bd9..6fc76f7ca3d0 100644
--- a/lib/Core/Error.cpp
+++ b/lib/Core/Error.cpp
@@ -16,9 +16,10 @@
using namespace lld;
+namespace {
class _YamlReaderErrorCategory : public std::error_category {
public:
- const char* name() const LLVM_NOEXCEPT override {
+ const char* name() const noexcept override {
return "lld.yaml.reader";
}
@@ -33,6 +34,7 @@ public:
"message defined.");
}
};
+} // end anonymous namespace
const std::error_category &lld::YamlReaderCategory() {
static _YamlReaderErrorCategory o;
@@ -48,7 +50,7 @@ class dynamic_error_category : public std::error_category {
public:
~dynamic_error_category() override = default;
- const char *name() const LLVM_NOEXCEPT override {
+ const char *name() const noexcept override {
return "lld.dynamic_error";
}
diff --git a/lib/Core/File.cpp b/lib/Core/File.cpp
index b84132bfecd5..30ded091a92a 100644
--- a/lib/Core/File.cpp
+++ b/lib/Core/File.cpp
@@ -8,12 +8,11 @@
//===----------------------------------------------------------------------===//
#include "lld/Core/File.h"
-#include "lld/Core/LLVM.h"
#include <mutex>
namespace lld {
-File::~File() { }
+File::~File() = default;
File::AtomVector<DefinedAtom> File::_noDefinedAtoms;
File::AtomVector<UndefinedAtom> File::_noUndefinedAtoms;
@@ -27,4 +26,4 @@ std::error_code File::parse() {
return _lastError.getValue();
}
-} // namespace lld
+} // end namespace lld
diff --git a/lib/Core/LinkingContext.cpp b/lib/Core/LinkingContext.cpp
index 2732543d306e..5de863aa7f37 100644
--- a/lib/Core/LinkingContext.cpp
+++ b/lib/Core/LinkingContext.cpp
@@ -8,16 +8,17 @@
//===----------------------------------------------------------------------===//
#include "lld/Core/LinkingContext.h"
-#include "lld/Core/Resolver.h"
+#include "lld/Core/File.h"
+#include "lld/Core/Node.h"
#include "lld/Core/Simple.h"
#include "lld/Core/Writer.h"
-#include "llvm/ADT/Triple.h"
+#include <algorithm>
namespace lld {
-LinkingContext::LinkingContext() {}
+LinkingContext::LinkingContext() = default;
-LinkingContext::~LinkingContext() {}
+LinkingContext::~LinkingContext() = default;
bool LinkingContext::validate(raw_ostream &diagnostics) {
return validateImpl(diagnostics);
@@ -59,7 +60,7 @@ LinkingContext::createUndefinedSymbolFile(StringRef filename) const {
}
void LinkingContext::createInternalFiles(
- std::vector<std::unique_ptr<File> > &result) const {
+ std::vector<std::unique_ptr<File>> &result) const {
if (std::unique_ptr<File> file = createEntrySymbolFile())
result.push_back(std::move(file));
if (std::unique_ptr<File> file = createUndefinedSymbolFile())
diff --git a/lib/Core/Reader.cpp b/lib/Core/Reader.cpp
index 107db07891da..24652abec688 100644
--- a/lib/Core/Reader.cpp
+++ b/lib/Core/Reader.cpp
@@ -7,18 +7,19 @@
//
//===----------------------------------------------------------------------===//
-#include "lld/Core/File.h"
#include "lld/Core/Reader.h"
+#include "lld/Core/File.h"
+#include "lld/Core/Reference.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Errc.h"
-#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
+#include <algorithm>
#include <memory>
-#include <system_error>
namespace lld {
-YamlIOTaggedDocumentHandler::~YamlIOTaggedDocumentHandler() {}
+YamlIOTaggedDocumentHandler::~YamlIOTaggedDocumentHandler() = default;
void Registry::add(std::unique_ptr<Reader> reader) {
_readers.push_back(std::move(reader));
@@ -63,7 +64,6 @@ bool Registry::handleTaggedDoc(llvm::yaml::IO &io,
return false;
}
-
void Registry::addKindTable(Reference::KindNamespace ns,
Reference::KindArch arch,
const KindStrings array[]) {
diff --git a/lib/Core/Reproduce.cpp b/lib/Core/Reproduce.cpp
new file mode 100644
index 000000000000..39b0e41c44e5
--- /dev/null
+++ b/lib/Core/Reproduce.cpp
@@ -0,0 +1,128 @@
+//===- Reproduce.cpp - Utilities for creating reproducers -----------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Core/Reproduce.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/Path.h"
+
+using namespace lld;
+using namespace llvm;
+using namespace sys;
+
+CpioFile::CpioFile(std::unique_ptr<raw_fd_ostream> OS, StringRef S)
+ : OS(std::move(OS)), Basename(S) {}
+
+ErrorOr<CpioFile *> CpioFile::create(StringRef OutputPath) {
+ std::string Path = (OutputPath + ".cpio").str();
+ std::error_code EC;
+ auto OS = llvm::make_unique<raw_fd_ostream>(Path, EC, sys::fs::F_None);
+ if (EC)
+ return EC;
+ return new CpioFile(std::move(OS), path::filename(OutputPath));
+}
+
+static void writeMember(raw_fd_ostream &OS, StringRef Path, StringRef Data) {
+ // The c_dev/c_ino pair should be unique according to the spec,
+ // but no one seems to care.
+ OS << "070707"; // c_magic
+ OS << "000000"; // c_dev
+ OS << "000000"; // c_ino
+ OS << "100664"; // c_mode: C_ISREG | rw-rw-r--
+ OS << "000000"; // c_uid
+ OS << "000000"; // c_gid
+ OS << "000001"; // c_nlink
+ OS << "000000"; // c_rdev
+ OS << "00000000000"; // c_mtime
+ OS << format("%06o", Path.size() + 1); // c_namesize
+ OS << format("%011o", Data.size()); // c_filesize
+ OS << Path << '\0'; // c_name
+ OS << Data; // c_filedata
+}
+
+void CpioFile::append(StringRef Path, StringRef Data) {
+ if (!Seen.insert(Path).second)
+ return;
+
+ // Construct an in-archive filename so that /home/foo/bar is stored
+ // as baz/home/foo/bar where baz is the basename of the output file.
+ // (i.e. in that case we are creating baz.cpio.)
+ SmallString<128> Fullpath;
+ path::append(Fullpath, Basename, Path);
+
+ writeMember(*OS, convertToUnixPathSeparator(Fullpath), Data);
+
+ // Print the trailer and seek back.
+ // This way we have a valid archive if we crash.
+ uint64_t Pos = OS->tell();
+ writeMember(*OS, "TRAILER!!!", "");
+ OS->seek(Pos);
+}
+
+// Makes a given pathname an absolute path first, and then remove
+// beginning /. For example, "../foo.o" is converted to "home/john/foo.o",
+// assuming that the current directory is "/home/john/bar".
+// Returned string is a forward slash separated path even on Windows to avoid
+// a mess with backslash-as-escape and backslash-as-path-separator.
+std::string lld::relativeToRoot(StringRef Path) {
+ SmallString<128> Abs = Path;
+ if (sys::fs::make_absolute(Abs))
+ return Path;
+ path::remove_dots(Abs, /*remove_dot_dot=*/true);
+
+ // This is Windows specific. root_name() returns a drive letter
+ // (e.g. "c:") or a UNC name (//net). We want to keep it as part
+ // of the result.
+ SmallString<128> Res;
+ StringRef Root = path::root_name(Abs);
+ if (Root.endswith(":"))
+ Res = Root.drop_back();
+ else if (Root.startswith("//"))
+ Res = Root.substr(2);
+
+ path::append(Res, path::relative_path(Abs));
+ return convertToUnixPathSeparator(Res);
+}
+
+// Quote a given string if it contains a space character.
+std::string lld::quote(StringRef S) {
+ if (S.find(' ') == StringRef::npos)
+ return S;
+ return ("\"" + S + "\"").str();
+}
+
+std::string lld::rewritePath(StringRef S) {
+ if (fs::exists(S))
+ return relativeToRoot(S);
+ return S;
+}
+
+std::string lld::stringize(opt::Arg *Arg) {
+ std::string K = Arg->getSpelling();
+ if (Arg->getNumValues() == 0)
+ return K;
+ std::string V = quote(Arg->getValue());
+ if (Arg->getOption().getRenderStyle() == opt::Option::RenderJoinedStyle)
+ return K + V;
+ return K + " " + V;
+}
+
+std::string lld::convertToUnixPathSeparator(StringRef S) {
+#ifdef LLVM_ON_WIN32
+ std::string Ret = S.str();
+ std::replace(Ret.begin(), Ret.end(), '\\', '/');
+ return Ret;
+#else
+ return S;
+#endif
+}
diff --git a/lib/Core/Resolver.cpp b/lib/Core/Resolver.cpp
index ef694fd972fc..e7cfaaac7835 100644
--- a/lib/Core/Resolver.cpp
+++ b/lib/Core/Resolver.cpp
@@ -100,7 +100,7 @@ llvm::Error Resolver::handleSharedLibrary(File &file) {
if (auto ec = undefAddedOrError.takeError())
return ec;
- return llvm::Error();
+ return llvm::Error::success();
}
bool Resolver::doUndefinedAtom(OwningAtomPtr<UndefinedAtom> atom) {
diff --git a/lib/Core/SymbolTable.cpp b/lib/Core/SymbolTable.cpp
index 44631a5d40dc..cacea5f30847 100644
--- a/lib/Core/SymbolTable.cpp
+++ b/lib/Core/SymbolTable.cpp
@@ -223,13 +223,9 @@ bool SymbolTable::AtomMappingInfo::isEqual(const DefinedAtom * const l,
const DefinedAtom * const r) {
if (l == r)
return true;
- if (l == getEmptyKey())
+ if (l == getEmptyKey() || r == getEmptyKey())
return false;
- if (r == getEmptyKey())
- return false;
- if (l == getTombstoneKey())
- return false;
- if (r == getTombstoneKey())
+ if (l == getTombstoneKey() || r == getTombstoneKey())
return false;
if (l->contentType() != r->contentType())
return false;
@@ -265,17 +261,6 @@ const Atom *SymbolTable::findByName(StringRef sym) {
return pos->second;
}
-bool SymbolTable::isDefined(StringRef sym) {
- if (const Atom *atom = findByName(sym))
- return !isa<UndefinedAtom>(atom);
- return false;
-}
-
-void SymbolTable::addReplacement(const Atom *replaced,
- const Atom *replacement) {
- _replacedAtoms[replaced] = replacement;
-}
-
const Atom *SymbolTable::replacement(const Atom *atom) {
// Find the replacement for a given atom. Atoms in _replacedAtoms
// may be chained, so find the last one.
@@ -303,17 +288,4 @@ std::vector<const UndefinedAtom *> SymbolTable::undefines() {
return ret;
}
-std::vector<StringRef> SymbolTable::tentativeDefinitions() {
- std::vector<StringRef> ret;
- for (auto entry : _nameTable) {
- const Atom *atom = entry.second;
- StringRef name = entry.first;
- assert(atom != nullptr);
- if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom))
- if (defAtom->merge() == DefinedAtom::mergeAsTentative)
- ret.push_back(name);
- }
- return ret;
-}
-
} // namespace lld
diff --git a/lib/Core/Writer.cpp b/lib/Core/Writer.cpp
index 93e6438a28f5..51f95bc5053a 100644
--- a/lib/Core/Writer.cpp
+++ b/lib/Core/Writer.cpp
@@ -7,13 +7,12 @@
//
//===----------------------------------------------------------------------===//
-#include "lld/Core/File.h"
#include "lld/Core/Writer.h"
namespace lld {
-Writer::Writer() {
-}
-Writer::~Writer() {
-}
+Writer::Writer() = default;
+
+Writer::~Writer() = default;
+
} // end namespace lld
diff --git a/lib/Driver/DarwinLdDriver.cpp b/lib/Driver/DarwinLdDriver.cpp
index 496b651bab4f..9b4aede19aa2 100644
--- a/lib/Driver/DarwinLdDriver.cpp
+++ b/lib/Driver/DarwinLdDriver.cpp
@@ -14,24 +14,45 @@
//===----------------------------------------------------------------------===//
#include "lld/Core/ArchiveLibraryFile.h"
+#include "lld/Core/Error.h"
#include "lld/Core/File.h"
#include "lld/Core/Instrumentation.h"
+#include "lld/Core/LLVM.h"
+#include "lld/Core/Node.h"
#include "lld/Core/PassManager.h"
#include "lld/Core/Resolver.h"
#include "lld/Core/SharedLibraryFile.h"
-#include "lld/Driver/Driver.h"
+#include "lld/Core/Simple.h"
+#include "lld/Core/LinkingContext.h"
#include "lld/ReaderWriter/MachOLinkingContext.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
-#include "llvm/ADT/Triple.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/MachO.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <utility>
+#include <vector>
using namespace lld;
@@ -116,7 +137,7 @@ loadFile(MachOLinkingContext &ctx, StringRef path,
return files;
}
-} // anonymous namespace
+} // end anonymous namespace
// Test may be running on Windows. Canonicalize the path
// separator to '/' to get consistent outputs for tests.
@@ -166,8 +187,6 @@ static std::error_code parseExportsList(StringRef exportFilePath,
return std::error_code();
}
-
-
/// Order files are one symbol per line. Blank lines are ignored.
/// Trailing comments start with #. Symbol names can be prefixed with an
/// architecture name and/or .o leaf name. Examples:
@@ -270,7 +289,7 @@ static llvm::Error loadFileList(StringRef fileListPath,
addFile(path, ctx, forceLoad, false, diagnostics);
buffer = lineAndRest.second;
}
- return llvm::Error();
+ return llvm::Error::success();
}
/// Parse number assuming it is base 16, but allow 0x prefix.
@@ -739,9 +758,10 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx,
}
break;
case MachOLinkingContext::OS::iOS_simulator:
- if (pie->getOption().getID() == OPT_no_pie)
+ if (pie->getOption().getID() == OPT_no_pie) {
diagnostics << "iOS simulator programs must be built PIE\n";
return false;
+ }
break;
case MachOLinkingContext::OS::unknown:
break;
@@ -759,7 +779,6 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx,
diagnostics << pie->getSpelling()
<< " can only used when linking main executables\n";
return false;
- break;
}
}
@@ -1136,6 +1155,18 @@ bool parse(llvm::ArrayRef<const char *> args, MachOLinkingContext &ctx,
return ctx.validate(diagnostics);
}
+static void createFiles(MachOLinkingContext &ctx, bool Implicit) {
+ std::vector<std::unique_ptr<File>> Files;
+ if (Implicit)
+ ctx.createImplicitFiles(Files);
+ else
+ ctx.createInternalFiles(Files);
+ for (auto i = Files.rbegin(), e = Files.rend(); i != e; ++i) {
+ auto &members = ctx.getNodes();
+ members.insert(members.begin(), llvm::make_unique<FileNode>(std::move(*i)));
+ }
+}
+
/// This is where the link is actually performed.
bool link(llvm::ArrayRef<const char *> args, raw_ostream &diagnostics) {
MachOLinkingContext ctx;
@@ -1150,20 +1181,10 @@ bool link(llvm::ArrayRef<const char *> args, raw_ostream &diagnostics) {
if (FileNode *node = dyn_cast<FileNode>(ie.get()))
node->getFile()->parse();
- std::vector<std::unique_ptr<File>> internalFiles;
- ctx.createInternalFiles(internalFiles);
- for (auto i = internalFiles.rbegin(), e = internalFiles.rend(); i != e; ++i) {
- auto &members = ctx.getNodes();
- members.insert(members.begin(), llvm::make_unique<FileNode>(std::move(*i)));
- }
+ createFiles(ctx, false /* Implicit */);
- // Give target a chance to add files.
- std::vector<std::unique_ptr<File>> implicitFiles;
- ctx.createImplicitFiles(implicitFiles);
- for (auto i = implicitFiles.rbegin(), e = implicitFiles.rend(); i != e; ++i) {
- auto &members = ctx.getNodes();
- members.insert(members.begin(), llvm::make_unique<FileNode>(std::move(*i)));
- }
+ // Give target a chance to add files
+ createFiles(ctx, true /* Implicit */);
// Give target a chance to postprocess input files.
// Mach-O uses this chance to move all object files before library files.
@@ -1211,5 +1232,6 @@ bool link(llvm::ArrayRef<const char *> args, raw_ostream &diagnostics) {
return true;
}
-} // namespace mach_o
-} // namespace lld
+
+} // end namespace mach_o
+} // end namespace lld
diff --git a/lib/ReaderWriter/FileArchive.cpp b/lib/ReaderWriter/FileArchive.cpp
index eb7e7fb1837b..799f947a8c82 100644
--- a/lib/ReaderWriter/FileArchive.cpp
+++ b/lib/ReaderWriter/FileArchive.cpp
@@ -52,9 +52,12 @@ public:
Archive::Child c = member->second;
// Don't return a member already returned
- ErrorOr<StringRef> buf = c.getBuffer();
- if (!buf)
+ Expected<StringRef> buf = c.getBuffer();
+ if (!buf) {
+ // TODO: Actually report errors helpfully.
+ consumeError(buf.takeError());
return nullptr;
+ }
const char *memberStart = buf->data();
if (_membersInstantiated.count(memberStart))
return nullptr;
@@ -76,7 +79,7 @@ public:
parseAllMembers(std::vector<std::unique_ptr<File>> &result) override {
if (std::error_code ec = parse())
return ec;
- llvm::Error err;
+ llvm::Error err = llvm::Error::success();
for (auto mf = _archive->child_begin(err), me = _archive->child_end();
mf != me; ++mf) {
std::unique_ptr<File> file;
@@ -119,7 +122,7 @@ public:
protected:
std::error_code doParse() override {
// Make Archive object which will be owned by FileArchive object.
- llvm::Error Err;
+ llvm::Error Err = llvm::Error::success();
_archive.reset(new Archive(_mb->getMemBufferRef(), Err));
if (Err)
return errorToErrorCode(std::move(Err));
@@ -132,9 +135,9 @@ protected:
private:
std::error_code instantiateMember(Archive::Child member,
std::unique_ptr<File> &result) const {
- ErrorOr<llvm::MemoryBufferRef> mbOrErr = member.getMemoryBufferRef();
- if (std::error_code ec = mbOrErr.getError())
- return ec;
+ Expected<llvm::MemoryBufferRef> mbOrErr = member.getMemoryBufferRef();
+ if (!mbOrErr)
+ return errorToErrorCode(mbOrErr.takeError());
llvm::MemoryBufferRef mb = mbOrErr.get();
std::string memberPath = (_archive->getFileName() + "("
+ mb.getBufferIdentifier() + ")").str();
@@ -166,9 +169,9 @@ private:
<< _archive->getFileName() << "':\n");
for (const Archive::Symbol &sym : _archive->symbols()) {
StringRef name = sym.getName();
- ErrorOr<Archive::Child> memberOrErr = sym.getMember();
- if (std::error_code ec = memberOrErr.getError())
- return ec;
+ Expected<Archive::Child> memberOrErr = sym.getMember();
+ if (!memberOrErr)
+ return errorToErrorCode(memberOrErr.takeError());
Archive::Child member = memberOrErr.get();
DEBUG_WITH_TYPE("FileArchive",
llvm::dbgs()
diff --git a/lib/ReaderWriter/MachO/ArchHandler_arm.cpp b/lib/ReaderWriter/MachO/ArchHandler_arm.cpp
index 3286fe064535..7d1544854cf1 100644
--- a/lib/ReaderWriter/MachO/ArchHandler_arm.cpp
+++ b/lib/ReaderWriter/MachO/ArchHandler_arm.cpp
@@ -540,7 +540,7 @@ llvm::Error ArchHandler_arm::getReferenceInfo(
// Instruction contains branch to addend.
displacement = getDisplacementFromThumbBranch(instruction, fixupAddress);
*addend = fixupAddress + 4 + displacement;
- return llvm::Error();
+ return llvm::Error::success();
case ARM_THUMB_RELOC_BR22 | rPcRel | rLength4:
// ex: bl _foo (and _foo is defined)
if ((instruction & 0xD000F800) == 0x9000F000)
@@ -563,7 +563,7 @@ llvm::Error ArchHandler_arm::getReferenceInfo(
// reloc.value is target atom's address. Instruction contains branch
// to atom+addend.
*addend += (targetAddress - reloc.value);
- return llvm::Error();
+ return llvm::Error::success();
case ARM_RELOC_BR24 | rPcRel | rExtern | rLength4:
// ex: bl _foo (and _foo is undefined)
if (((instruction & 0x0F000000) == 0x0A000000)
@@ -576,7 +576,7 @@ llvm::Error ArchHandler_arm::getReferenceInfo(
// Instruction contains branch to addend.
displacement = getDisplacementFromArmBranch(instruction);
*addend = fixupAddress + 8 + displacement;
- return llvm::Error();
+ return llvm::Error::success();
case ARM_RELOC_BR24 | rPcRel | rLength4:
// ex: bl _foo (and _foo is defined)
if (((instruction & 0x0F000000) == 0x0A000000)
@@ -601,32 +601,32 @@ llvm::Error ArchHandler_arm::getReferenceInfo(
// reloc.value is target atom's address. Instruction contains branch
// to atom+addend.
*addend += (targetAddress - reloc.value);
- return llvm::Error();
+ return llvm::Error::success();
case ARM_RELOC_VANILLA | rExtern | rLength4:
// ex: .long _foo (and _foo is undefined)
*kind = pointer32;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = instruction;
- return llvm::Error();
+ return llvm::Error::success();
case ARM_RELOC_VANILLA | rLength4:
// ex: .long _foo (and _foo is defined)
*kind = pointer32;
if (auto ec = atomFromAddress(reloc.symbol, instruction, target, addend))
return ec;
*addend = clearThumbBit((uint32_t) * addend, *target);
- return llvm::Error();
+ return llvm::Error::success();
case ARM_RELOC_VANILLA | rScattered | rLength4:
// ex: .long _foo+a (and _foo is defined)
*kind = pointer32;
if (auto ec = atomFromAddress(0, reloc.value, target, addend))
return ec;
*addend += (clearThumbBit(instruction, *target) - reloc.value);
- return llvm::Error();
+ return llvm::Error::success();
default:
return llvm::make_error<GenericError>("unsupported arm relocation type");
}
- return llvm::Error();
+ return llvm::Error::success();
}
llvm::Error
@@ -847,7 +847,7 @@ ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1,
value = clearThumbBit(value, *target);
int64_t ta = (int64_t) value - (toAddress - fromAddress);
*addend = ta - offsetInFrom;
- return llvm::Error();
+ return llvm::Error::success();
} else {
uint32_t sectIndex;
if (thumbReloc) {
@@ -895,7 +895,7 @@ ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1,
}
}
- return llvm::Error();
+ return llvm::Error::success();
}
void ArchHandler_arm::applyFixupFinal(const Reference &ref, uint8_t *loc,
diff --git a/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp b/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp
index a61f6aac05e1..392a1be5b3d0 100644
--- a/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp
+++ b/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp
@@ -275,8 +275,8 @@ const ArchHandler::StubInfo ArchHandler_arm64::_sStubInfo = {
// GOT pointer to dyld_stub_binder
{ Reference::KindArch::AArch64, pointer64, 0, 0 },
- // arm64 code alignment 2^2
- 2,
+ // arm64 code alignment 2^1
+ 1,
// Stub size and code
12,
@@ -389,56 +389,56 @@ llvm::Error ArchHandler_arm64::getReferenceInfo(
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
- return llvm::Error();
+ return llvm::Error::success();
case ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4:
// ex: adrp x1, _foo@PAGE
*kind = page21;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
- return llvm::Error();
+ return llvm::Error::success();
case ARM64_RELOC_PAGEOFF12 | rExtern | rLength4:
// ex: ldr x0, [x1, _foo@PAGEOFF]
*kind = offset12KindFromInstruction(*(const little32_t *)fixupContent);
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
- return llvm::Error();
+ return llvm::Error::success();
case ARM64_RELOC_GOT_LOAD_PAGE21 | rPcRel | rExtern | rLength4:
// ex: adrp x1, _foo@GOTPAGE
*kind = gotPage21;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
- return llvm::Error();
+ return llvm::Error::success();
case ARM64_RELOC_GOT_LOAD_PAGEOFF12 | rExtern | rLength4:
// ex: ldr x0, [x1, _foo@GOTPAGEOFF]
*kind = gotOffset12;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
- return llvm::Error();
+ return llvm::Error::success();
case ARM64_RELOC_TLVP_LOAD_PAGE21 | rPcRel | rExtern | rLength4:
// ex: adrp x1, _foo@TLVPAGE
*kind = tlvPage21;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
- return llvm::Error();
+ return llvm::Error::success();
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12 | rExtern | rLength4:
// ex: ldr x0, [x1, _foo@TLVPAGEOFF]
*kind = tlvOffset12;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
- return llvm::Error();
+ return llvm::Error::success();
case ARM64_RELOC_UNSIGNED | rExtern | rLength8:
// ex: .quad _foo + N
*kind = pointer64;
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = *(const little64_t *)fixupContent;
- return llvm::Error();
+ return llvm::Error::success();
case ARM64_RELOC_UNSIGNED | rLength8:
// ex: .quad Lfoo + N
*kind = pointer64;
@@ -450,7 +450,7 @@ llvm::Error ArchHandler_arm64::getReferenceInfo(
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
- return llvm::Error();
+ return llvm::Error::success();
case ARM64_RELOC_POINTER_TO_GOT | rPcRel | rExtern | rLength4:
// ex: .long _foo@GOT - .
@@ -464,7 +464,7 @@ llvm::Error ArchHandler_arm64::getReferenceInfo(
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = 0;
- return llvm::Error();
+ return llvm::Error::success();
default:
return llvm::make_error<GenericError>("unsupported arm64 relocation type");
}
@@ -485,7 +485,7 @@ llvm::Error ArchHandler_arm64::getPairReferenceInfo(
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
return ec;
*addend = reloc1.symbol;
- return llvm::Error();
+ return llvm::Error::success();
case ((ARM64_RELOC_ADDEND | rLength4) << 16 |
ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4):
// ex: adrp x1, _foo@PAGE
@@ -493,7 +493,7 @@ llvm::Error ArchHandler_arm64::getPairReferenceInfo(
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
return ec;
*addend = reloc1.symbol;
- return llvm::Error();
+ return llvm::Error::success();
case ((ARM64_RELOC_ADDEND | rLength4) << 16 |
ARM64_RELOC_PAGEOFF12 | rExtern | rLength4): {
// ex: ldr w0, [x1, _foo@PAGEOFF]
@@ -502,7 +502,7 @@ llvm::Error ArchHandler_arm64::getPairReferenceInfo(
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
return ec;
*addend = reloc1.symbol;
- return llvm::Error();
+ return llvm::Error::success();
}
case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 |
ARM64_RELOC_UNSIGNED | rExtern | rLength8):
@@ -522,7 +522,7 @@ llvm::Error ArchHandler_arm64::getPairReferenceInfo(
return llvm::make_error<GenericError>(
"paired relocs must have the same offset");
*addend = (int64_t)*(const little64_t *)fixupContent + offsetInAtom;
- return llvm::Error();
+ return llvm::Error::success();
case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 |
ARM64_RELOC_UNSIGNED | rExtern | rLength4):
// ex: .quad _foo - .
@@ -530,7 +530,7 @@ llvm::Error ArchHandler_arm64::getPairReferenceInfo(
if (auto ec = atomFromSymbolIndex(reloc2.symbol, target))
return ec;
*addend = (int32_t)*(const little32_t *)fixupContent + offsetInAtom;
- return llvm::Error();
+ return llvm::Error::success();
default:
return llvm::make_error<GenericError>("unsupported arm64 relocation pair");
}
diff --git a/lib/ReaderWriter/MachO/ArchHandler_x86.cpp b/lib/ReaderWriter/MachO/ArchHandler_x86.cpp
index 15f1f793b5d7..c940ea542ee4 100644
--- a/lib/ReaderWriter/MachO/ArchHandler_x86.cpp
+++ b/lib/ReaderWriter/MachO/ArchHandler_x86.cpp
@@ -345,7 +345,7 @@ ArchHandler_x86::getReferenceInfo(const Relocation &reloc,
default:
return llvm::make_error<GenericError>("unsupported i386 relocation type");
}
- return llvm::Error();
+ return llvm::Error::success();
}
llvm::Error
@@ -403,7 +403,7 @@ ArchHandler_x86::getPairReferenceInfo(const normalized::Relocation &reloc1,
*addend = fromAddress + value - toAddress;
}
}
- return llvm::Error();
+ return llvm::Error::success();
break;
default:
return llvm::make_error<GenericError>("unsupported i386 relocation type");
diff --git a/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp b/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
index c36982a77b13..d687ca5de5b4 100644
--- a/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
+++ b/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
@@ -382,22 +382,22 @@ ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc,
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = *(const little32_t *)fixupContent;
- return llvm::Error();
+ return llvm::Error::success();
case ripRel32Minus1:
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = (int32_t)*(const little32_t *)fixupContent + 1;
- return llvm::Error();
+ return llvm::Error::success();
case ripRel32Minus2:
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = (int32_t)*(const little32_t *)fixupContent + 2;
- return llvm::Error();
+ return llvm::Error::success();
case ripRel32Minus4:
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = (int32_t)*(const little32_t *)fixupContent + 4;
- return llvm::Error();
+ return llvm::Error::success();
case ripRel32Anon:
targetAddress = fixupAddress + 4 + *(const little32_t *)fixupContent;
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
@@ -416,7 +416,7 @@ ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc,
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
return ec;
*addend = *(const little32_t *)fixupContent;
- return llvm::Error();
+ return llvm::Error::success();
case tlvInitSectionOffset:
case pointer64:
if (auto ec = atomFromSymbolIndex(reloc.symbol, target))
@@ -429,7 +429,7 @@ ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc,
assert(*addend == 0 && "TLV-init has non-zero addend?");
} else
*addend = *(const little64_t *)fixupContent;
- return llvm::Error();
+ return llvm::Error::success();
case pointer64Anon:
targetAddress = *(const little64_t *)fixupContent;
return atomFromAddress(reloc.symbol, targetAddress, target, addend);
@@ -463,7 +463,10 @@ ArchHandler_x86_64::getPairReferenceInfo(const normalized::Relocation &reloc1,
return ec;
uint64_t encodedAddend = (int64_t)*(const little64_t *)fixupContent;
if (inAtom == fromTarget) {
- *kind = delta64;
+ if (inAtom->contentType() == DefinedAtom::typeCFI)
+ *kind = unwindFDEToFunction;
+ else
+ *kind = delta64;
*addend = encodedAddend + offsetInAtom;
} else if (inAtom == *target) {
*kind = negDelta64;
@@ -471,7 +474,7 @@ ArchHandler_x86_64::getPairReferenceInfo(const normalized::Relocation &reloc1,
*target = fromTarget;
} else
return llvm::make_error<GenericError>("Invalid pointer diff");
- return llvm::Error();
+ return llvm::Error::success();
}
case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 |
X86_64_RELOC_UNSIGNED | rExtern | rLength4): {
@@ -487,7 +490,7 @@ ArchHandler_x86_64::getPairReferenceInfo(const normalized::Relocation &reloc1,
*target = fromTarget;
} else
return llvm::make_error<GenericError>("Invalid pointer diff");
- return llvm::Error();
+ return llvm::Error::success();
}
case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 |
X86_64_RELOC_UNSIGNED | rLength8):
diff --git a/lib/ReaderWriter/MachO/CMakeLists.txt b/lib/ReaderWriter/MachO/CMakeLists.txt
index 70f451c997b3..6a1064d6dfb5 100644
--- a/lib/ReaderWriter/MachO/CMakeLists.txt
+++ b/lib/ReaderWriter/MachO/CMakeLists.txt
@@ -21,8 +21,10 @@ add_lld_library(lldMachO
LINK_LIBS
lldCore
lldYAML
+ LLVMDebugInfoDWARF
LLVMObject
LLVMSupport
+ LLVMDemangle
${PTHREAD_LIB}
)
diff --git a/lib/ReaderWriter/MachO/CompactUnwindPass.cpp b/lib/ReaderWriter/MachO/CompactUnwindPass.cpp
index 6f5ab83dbda6..49d518456a45 100644
--- a/lib/ReaderWriter/MachO/CompactUnwindPass.cpp
+++ b/lib/ReaderWriter/MachO/CompactUnwindPass.cpp
@@ -298,7 +298,7 @@ private:
// Skip rest of pass if no unwind info.
if (unwindLocs.empty() && dwarfFrames.empty())
- return llvm::Error();
+ return llvm::Error::success();
// FIXME: if there are more than 4 personality functions then we need to
// defer to DWARF info for the ones we don't put in the list. They should
@@ -353,7 +353,7 @@ private:
return atom->contentType() == DefinedAtom::typeCompactUnwindInfo;
});
- return llvm::Error();
+ return llvm::Error::success();
}
void collectCompactUnwindEntries(
diff --git a/lib/ReaderWriter/MachO/DebugInfo.h b/lib/ReaderWriter/MachO/DebugInfo.h
new file mode 100644
index 000000000000..28e41bf4263c
--- /dev/null
+++ b/lib/ReaderWriter/MachO/DebugInfo.h
@@ -0,0 +1,106 @@
+//===- lib/ReaderWriter/MachO/File.h ----------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_READER_WRITER_MACHO_DEBUGINFO_H
+#define LLD_READER_WRITER_MACHO_DEBUGINFO_H
+
+#include "lld/Core/Atom.h"
+#include <vector>
+
+#include "llvm/Support/Format.h"
+#include "llvm/Support/raw_ostream.h"
+
+
+namespace lld {
+namespace mach_o {
+
+class DebugInfo {
+public:
+ enum class Kind {
+ Dwarf,
+ Stabs
+ };
+
+ Kind kind() const { return _kind; }
+
+ void setAllocator(std::unique_ptr<llvm::BumpPtrAllocator> allocator) {
+ _allocator = std::move(allocator);
+ }
+
+protected:
+ DebugInfo(Kind kind) : _kind(kind) {}
+
+private:
+ std::unique_ptr<llvm::BumpPtrAllocator> _allocator;
+ Kind _kind;
+};
+
+struct TranslationUnitSource {
+ StringRef name;
+ StringRef path;
+};
+
+class DwarfDebugInfo : public DebugInfo {
+public:
+ DwarfDebugInfo(TranslationUnitSource tu)
+ : DebugInfo(Kind::Dwarf), _tu(std::move(tu)) {}
+
+ static inline bool classof(const DebugInfo *di) {
+ return di->kind() == Kind::Dwarf;
+ }
+
+ const TranslationUnitSource &translationUnitSource() const { return _tu; }
+
+private:
+ TranslationUnitSource _tu;
+};
+
+struct Stab {
+ Stab(const Atom* atom, uint8_t type, uint8_t other, uint16_t desc,
+ uint32_t value, StringRef str)
+ : atom(atom), type(type), other(other), desc(desc), value(value),
+ str(str) {}
+
+ const class Atom* atom;
+ uint8_t type;
+ uint8_t other;
+ uint16_t desc;
+ uint32_t value;
+ StringRef str;
+};
+
+inline raw_ostream& operator<<(raw_ostream &os, Stab &s) {
+ os << "Stab -- atom: " << llvm::format("%p", s.atom) << ", type: " << (uint32_t)s.type
+ << ", other: " << (uint32_t)s.other << ", desc: " << s.desc << ", value: " << s.value
+ << ", str: '" << s.str << "'";
+ return os;
+}
+
+class StabsDebugInfo : public DebugInfo {
+public:
+
+ typedef std::vector<Stab> StabsList;
+
+ StabsDebugInfo(StabsList stabs)
+ : DebugInfo(Kind::Stabs), _stabs(std::move(stabs)) {}
+
+ static inline bool classof(const DebugInfo *di) {
+ return di->kind() == Kind::Stabs;
+ }
+
+ const StabsList& stabs() const { return _stabs; }
+
+public:
+ StabsList _stabs;
+};
+
+} // end namespace mach_o
+} // end namespace lld
+
+#endif // LLD_READER_WRITER_MACHO_DEBUGINFO_H
diff --git a/lib/ReaderWriter/MachO/File.h b/lib/ReaderWriter/MachO/File.h
index 64a0fcf82844..2bdd6342b477 100644
--- a/lib/ReaderWriter/MachO/File.h
+++ b/lib/ReaderWriter/MachO/File.h
@@ -11,11 +11,13 @@
#define LLD_READER_WRITER_MACHO_FILE_H
#include "Atoms.h"
+#include "DebugInfo.h"
#include "MachONormalizedFile.h"
#include "lld/Core/SharedLibraryFile.h"
#include "lld/Core/Simple.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Format.h"
#include <unordered_map>
namespace lld {
@@ -25,11 +27,15 @@ using lld::mach_o::normalized::Section;
class MachOFile : public SimpleFile {
public:
+
+ /// Real file constructor - for on-disk files.
MachOFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
: SimpleFile(mb->getBufferIdentifier(), File::kindMachObject),
_mb(std::move(mb)), _ctx(ctx) {}
- MachOFile(StringRef path) : SimpleFile(path, File::kindMachObject) {}
+ /// Dummy file constructor - for virtual files.
+ MachOFile(StringRef path)
+ : SimpleFile(path, File::kindMachObject) {}
void addDefinedAtom(StringRef name, Atom::Scope scope,
DefinedAtom::ContentType type, DefinedAtom::Merge merge,
@@ -225,6 +231,13 @@ public:
return F->kind() == File::kindMachObject;
}
+ void setDebugInfo(std::unique_ptr<DebugInfo> debugInfo) {
+ _debugInfo = std::move(debugInfo);
+ }
+
+ DebugInfo* debugInfo() const { return _debugInfo.get(); }
+ std::unique_ptr<DebugInfo> takeDebugInfo() { return std::move(_debugInfo); }
+
protected:
std::error_code doParse() override {
// Convert binary file to normalized mach-o.
@@ -265,6 +278,7 @@ private:
MachOLinkingContext::objc_unknown;
uint32_t _swiftVersion = 0;
normalized::FileFlags _flags = llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS;
+ std::unique_ptr<DebugInfo> _debugInfo;
};
class MachODylibFile : public SharedLibraryFile {
@@ -297,7 +311,7 @@ public:
_reExportedDylibs.emplace_back(dylibPath);
}
- StringRef installName() { return _installName; }
+ StringRef installName() const { return _installName; }
uint32_t currentVersion() { return _currentVersion; }
uint32_t compatVersion() { return _compatVersion; }
diff --git a/lib/ReaderWriter/MachO/GOTPass.cpp b/lib/ReaderWriter/MachO/GOTPass.cpp
index 6cdca0a9e055..8458a1c79282 100644
--- a/lib/ReaderWriter/MachO/GOTPass.cpp
+++ b/lib/ReaderWriter/MachO/GOTPass.cpp
@@ -134,7 +134,7 @@ private:
for (const GOTEntryAtom *slot : entries)
mergedFile.addAtom(*slot);
- return llvm::Error();
+ return llvm::Error::success();
}
bool shouldReplaceTargetWithGOTAtom(const Atom *target, bool canBypassGOT) {
diff --git a/lib/ReaderWriter/MachO/LayoutPass.cpp b/lib/ReaderWriter/MachO/LayoutPass.cpp
index dd2ee8567ec9..24dbf79d3e3b 100644
--- a/lib/ReaderWriter/MachO/LayoutPass.cpp
+++ b/lib/ReaderWriter/MachO/LayoutPass.cpp
@@ -474,7 +474,7 @@ llvm::Error LayoutPass::perform(SimpleFile &mergedFile) {
});
DEBUG(llvm::dbgs() << "******** Finished laying out atoms\n");
- return llvm::Error();
+ return llvm::Error::success();
}
void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx) {
diff --git a/lib/ReaderWriter/MachO/MachOLinkingContext.cpp b/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
index 05375f145d34..db4a96823e74 100644
--- a/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
+++ b/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
@@ -22,7 +22,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Triple.h"
-#include "llvm/Config/config.h"
+#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Host.h"
@@ -30,10 +30,6 @@
#include "llvm/Support/Path.h"
#include <algorithm>
-#if defined(HAVE_CXXABI_H)
-#include <cxxabi.h>
-#endif
-
using lld::mach_o::ArchHandler;
using lld::mach_o::MachOFile;
using lld::mach_o::MachODylibFile;
@@ -734,7 +730,7 @@ uint32_t MachOLinkingContext::dylibCurrentVersion(StringRef installName) const {
if (pos != _pathToDylibMap.end())
return pos->second->currentVersion();
else
- return 0x1000; // 1.0
+ return 0x10000; // 1.0
}
uint32_t MachOLinkingContext::dylibCompatVersion(StringRef installName) const {
@@ -742,7 +738,7 @@ uint32_t MachOLinkingContext::dylibCompatVersion(StringRef installName) const {
if (pos != _pathToDylibMap.end())
return pos->second->compatVersion();
else
- return 0x1000; // 1.0
+ return 0x10000; // 1.0
}
void MachOLinkingContext::createImplicitFiles(
@@ -772,7 +768,10 @@ void MachOLinkingContext::createImplicitFiles(
void MachOLinkingContext::registerDylib(MachODylibFile *dylib,
bool upward) const {
std::lock_guard<std::mutex> lock(_dylibsMutex);
- _allDylibs.insert(dylib);
+
+ if (std::find(_allDylibs.begin(),
+ _allDylibs.end(), dylib) == _allDylibs.end())
+ _allDylibs.push_back(dylib);
_pathToDylibMap[dylib->installName()] = dylib;
// If path is different than install name, register path too.
if (!dylib->path().equals(dylib->installName()))
@@ -873,24 +872,32 @@ std::string MachOLinkingContext::demangle(StringRef symbolName) const {
if (!symbolName.startswith("__Z"))
return symbolName;
-#if defined(HAVE_CXXABI_H)
SmallString<256> symBuff;
StringRef nullTermSym = Twine(symbolName).toNullTerminatedStringRef(symBuff);
// Mach-O has extra leading underscore that needs to be removed.
const char *cstr = nullTermSym.data() + 1;
int status;
- char *demangled = abi::__cxa_demangle(cstr, nullptr, nullptr, &status);
+ char *demangled = llvm::itaniumDemangle(cstr, nullptr, nullptr, &status);
if (demangled) {
std::string result(demangled);
// __cxa_demangle() always uses a malloc'ed buffer to return the result.
free(demangled);
return result;
}
-#endif
return symbolName;
}
+static void addDependencyInfoHelper(llvm::raw_fd_ostream *DepInfo,
+ char Opcode, StringRef Path) {
+ if (!DepInfo)
+ return;
+
+ *DepInfo << Opcode;
+ *DepInfo << Path;
+ *DepInfo << '\0';
+}
+
std::error_code MachOLinkingContext::createDependencyFile(StringRef path) {
std::error_code ec;
_dependencyInfo = std::unique_ptr<llvm::raw_fd_ostream>(new
@@ -900,42 +907,20 @@ std::error_code MachOLinkingContext::createDependencyFile(StringRef path) {
return ec;
}
- char linkerVersionOpcode = 0x00;
- *_dependencyInfo << linkerVersionOpcode;
- *_dependencyInfo << "lld"; // FIXME
- *_dependencyInfo << '\0';
-
+ addDependencyInfoHelper(_dependencyInfo.get(), 0x00, "lld" /*FIXME*/);
return std::error_code();
}
void MachOLinkingContext::addInputFileDependency(StringRef path) const {
- if (!_dependencyInfo)
- return;
-
- char inputFileOpcode = 0x10;
- *_dependencyInfo << inputFileOpcode;
- *_dependencyInfo << path;
- *_dependencyInfo << '\0';
+ addDependencyInfoHelper(_dependencyInfo.get(), 0x10, path);
}
void MachOLinkingContext::addInputFileNotFound(StringRef path) const {
- if (!_dependencyInfo)
- return;
-
- char inputFileOpcode = 0x11;
- *_dependencyInfo << inputFileOpcode;
- *_dependencyInfo << path;
- *_dependencyInfo << '\0';
+ addDependencyInfoHelper(_dependencyInfo.get(), 0x11, path);
}
void MachOLinkingContext::addOutputFileDependency(StringRef path) const {
- if (!_dependencyInfo)
- return;
-
- char outputFileOpcode = 0x40;
- *_dependencyInfo << outputFileOpcode;
- *_dependencyInfo << path;
- *_dependencyInfo << '\0';
+ addDependencyInfoHelper(_dependencyInfo.get(), 0x40, path);
}
void MachOLinkingContext::appendOrderedSymbol(StringRef symbol,
@@ -1044,7 +1029,7 @@ void MachOLinkingContext::finalizeInputFiles() {
llvm::Error MachOLinkingContext::handleLoadedFile(File &file) {
auto *machoFile = dyn_cast<MachOFile>(&file);
if (!machoFile)
- return llvm::Error();
+ return llvm::Error::success();
// Check that the arch of the context matches that of the file.
// Also set the arch of the context if it didn't have one.
@@ -1111,7 +1096,7 @@ llvm::Error MachOLinkingContext::handleLoadedFile(File &file) {
return llvm::make_error<GenericError>("different swift versions");
}
- return llvm::Error();
+ return llvm::Error::success();
}
} // end namespace lld
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFile.h b/lib/ReaderWriter/MachO/MachONormalizedFile.h
index 92a21f7ef83d..60d76d4b5c9b 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFile.h
+++ b/lib/ReaderWriter/MachO/MachONormalizedFile.h
@@ -42,6 +42,7 @@
#ifndef LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H
#define LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H
+#include "DebugInfo.h"
#include "lld/Core/Error.h"
#include "lld/Core/LLVM.h"
#include "lld/ReaderWriter/MachOLinkingContext.h"
@@ -226,7 +227,6 @@ struct DataInCode {
DataRegionType kind;
};
-
/// A typedef so that YAML I/O can encode/decode mach_header.flags.
LLVM_YAML_STRONG_TYPEDEF(uint32_t, FileFlags)
@@ -242,6 +242,7 @@ struct NormalizedFile {
std::vector<Symbol> localSymbols;
std::vector<Symbol> globalSymbols;
std::vector<Symbol> undefinedSymbols;
+ std::vector<Symbol> stabsSymbols;
// Maps to load commands with no LINKEDIT content (final linked images only).
std::vector<DependentDylib> dependentDylibs;
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
index a17de5be1742..23c7ea17f7e7 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
@@ -70,12 +70,12 @@ static llvm::Error forEachLoadCommand(
return llvm::make_error<GenericError>("Load command exceeds range");
if (func(slc->cmd, slc->cmdsize, p))
- return llvm::Error();
+ return llvm::Error::success();
p += slc->cmdsize;
}
- return llvm::Error();
+ return llvm::Error::success();
}
static std::error_code appendRelocations(Relocations &relocs, StringRef buffer,
@@ -390,12 +390,14 @@ readBinary(std::unique_ptr<MemoryBuffer> &mb,
if (sin->n_strx > strSize)
return true;
sout.name = &strings[sin->n_strx];
- sout.type = (NListType)(sin->n_type & N_TYPE);
+ sout.type = static_cast<NListType>(sin->n_type & (N_STAB|N_TYPE));
sout.scope = (sin->n_type & (N_PEXT|N_EXT));
sout.sect = sin->n_sect;
sout.desc = sin->n_desc;
sout.value = sin->n_value;
- if (sout.type == N_UNDF)
+ if (sin->n_type & N_STAB)
+ f->stabsSymbols.push_back(sout);
+ else if (sout.type == N_UNDF)
f->undefinedSymbols.push_back(sout);
else if (sin->n_type & N_EXT)
f->globalSymbols.push_back(sout);
@@ -429,6 +431,8 @@ readBinary(std::unique_ptr<MemoryBuffer> &mb,
f->undefinedSymbols.push_back(sout);
else if (sout.scope == (SymbolScope)N_EXT)
f->globalSymbols.push_back(sout);
+ else if (sin->n_type & N_STAB)
+ f->stabsSymbols.push_back(sout);
else
f->localSymbols.push_back(sout);
}
@@ -535,7 +539,7 @@ public:
loadFile(std::unique_ptr<MemoryBuffer> mb,
const Registry &registry) const override {
std::unique_ptr<File> ret =
- llvm::make_unique<MachOFile>(std::move(mb), &_ctx);
+ llvm::make_unique<MachOFile>(std::move(mb), &_ctx);
return std::move(ret);
}
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h
index 86823efa33c9..d69c5389e9d6 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h
@@ -71,22 +71,19 @@ using llvm::sys::getSwappedBytes;
template<typename T>
static inline uint16_t read16(const T *loc, bool isBig) {
- assert((uint64_t)loc % llvm::alignOf<T>() == 0 &&
- "invalid pointer alignment");
+ assert((uint64_t)loc % alignof(T) == 0 && "invalid pointer alignment");
return isBig ? read16be(loc) : read16le(loc);
}
template<typename T>
static inline uint32_t read32(const T *loc, bool isBig) {
- assert((uint64_t)loc % llvm::alignOf<T>() == 0 &&
- "invalid pointer alignment");
+ assert((uint64_t)loc % alignof(T) == 0 && "invalid pointer alignment");
return isBig ? read32be(loc) : read32le(loc);
}
template<typename T>
static inline uint64_t read64(const T *loc, bool isBig) {
- assert((uint64_t)loc % llvm::alignOf<T>() == 0 &&
- "invalid pointer alignment");
+ assert((uint64_t)loc % alignof(T) == 0 && "invalid pointer alignment");
return isBig ? read64be(loc) : read64le(loc);
}
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp
index f3e159684e15..e853faf9112e 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp
@@ -66,26 +66,9 @@ struct TrieEdge : public llvm::ilist_node<TrieEdge> {
namespace llvm {
- using lld::mach_o::normalized::TrieEdge;
- template <>
- struct ilist_traits<TrieEdge>
- : public ilist_default_traits<TrieEdge> {
- private:
- mutable ilist_half_node<TrieEdge> Sentinel;
- public:
- TrieEdge *createSentinel() const {
- return static_cast<TrieEdge*>(&Sentinel);
- }
- void destroySentinel(TrieEdge *) const {}
-
- TrieEdge *provideInitialHead() const { return createSentinel(); }
- TrieEdge *ensureHead(TrieEdge*) const { return createSentinel(); }
- static void noteHead(TrieEdge*, TrieEdge*) {}
- void deleteNode(TrieEdge *N) {}
-
- private:
- void createNode(const TrieEdge &);
- };
+using lld::mach_o::normalized::TrieEdge;
+template <>
+struct ilist_alloc_traits<TrieEdge> : ilist_noalloc_traits<TrieEdge> {};
} // namespace llvm
@@ -103,6 +86,9 @@ struct TrieNode {
void addSymbol(const Export &entry, BumpPtrAllocator &allocator,
std::vector<TrieNode *> &allNodes);
+
+ void addOrderedNodes(const Export &entry,
+ std::vector<TrieNode *> &allNodes);
bool updateOffset(uint32_t &offset);
void appendToByteBuffer(ByteBuffer &out);
@@ -115,6 +101,7 @@ private:
StringRef _importedName;
uint32_t _trieOffset;
bool _hasExportInfo;
+ bool _ordered = false;
};
/// Utility class for writing a mach-o binary file given an in-memory
@@ -639,7 +626,9 @@ llvm::Error MachOFileLayout::writeSingleSegmentLoadCommand(uint8_t *&lc) {
seg->vmsize = _file.sections.back().address
+ _file.sections.back().content.size();
seg->fileoff = _endOfLoadCommands;
- seg->filesize = seg->vmsize;
+ seg->filesize = _sectInfo[&_file.sections.back()].fileOffset +
+ _file.sections.back().content.size() -
+ _sectInfo[&_file.sections.front()].fileOffset;
seg->maxprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
seg->initprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
seg->nsects = _file.sections.size();
@@ -668,7 +657,7 @@ llvm::Error MachOFileLayout::writeSingleSegmentLoadCommand(uint8_t *&lc) {
++sout;
}
lc = next;
- return llvm::Error();
+ return llvm::Error::success();
}
template <typename T>
@@ -738,7 +727,7 @@ llvm::Error MachOFileLayout::writeSegmentLoadCommands(uint8_t *&lc) {
}
lc = reinterpret_cast<uint8_t*>(next);
}
- return llvm::Error();
+ return llvm::Error::success();
}
static void writeVersionMinLoadCommand(const NormalizedFile &_file,
@@ -789,8 +778,8 @@ llvm::Error MachOFileLayout::writeLoadCommands() {
st->cmd = LC_SYMTAB;
st->cmdsize = sizeof(symtab_command);
st->symoff = _startOfSymbols;
- st->nsyms = _file.localSymbols.size() + _file.globalSymbols.size()
- + _file.undefinedSymbols.size();
+ st->nsyms = _file.stabsSymbols.size() + _file.localSymbols.size() +
+ _file.globalSymbols.size() + _file.undefinedSymbols.size();
st->stroff = _startOfSymbolStrings;
st->strsize = _endOfSymbolStrings - _startOfSymbolStrings;
if (_swap)
@@ -876,8 +865,8 @@ llvm::Error MachOFileLayout::writeLoadCommands() {
st->cmd = LC_SYMTAB;
st->cmdsize = sizeof(symtab_command);
st->symoff = _startOfSymbols;
- st->nsyms = _file.localSymbols.size() + _file.globalSymbols.size()
- + _file.undefinedSymbols.size();
+ st->nsyms = _file.stabsSymbols.size() + _file.localSymbols.size() +
+ _file.globalSymbols.size() + _file.undefinedSymbols.size();
st->stroff = _startOfSymbolStrings;
st->strsize = _endOfSymbolStrings - _startOfSymbolStrings;
if (_swap)
@@ -890,7 +879,8 @@ llvm::Error MachOFileLayout::writeLoadCommands() {
dst->cmd = LC_DYSYMTAB;
dst->cmdsize = sizeof(dysymtab_command);
dst->ilocalsym = _symbolTableLocalsStartIndex;
- dst->nlocalsym = _file.localSymbols.size();
+ dst->nlocalsym = _file.stabsSymbols.size() +
+ _file.localSymbols.size();
dst->iextdefsym = _symbolTableGlobalsStartIndex;
dst->nextdefsym = _file.globalSymbols.size();
dst->iundefsym = _symbolTableUndefinesStartIndex;
@@ -1017,7 +1007,7 @@ llvm::Error MachOFileLayout::writeLoadCommands() {
lc += sizeof(linkedit_data_command);
}
}
- return llvm::Error();
+ return llvm::Error::success();
}
void MachOFileLayout::writeSectionContent() {
@@ -1101,7 +1091,10 @@ void MachOFileLayout::writeSymbolTable() {
// Write symbol table and symbol strings in parallel.
uint32_t symOffset = _startOfSymbols;
uint32_t strOffset = _startOfSymbolStrings;
- _buffer[strOffset++] = '\0'; // Reserve n_strx offset of zero to mean no name.
+ // Reserve n_strx offset of zero to mean no name.
+ _buffer[strOffset++] = ' ';
+ _buffer[strOffset++] = '\0';
+ appendSymbols(_file.stabsSymbols, symOffset, strOffset);
appendSymbols(_file.localSymbols, symOffset, strOffset);
appendSymbols(_file.globalSymbols, symOffset, strOffset);
appendSymbols(_file.undefinedSymbols, symOffset, strOffset);
@@ -1182,19 +1175,44 @@ void MachOFileLayout::buildRebaseInfo() {
void MachOFileLayout::buildBindInfo() {
// TODO: compress bind info.
uint64_t lastAddend = 0;
+ int lastOrdinal = 0x80000000;
+ StringRef lastSymbolName;
+ BindType lastType = (BindType)0;
+ Hex32 lastSegOffset = ~0U;
+ uint8_t lastSegIndex = (uint8_t)~0U;
for (const BindLocation& entry : _file.bindingInfo) {
- _bindingInfo.append_byte(BIND_OPCODE_SET_TYPE_IMM | entry.kind);
- _bindingInfo.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
- | entry.segIndex);
- _bindingInfo.append_uleb128(entry.segOffset);
- if (entry.ordinal > 0)
- _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
- (entry.ordinal & 0xF));
- else
- _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM |
- (entry.ordinal & 0xF));
- _bindingInfo.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM);
- _bindingInfo.append_string(entry.symbolName);
+ if (entry.ordinal != lastOrdinal) {
+ if (entry.ordinal <= 0)
+ _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM |
+ (entry.ordinal & BIND_IMMEDIATE_MASK));
+ else if (entry.ordinal <= BIND_IMMEDIATE_MASK)
+ _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
+ entry.ordinal);
+ else {
+ _bindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
+ _bindingInfo.append_uleb128(entry.ordinal);
+ }
+ lastOrdinal = entry.ordinal;
+ }
+
+ if (lastSymbolName != entry.symbolName) {
+ _bindingInfo.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM);
+ _bindingInfo.append_string(entry.symbolName);
+ lastSymbolName = entry.symbolName;
+ }
+
+ if (lastType != entry.kind) {
+ _bindingInfo.append_byte(BIND_OPCODE_SET_TYPE_IMM | entry.kind);
+ lastType = entry.kind;
+ }
+
+ if (lastSegIndex != entry.segIndex || lastSegOffset != entry.segOffset) {
+ _bindingInfo.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
+ | entry.segIndex);
+ _bindingInfo.append_uleb128(entry.segOffset);
+ lastSegIndex = entry.segIndex;
+ lastSegOffset = entry.segOffset;
+ }
if (entry.addend != lastAddend) {
_bindingInfo.append_byte(BIND_OPCODE_SET_ADDEND_SLEB);
_bindingInfo.append_sleb128(entry.addend);
@@ -1208,22 +1226,25 @@ void MachOFileLayout::buildBindInfo() {
void MachOFileLayout::buildLazyBindInfo() {
for (const BindLocation& entry : _file.lazyBindingInfo) {
- _lazyBindingInfo.append_byte(BIND_OPCODE_SET_TYPE_IMM | entry.kind);
_lazyBindingInfo.append_byte(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
| entry.segIndex);
- _lazyBindingInfo.append_uleb128Fixed(entry.segOffset, 5);
- if (entry.ordinal > 0)
- _lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
- (entry.ordinal & 0xF));
- else
+ _lazyBindingInfo.append_uleb128(entry.segOffset);
+ if (entry.ordinal <= 0)
_lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM |
- (entry.ordinal & 0xF));
+ (entry.ordinal & BIND_IMMEDIATE_MASK));
+ else if (entry.ordinal <= BIND_IMMEDIATE_MASK)
+ _lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
+ entry.ordinal);
+ else {
+ _lazyBindingInfo.append_byte(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
+ _lazyBindingInfo.append_uleb128(entry.ordinal);
+ }
+ // FIXME: We need to | the opcode here with flags.
_lazyBindingInfo.append_byte(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM);
_lazyBindingInfo.append_string(entry.symbolName);
_lazyBindingInfo.append_byte(BIND_OPCODE_DO_BIND);
_lazyBindingInfo.append_byte(BIND_OPCODE_DONE);
}
- _lazyBindingInfo.append_byte(BIND_OPCODE_DONE);
_lazyBindingInfo.align(_is64 ? 8 : 4);
}
@@ -1287,6 +1308,24 @@ void TrieNode::addSymbol(const Export& entry,
allNodes.push_back(newNode);
}
+void TrieNode::addOrderedNodes(const Export& entry,
+ std::vector<TrieNode*> &orderedNodes) {
+ if (!_ordered) {
+ orderedNodes.push_back(this);
+ _ordered = true;
+ }
+
+ StringRef partialStr = entry.name.drop_front(_cummulativeString.size());
+ for (TrieEdge &edge : _children) {
+ StringRef edgeStr = edge._subString;
+ if (partialStr.startswith(edgeStr)) {
+ // Already have matching edge, go down that path.
+ edge._child->addOrderedNodes(entry, orderedNodes);
+ return;
+ }
+ }
+}
+
bool TrieNode::updateOffset(uint32_t& offset) {
uint32_t nodeSize = 1; // Length when no export info
if (_hasExportInfo) {
@@ -1392,20 +1431,26 @@ void MachOFileLayout::buildExportTrie() {
rootNode->addSymbol(entry, allocator, allNodes);
}
+ std::vector<TrieNode*> orderedNodes;
+ orderedNodes.reserve(allNodes.size());
+
+ for (const Export& entry : _file.exportInfo)
+ rootNode->addOrderedNodes(entry, orderedNodes);
+
// Assign each node in the vector an offset in the trie stream, iterating
// until all uleb128 sizes have stabilized.
bool more;
do {
uint32_t offset = 0;
more = false;
- for (TrieNode* node : allNodes) {
+ for (TrieNode* node : orderedNodes) {
if (node->updateOffset(offset))
more = true;
}
} while (more);
// Serialize trie to ByteBuffer.
- for (TrieNode* node : allNodes) {
+ for (TrieNode* node : orderedNodes) {
node->appendToByteBuffer(_exportTrie);
}
_exportTrie.align(_is64 ? 8 : 4);
@@ -1414,10 +1459,15 @@ void MachOFileLayout::buildExportTrie() {
void MachOFileLayout::computeSymbolTableSizes() {
// MachO symbol tables have three ranges: locals, globals, and undefines
const size_t nlistSize = (_is64 ? sizeof(nlist_64) : sizeof(nlist));
- _symbolTableSize = nlistSize * (_file.localSymbols.size()
+ _symbolTableSize = nlistSize * (_file.stabsSymbols.size()
+ + _file.localSymbols.size()
+ _file.globalSymbols.size()
+ _file.undefinedSymbols.size());
- _symbolStringPoolSize = 1; // Always reserve 1-byte for the empty string.
+ // Always reserve 1-byte for the empty string and 1-byte for its terminator.
+ _symbolStringPoolSize = 2;
+ for (const Symbol &sym : _file.stabsSymbols) {
+ _symbolStringPoolSize += (sym.name.size()+1);
+ }
for (const Symbol &sym : _file.localSymbols) {
_symbolStringPoolSize += (sym.name.size()+1);
}
@@ -1428,7 +1478,8 @@ void MachOFileLayout::computeSymbolTableSizes() {
_symbolStringPoolSize += (sym.name.size()+1);
}
_symbolTableLocalsStartIndex = 0;
- _symbolTableGlobalsStartIndex = _file.localSymbols.size();
+ _symbolTableGlobalsStartIndex = _file.stabsSymbols.size() +
+ _file.localSymbols.size();
_symbolTableUndefinesStartIndex = _symbolTableGlobalsStartIndex
+ _file.globalSymbols.size();
@@ -1486,7 +1537,7 @@ llvm::Error MachOFileLayout::writeBinary(StringRef path) {
writeLinkEditContent();
fob->commit();
- return llvm::Error();
+ return llvm::Error::success();
}
/// Takes in-memory normalized view and writes a mach-o object file.
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
index 4775c75f7211..ddd3259842e2 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
@@ -22,6 +22,7 @@
#include "MachONormalizedFile.h"
#include "ArchHandler.h"
+#include "DebugInfo.h"
#include "MachONormalizedFileBinaryUtils.h"
#include "lld/Core/Error.h"
#include "lld/Core/LLVM.h"
@@ -34,6 +35,7 @@
#include "llvm/Support/MachO.h"
#include <map>
#include <system_error>
+#include <unordered_set>
using llvm::StringRef;
using llvm::isa;
@@ -120,6 +122,7 @@ public:
void copySectionInfo(NormalizedFile &file);
void updateSectionInfo(NormalizedFile &file);
void buildAtomToAddressMap();
+ llvm::Error synthesizeDebugNotes(NormalizedFile &file);
llvm::Error addSymbols(const lld::File &atomFile, NormalizedFile &file);
void addIndirectSymbols(const lld::File &atomFile, NormalizedFile &file);
void addRebaseAndBindingInfo(const lld::File &, NormalizedFile &file);
@@ -201,6 +204,7 @@ private:
bool _allSourceFilesHaveMinVersions = true;
LoadCommandType _minVersionCommandType = (LoadCommandType)0;
uint32_t _minVersion = 0;
+ std::vector<lld::mach_o::Stab> _stabs;
};
Util::~Util() {
@@ -785,6 +789,158 @@ void Util::buildAtomToAddressMap() {
}
}
+llvm::Error Util::synthesizeDebugNotes(NormalizedFile &file) {
+
+ // Bail out early if we don't need to generate a debug map.
+ if (_ctx.debugInfoMode() == MachOLinkingContext::DebugInfoMode::noDebugMap)
+ return llvm::Error::success();
+
+ std::vector<const DefinedAtom*> atomsNeedingDebugNotes;
+ std::set<const mach_o::MachOFile*> filesWithStabs;
+ bool objFileHasDwarf = false;
+ const File *objFile = nullptr;
+
+ for (SectionInfo *sect : _sectionInfos) {
+ for (const AtomInfo &info : sect->atomsAndOffsets) {
+ if (const DefinedAtom *atom = dyn_cast<DefinedAtom>(info.atom)) {
+
+ // FIXME: No stabs/debug-notes for symbols that wouldn't be in the
+ // symbol table.
+ // FIXME: No stabs/debug-notes for kernel dtrace probes.
+
+ if (atom->contentType() == DefinedAtom::typeCFI ||
+ atom->contentType() == DefinedAtom::typeCString)
+ continue;
+
+ // Whenever we encounter a new file, update the 'objfileHasDwarf' flag.
+ if (&info.atom->file() != objFile) {
+ objFileHasDwarf = false;
+ if (const mach_o::MachOFile *atomFile =
+ dyn_cast<mach_o::MachOFile>(&info.atom->file())) {
+ if (atomFile->debugInfo()) {
+ if (isa<mach_o::DwarfDebugInfo>(atomFile->debugInfo()))
+ objFileHasDwarf = true;
+ else if (isa<mach_o::StabsDebugInfo>(atomFile->debugInfo()))
+ filesWithStabs.insert(atomFile);
+ }
+ }
+ }
+
+ // If this atom is from a file that needs dwarf, add it to the list.
+ if (objFileHasDwarf)
+ atomsNeedingDebugNotes.push_back(info.atom);
+ }
+ }
+ }
+
+ // Sort atoms needing debug notes by file ordinal, then atom ordinal.
+ std::sort(atomsNeedingDebugNotes.begin(), atomsNeedingDebugNotes.end(),
+ [](const DefinedAtom *lhs, const DefinedAtom *rhs) {
+ if (lhs->file().ordinal() != rhs->file().ordinal())
+ return (lhs->file().ordinal() < rhs->file().ordinal());
+ return (lhs->ordinal() < rhs->ordinal());
+ });
+
+ // FIXME: Handle <rdar://problem/17689030>: Add -add_ast_path option to \
+ // linker which add N_AST stab entry to output
+ // See OutputFile::synthesizeDebugNotes in ObjectFile.cpp in ld64.
+
+ StringRef oldFileName = "";
+ StringRef oldDirPath = "";
+ bool wroteStartSO = false;
+ std::unordered_set<std::string> seenFiles;
+ for (const DefinedAtom *atom : atomsNeedingDebugNotes) {
+ const auto &atomFile = cast<mach_o::MachOFile>(atom->file());
+ assert(dyn_cast_or_null<lld::mach_o::DwarfDebugInfo>(atomFile.debugInfo())
+ && "file for atom needing debug notes does not contain dwarf");
+ auto &dwarf = cast<lld::mach_o::DwarfDebugInfo>(*atomFile.debugInfo());
+
+ auto &tu = dwarf.translationUnitSource();
+ StringRef newFileName = tu.name;
+ StringRef newDirPath = tu.path;
+
+ // Add an SO whenever the TU source file changes.
+ if (newFileName != oldFileName || newDirPath != oldDirPath) {
+ // Translation unit change, emit ending SO
+ if (oldFileName != "")
+ _stabs.push_back(mach_o::Stab(nullptr, N_SO, 1, 0, 0, ""));
+
+ oldFileName = newFileName;
+ oldDirPath = newDirPath;
+
+ // If newDirPath doesn't end with a '/' we need to add one:
+ if (newDirPath.back() != '/') {
+ char *p =
+ file.ownedAllocations.Allocate<char>(newDirPath.size() + 2);
+ memcpy(p, newDirPath.data(), newDirPath.size());
+ p[newDirPath.size()] = '/';
+ p[newDirPath.size() + 1] = '\0';
+ newDirPath = p;
+ }
+
+ // New translation unit, emit start SOs:
+ _stabs.push_back(mach_o::Stab(nullptr, N_SO, 0, 0, 0, newDirPath));
+ _stabs.push_back(mach_o::Stab(nullptr, N_SO, 0, 0, 0, newFileName));
+
+ // Synthesize OSO for start of file.
+ char *fullPath = nullptr;
+ {
+ SmallString<1024> pathBuf(atomFile.path());
+ if (auto EC = llvm::sys::fs::make_absolute(pathBuf))
+ return llvm::errorCodeToError(EC);
+ fullPath = file.ownedAllocations.Allocate<char>(pathBuf.size() + 1);
+ memcpy(fullPath, pathBuf.c_str(), pathBuf.size() + 1);
+ }
+
+ // Get mod time.
+ uint32_t modTime = 0;
+ llvm::sys::fs::file_status stat;
+ if (!llvm::sys::fs::status(fullPath, stat))
+ if (llvm::sys::fs::exists(stat))
+ modTime = llvm::sys::toTimeT(stat.getLastModificationTime());
+
+ _stabs.push_back(mach_o::Stab(nullptr, N_OSO, _ctx.getCPUSubType(), 1,
+ modTime, fullPath));
+ // <rdar://problem/6337329> linker should put cpusubtype in n_sect field
+ // of nlist entry for N_OSO debug note entries.
+ wroteStartSO = true;
+ }
+
+ if (atom->contentType() == DefinedAtom::typeCode) {
+ // Synthesize BNSYM and start FUN stabs.
+ _stabs.push_back(mach_o::Stab(atom, N_BNSYM, 1, 0, 0, ""));
+ _stabs.push_back(mach_o::Stab(atom, N_FUN, 1, 0, 0, atom->name()));
+ // Synthesize any SOL stabs needed
+ // FIXME: add SOL stabs.
+ _stabs.push_back(mach_o::Stab(nullptr, N_FUN, 0, 0,
+ atom->rawContent().size(), ""));
+ _stabs.push_back(mach_o::Stab(nullptr, N_ENSYM, 1, 0,
+ atom->rawContent().size(), ""));
+ } else {
+ if (atom->scope() == Atom::scopeTranslationUnit)
+ _stabs.push_back(mach_o::Stab(atom, N_STSYM, 1, 0, 0, atom->name()));
+ else
+ _stabs.push_back(mach_o::Stab(nullptr, N_GSYM, 1, 0, 0, atom->name()));
+ }
+ }
+
+ // Emit ending SO if necessary.
+ if (wroteStartSO)
+ _stabs.push_back(mach_o::Stab(nullptr, N_SO, 1, 0, 0, ""));
+
+ // Copy any stabs from .o file.
+ for (const auto *objFile : filesWithStabs) {
+ const auto &stabsList =
+ cast<mach_o::StabsDebugInfo>(objFile->debugInfo())->stabs();
+ for (auto &stab : stabsList) {
+ // FIXME: Drop stabs whose atoms have been dead-stripped.
+ _stabs.push_back(stab);
+ }
+ }
+
+ return llvm::Error::success();
+}
+
uint16_t Util::descBits(const DefinedAtom* atom) {
uint16_t desc = 0;
switch (atom->merge()) {
@@ -807,7 +963,8 @@ uint16_t Util::descBits(const DefinedAtom* atom) {
desc |= REFERENCED_DYNAMICALLY;
if (_archHandler.isThumbFunction(*atom))
desc |= N_ARM_THUMB_DEF;
- if (atom->deadStrip() == DefinedAtom::deadStripNever) {
+ if (atom->deadStrip() == DefinedAtom::deadStripNever &&
+ _ctx.outputMachOType() == llvm::MachO::MH_OBJECT) {
if ((atom->contentType() != DefinedAtom::typeInitializerPtr)
&& (atom->contentType() != DefinedAtom::typeTerminatorPtr))
desc |= N_NO_DEAD_STRIP;
@@ -828,7 +985,7 @@ llvm::Error Util::getSymbolTableRegion(const DefinedAtom* atom,
case Atom::scopeTranslationUnit:
scope = 0;
inGlobalsRegion = false;
- return llvm::Error();
+ return llvm::Error::success();
case Atom::scopeLinkageUnit:
if ((_ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) &&
_ctx.exportSymbolNamed(atom->name())) {
@@ -840,38 +997,55 @@ llvm::Error Util::getSymbolTableRegion(const DefinedAtom* atom,
// -keep_private_externs means keep in globals region as N_PEXT.
scope = N_PEXT | N_EXT;
inGlobalsRegion = true;
- return llvm::Error();
+ return llvm::Error::success();
}
}
// scopeLinkageUnit symbols are no longer global once linked.
scope = N_PEXT;
inGlobalsRegion = false;
- return llvm::Error();
+ return llvm::Error::success();
case Atom::scopeGlobal:
if (_ctx.exportRestrictMode()) {
if (_ctx.exportSymbolNamed(atom->name())) {
scope = N_EXT;
inGlobalsRegion = true;
- return llvm::Error();
+ return llvm::Error::success();
} else {
scope = N_PEXT;
inGlobalsRegion = false;
- return llvm::Error();
+ return llvm::Error::success();
}
} else {
scope = N_EXT;
inGlobalsRegion = true;
- return llvm::Error();
+ return llvm::Error::success();
}
break;
}
llvm_unreachable("atom->scope() unknown enum value");
}
+
+
llvm::Error Util::addSymbols(const lld::File &atomFile,
NormalizedFile &file) {
bool rMode = (_ctx.outputMachOType() == llvm::MachO::MH_OBJECT);
- // Mach-O symbol table has three regions: locals, globals, undefs.
+ // Mach-O symbol table has four regions: stabs, locals, globals, undefs.
+
+ // Add all stabs.
+ for (auto &stab : _stabs) {
+ Symbol sym;
+ sym.type = static_cast<NListType>(stab.type);
+ sym.scope = 0;
+ sym.sect = stab.other;
+ sym.desc = stab.desc;
+ if (stab.atom)
+ sym.value = _atomToAddress[stab.atom];
+ else
+ sym.value = stab.value;
+ sym.name = stab.str;
+ file.stabsSymbols.push_back(sym);
+ }
// Add all local (non-global) symbols in address order
std::vector<AtomAndIndex> globals;
@@ -965,7 +1139,7 @@ llvm::Error Util::addSymbols(const lld::File &atomFile,
file.undefinedSymbols.push_back(sym);
}
- return llvm::Error();
+ return llvm::Error::success();
}
const Atom *Util::targetOfLazyPointer(const DefinedAtom *lpAtom) {
@@ -1040,15 +1214,15 @@ void Util::addIndirectSymbols(const lld::File &atomFile, NormalizedFile &file) {
}
}
-void Util::addDependentDylibs(const lld::File &atomFile,NormalizedFile &nFile) {
+void Util::addDependentDylibs(const lld::File &atomFile,
+ NormalizedFile &nFile) {
// Scan all imported symbols and build up list of dylibs they are from.
int ordinal = 1;
- for (const SharedLibraryAtom *slAtom : atomFile.sharedLibrary()) {
- StringRef loadPath = slAtom->loadName();
- DylibPathToInfo::iterator pos = _dylibInfo.find(loadPath);
+ for (const auto *dylib : _ctx.allDylibs()) {
+ DylibPathToInfo::iterator pos = _dylibInfo.find(dylib->installName());
if (pos == _dylibInfo.end()) {
DylibInfo info;
- bool flatNamespaceAtom = &slAtom->file() == _ctx.flatNamespaceFile();
+ bool flatNamespaceAtom = dylib == _ctx.flatNamespaceFile();
// If we're in -flat_namespace mode (or this atom came from the flat
// namespace file under -undefined dynamic_lookup) then use the flat
@@ -1057,24 +1231,22 @@ void Util::addDependentDylibs(const lld::File &atomFile,NormalizedFile &nFile) {
info.ordinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
else
info.ordinal = ordinal++;
- info.hasWeak = slAtom->canBeNullAtRuntime();
+ info.hasWeak = false;
info.hasNonWeak = !info.hasWeak;
- _dylibInfo[loadPath] = info;
+ _dylibInfo[dylib->installName()] = info;
// Unless this was a flat_namespace atom, record the source dylib.
if (!flatNamespaceAtom) {
DependentDylib depInfo;
- depInfo.path = loadPath;
+ depInfo.path = dylib->installName();
depInfo.kind = llvm::MachO::LC_LOAD_DYLIB;
- depInfo.currentVersion = _ctx.dylibCurrentVersion(loadPath);
- depInfo.compatVersion = _ctx.dylibCompatVersion(loadPath);
+ depInfo.currentVersion = _ctx.dylibCurrentVersion(dylib->path());
+ depInfo.compatVersion = _ctx.dylibCompatVersion(dylib->path());
nFile.dependentDylibs.push_back(depInfo);
}
} else {
- if ( slAtom->canBeNullAtRuntime() )
- pos->second.hasWeak = true;
- else
- pos->second.hasNonWeak = true;
+ pos->second.hasWeak = false;
+ pos->second.hasNonWeak = !pos->second.hasWeak;
}
}
// Automatically weak link dylib in which all symbols are weak (canBeNull).
@@ -1404,6 +1576,8 @@ normalizedFromAtoms(const lld::File &atomFile,
util.copySectionInfo(normFile);
util.assignAddressesToSections(normFile);
util.buildAtomToAddressMap();
+ if (auto err = util.synthesizeDebugNotes(normFile))
+ return std::move(err);
util.updateSectionInfo(normFile);
util.copySectionContent(normFile);
if (auto ec = util.addSymbols(atomFile, normFile)) {
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
index fc760a3eddd0..4b17f7b3a85f 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
@@ -27,7 +27,11 @@
#include "MachONormalizedFileBinaryUtils.h"
#include "lld/Core/Error.h"
#include "lld/Core/LLVM.h"
+#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
+#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/Debug.h"
+#include "llvm/Support/Dwarf.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MachO.h"
#include "llvm/Support/LEB128.h"
@@ -320,7 +324,7 @@ llvm::Error processSymboledSection(DefinedAtom::ContentType atomType,
// If section has no symbols and no content, there are no atoms.
if (symbols.empty() && section.content.empty())
- return llvm::Error();
+ return llvm::Error::success();
if (symbols.empty()) {
// Section has no symbols, put all content in one anoymous atom.
@@ -371,7 +375,7 @@ llvm::Error processSymboledSection(DefinedAtom::ContentType atomType,
});
}
- return llvm::Error();
+ return llvm::Error::success();
}
llvm::Error processSection(DefinedAtom::ContentType atomType,
@@ -483,7 +487,7 @@ llvm::Error processSection(DefinedAtom::ContentType atomType,
offset += size;
}
}
- return llvm::Error();
+ return llvm::Error::success();
}
const Section* findSectionCoveringAddress(const NormalizedFile &normalizedFile,
@@ -499,7 +503,7 @@ const Section* findSectionCoveringAddress(const NormalizedFile &normalizedFile,
const MachODefinedAtom *
findAtomCoveringAddress(const NormalizedFile &normalizedFile, MachOFile &file,
- uint64_t addr, Reference::Addend *addend) {
+ uint64_t addr, Reference::Addend &addend) {
const Section *sect = nullptr;
sect = findSectionCoveringAddress(normalizedFile, addr);
if (!sect)
@@ -509,7 +513,7 @@ findAtomCoveringAddress(const NormalizedFile &normalizedFile, MachOFile &file,
uint64_t offsetInSect = addr - sect->address;
auto atom =
file.findAtomCoveringAddress(*sect, offsetInSect, &offsetInTarget);
- *addend = offsetInTarget;
+ addend = offsetInTarget;
return atom;
}
@@ -540,7 +544,7 @@ llvm::Error convertRelocs(const Section &section,
uint64_t offsetInSect = addr - sect->address;
*atom = file.findAtomCoveringAddress(*sect, offsetInSect, &offsetInTarget);
*addend = offsetInTarget;
- return llvm::Error();
+ return llvm::Error::success();
};
// Utility function for ArchHandler to find atom by its symbol index.
@@ -548,19 +552,23 @@ llvm::Error convertRelocs(const Section &section,
-> llvm::Error {
// Find symbol from index.
const Symbol *sym = nullptr;
+ uint32_t numStabs = normalizedFile.stabsSymbols.size();
uint32_t numLocal = normalizedFile.localSymbols.size();
uint32_t numGlobal = normalizedFile.globalSymbols.size();
uint32_t numUndef = normalizedFile.undefinedSymbols.size();
- if (symbolIndex < numLocal) {
- sym = &normalizedFile.localSymbols[symbolIndex];
- } else if (symbolIndex < numLocal+numGlobal) {
- sym = &normalizedFile.globalSymbols[symbolIndex-numLocal];
- } else if (symbolIndex < numLocal+numGlobal+numUndef) {
- sym = &normalizedFile.undefinedSymbols[symbolIndex-numLocal-numGlobal];
+ assert(symbolIndex >= numStabs && "Searched for stab via atomBySymbol?");
+ if (symbolIndex < numStabs+numLocal) {
+ sym = &normalizedFile.localSymbols[symbolIndex-numStabs];
+ } else if (symbolIndex < numStabs+numLocal+numGlobal) {
+ sym = &normalizedFile.globalSymbols[symbolIndex-numStabs-numLocal];
+ } else if (symbolIndex < numStabs+numLocal+numGlobal+numUndef) {
+ sym = &normalizedFile.undefinedSymbols[symbolIndex-numStabs-numLocal-
+ numGlobal];
} else {
return llvm::make_error<GenericError>(Twine("symbol index (")
+ Twine(symbolIndex) + ") out of range");
}
+
// Find atom from symbol.
if ((sym->type & N_TYPE) == N_SECT) {
if (sym->sect > normalizedFile.sections.size())
@@ -572,14 +580,14 @@ llvm::Error convertRelocs(const Section &section,
targetOffsetInSect);
if (target) {
*result = target;
- return llvm::Error();
+ return llvm::Error::success();
}
return llvm::make_error<GenericError>("no atom found for defined symbol");
} else if ((sym->type & N_TYPE) == N_UNDF) {
const lld::Atom *target = file.findUndefAtom(sym->name);
if (target) {
*result = target;
- return llvm::Error();
+ return llvm::Error::success();
}
return llvm::make_error<GenericError>("no undefined atom found for sym");
} else {
@@ -676,7 +684,7 @@ llvm::Error convertRelocs(const Section &section,
kind, offsetInAtom, target, addend);
}
- return llvm::Error();
+ return llvm::Error::success();
}
bool isDebugInfoSection(const Section &section) {
@@ -685,6 +693,301 @@ bool isDebugInfoSection(const Section &section) {
return section.segmentName.equals("__DWARF");
}
+static const Atom* findDefinedAtomByName(MachOFile &file, Twine name) {
+ std::string strName = name.str();
+ for (auto *atom : file.defined())
+ if (atom->name() == strName)
+ return atom;
+ return nullptr;
+}
+
+static StringRef copyDebugString(StringRef str, BumpPtrAllocator &alloc) {
+ char *strCopy = alloc.Allocate<char>(str.size() + 1);
+ memcpy(strCopy, str.data(), str.size());
+ strCopy[str.size()] = '\0';
+ return strCopy;
+}
+
+llvm::Error parseStabs(MachOFile &file,
+ const NormalizedFile &normalizedFile,
+ bool copyRefs) {
+
+ if (normalizedFile.stabsSymbols.empty())
+ return llvm::Error::success();
+
+ // FIXME: Kill this off when we can move to sane yaml parsing.
+ std::unique_ptr<BumpPtrAllocator> allocator;
+ if (copyRefs)
+ allocator = llvm::make_unique<BumpPtrAllocator>();
+
+ enum { start, inBeginEnd } state = start;
+
+ const Atom *currentAtom = nullptr;
+ uint64_t currentAtomAddress = 0;
+ StabsDebugInfo::StabsList stabsList;
+ for (const auto &stabSym : normalizedFile.stabsSymbols) {
+ Stab stab(nullptr, stabSym.type, stabSym.sect, stabSym.desc,
+ stabSym.value, stabSym.name);
+ switch (state) {
+ case start:
+ switch (static_cast<StabType>(stabSym.type)) {
+ case N_BNSYM:
+ state = inBeginEnd;
+ currentAtomAddress = stabSym.value;
+ Reference::Addend addend;
+ currentAtom = findAtomCoveringAddress(normalizedFile, file,
+ currentAtomAddress, addend);
+ if (addend != 0)
+ return llvm::make_error<GenericError>(
+ "Non-zero addend for BNSYM '" + stabSym.name + "' in " +
+ file.path());
+ if (currentAtom)
+ stab.atom = currentAtom;
+ else {
+ // FIXME: ld64 just issues a warning here - should we match that?
+ return llvm::make_error<GenericError>(
+ "can't find atom for stabs BNSYM at " +
+ Twine::utohexstr(stabSym.value) + " in " + file.path());
+ }
+ break;
+ case N_SO:
+ case N_OSO:
+ // Not associated with an atom, just copy.
+ if (copyRefs)
+ stab.str = copyDebugString(stabSym.name, *allocator);
+ else
+ stab.str = stabSym.name;
+ break;
+ case N_GSYM: {
+ auto colonIdx = stabSym.name.find(':');
+ if (colonIdx != StringRef::npos) {
+ StringRef name = stabSym.name.substr(0, colonIdx);
+ currentAtom = findDefinedAtomByName(file, "_" + name);
+ stab.atom = currentAtom;
+ if (copyRefs)
+ stab.str = copyDebugString(stabSym.name, *allocator);
+ else
+ stab.str = stabSym.name;
+ } else {
+ currentAtom = findDefinedAtomByName(file, stabSym.name);
+ stab.atom = currentAtom;
+ if (copyRefs)
+ stab.str = copyDebugString(stabSym.name, *allocator);
+ else
+ stab.str = stabSym.name;
+ }
+ if (stab.atom == nullptr)
+ return llvm::make_error<GenericError>(
+ "can't find atom for N_GSYM stabs" + stabSym.name +
+ " in " + file.path());
+ break;
+ }
+ case N_FUN:
+ return llvm::make_error<GenericError>(
+ "old-style N_FUN stab '" + stabSym.name + "' unsupported");
+ default:
+ return llvm::make_error<GenericError>(
+ "unrecognized stab symbol '" + stabSym.name + "'");
+ }
+ break;
+ case inBeginEnd:
+ stab.atom = currentAtom;
+ switch (static_cast<StabType>(stabSym.type)) {
+ case N_ENSYM:
+ state = start;
+ currentAtom = nullptr;
+ break;
+ case N_FUN:
+ // Just copy the string.
+ if (copyRefs)
+ stab.str = copyDebugString(stabSym.name, *allocator);
+ else
+ stab.str = stabSym.name;
+ break;
+ default:
+ return llvm::make_error<GenericError>(
+ "unrecognized stab symbol '" + stabSym.name + "'");
+ }
+ }
+ llvm::dbgs() << "Adding to stabsList: " << stab << "\n";
+ stabsList.push_back(stab);
+ }
+
+ file.setDebugInfo(llvm::make_unique<StabsDebugInfo>(std::move(stabsList)));
+
+ // FIXME: Kill this off when we fix YAML memory ownership.
+ file.debugInfo()->setAllocator(std::move(allocator));
+
+ return llvm::Error::success();
+}
+
+static llvm::DataExtractor
+dataExtractorFromSection(const NormalizedFile &normalizedFile,
+ const Section &S) {
+ const bool is64 = MachOLinkingContext::is64Bit(normalizedFile.arch);
+ const bool isBig = MachOLinkingContext::isBigEndian(normalizedFile.arch);
+ StringRef SecData(reinterpret_cast<const char*>(S.content.data()),
+ S.content.size());
+ return llvm::DataExtractor(SecData, !isBig, is64 ? 8 : 4);
+}
+
+// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
+// inspection" code if possible.
+static uint32_t getCUAbbrevOffset(llvm::DataExtractor abbrevData,
+ uint64_t abbrCode) {
+ uint64_t curCode;
+ uint32_t offset = 0;
+ while ((curCode = abbrevData.getULEB128(&offset)) != abbrCode) {
+ // Tag
+ abbrevData.getULEB128(&offset);
+ // DW_CHILDREN
+ abbrevData.getU8(&offset);
+ // Attributes
+ while (abbrevData.getULEB128(&offset) | abbrevData.getULEB128(&offset))
+ ;
+ }
+ return offset;
+}
+
+// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
+// inspection" code if possible.
+static Expected<const char *>
+getIndexedString(const NormalizedFile &normalizedFile,
+ llvm::dwarf::Form form, llvm::DataExtractor infoData,
+ uint32_t &infoOffset, const Section &stringsSection) {
+ if (form == llvm::dwarf::DW_FORM_string)
+ return infoData.getCStr(&infoOffset);
+ if (form != llvm::dwarf::DW_FORM_strp)
+ return llvm::make_error<GenericError>(
+ "string field encoded without DW_FORM_strp");
+ uint32_t stringOffset = infoData.getU32(&infoOffset);
+ llvm::DataExtractor stringsData =
+ dataExtractorFromSection(normalizedFile, stringsSection);
+ return stringsData.getCStr(&stringOffset);
+}
+
+// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
+// inspection" code if possible.
+static llvm::Expected<TranslationUnitSource>
+readCompUnit(const NormalizedFile &normalizedFile,
+ const Section &info,
+ const Section &abbrev,
+ const Section &strings,
+ StringRef path) {
+ // FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
+ // inspection" code if possible.
+ uint32_t offset = 0;
+ llvm::dwarf::DwarfFormat Format = llvm::dwarf::DwarfFormat::DWARF32;
+ auto infoData = dataExtractorFromSection(normalizedFile, info);
+ uint32_t length = infoData.getU32(&offset);
+ if (length == 0xffffffff) {
+ Format = llvm::dwarf::DwarfFormat::DWARF64;
+ infoData.getU64(&offset);
+ }
+ else if (length > 0xffffff00)
+ return llvm::make_error<GenericError>("Malformed DWARF in " + path);
+
+ uint16_t version = infoData.getU16(&offset);
+
+ if (version < 2 || version > 4)
+ return llvm::make_error<GenericError>("Unsupported DWARF version in " +
+ path);
+
+ infoData.getU32(&offset); // Abbrev offset (should be zero)
+ uint8_t addrSize = infoData.getU8(&offset);
+
+ uint32_t abbrCode = infoData.getULEB128(&offset);
+ auto abbrevData = dataExtractorFromSection(normalizedFile, abbrev);
+ uint32_t abbrevOffset = getCUAbbrevOffset(abbrevData, abbrCode);
+ uint64_t tag = abbrevData.getULEB128(&abbrevOffset);
+ if (tag != llvm::dwarf::DW_TAG_compile_unit)
+ return llvm::make_error<GenericError>("top level DIE is not a compile unit");
+ // DW_CHILDREN
+ abbrevData.getU8(&abbrevOffset);
+ uint32_t name;
+ llvm::dwarf::Form form;
+ TranslationUnitSource tu;
+ while ((name = abbrevData.getULEB128(&abbrevOffset)) |
+ (form = static_cast<llvm::dwarf::Form>(
+ abbrevData.getULEB128(&abbrevOffset))) &&
+ (name != 0 || form != 0)) {
+ switch (name) {
+ case llvm::dwarf::DW_AT_name: {
+ if (auto eName = getIndexedString(normalizedFile, form, infoData, offset,
+ strings))
+ tu.name = *eName;
+ else
+ return eName.takeError();
+ break;
+ }
+ case llvm::dwarf::DW_AT_comp_dir: {
+ if (auto eName = getIndexedString(normalizedFile, form, infoData, offset,
+ strings))
+ tu.path = *eName;
+ else
+ return eName.takeError();
+ break;
+ }
+ default:
+ llvm::DWARFFormValue::skipValue(form, infoData, &offset, version,
+ addrSize, Format);
+ }
+ }
+ return tu;
+}
+
+llvm::Error parseDebugInfo(MachOFile &file,
+ const NormalizedFile &normalizedFile, bool copyRefs) {
+
+ // Find the interesting debug info sections.
+ const Section *debugInfo = nullptr;
+ const Section *debugAbbrev = nullptr;
+ const Section *debugStrings = nullptr;
+
+ for (auto &s : normalizedFile.sections) {
+ if (s.segmentName == "__DWARF") {
+ if (s.sectionName == "__debug_info")
+ debugInfo = &s;
+ else if (s.sectionName == "__debug_abbrev")
+ debugAbbrev = &s;
+ else if (s.sectionName == "__debug_str")
+ debugStrings = &s;
+ }
+ }
+
+ if (!debugInfo)
+ return parseStabs(file, normalizedFile, copyRefs);
+
+ if (debugInfo->content.size() == 0)
+ return llvm::Error::success();
+
+ if (debugInfo->content.size() < 12)
+ return llvm::make_error<GenericError>("Malformed __debug_info section in " +
+ file.path() + ": too small");
+
+ if (!debugAbbrev)
+ return llvm::make_error<GenericError>("Missing __dwarf_abbrev section in " +
+ file.path());
+
+ if (auto tuOrErr = readCompUnit(normalizedFile, *debugInfo, *debugAbbrev,
+ *debugStrings, file.path())) {
+ // FIXME: Kill of allocator and code under 'copyRefs' when we fix YAML
+ // memory ownership.
+ std::unique_ptr<BumpPtrAllocator> allocator;
+ if (copyRefs) {
+ allocator = llvm::make_unique<BumpPtrAllocator>();
+ tuOrErr->name = copyDebugString(tuOrErr->name, *allocator);
+ tuOrErr->path = copyDebugString(tuOrErr->path, *allocator);
+ }
+ file.setDebugInfo(llvm::make_unique<DwarfDebugInfo>(std::move(*tuOrErr)));
+ if (copyRefs)
+ file.debugInfo()->setAllocator(std::move(allocator));
+ } else
+ return tuOrErr.takeError();
+
+ return llvm::Error::success();
+}
+
static int64_t readSPtr(bool is64, bool isBig, const uint8_t *addr) {
if (is64)
return read64(addr, isBig);
@@ -712,7 +1015,7 @@ static llvm::Error processAugmentationString(const uint8_t *augStr,
if (augStr[0] == '\0') {
len = 1;
- return llvm::Error();
+ return llvm::Error::success();
}
if (augStr[0] != 'z')
@@ -762,7 +1065,7 @@ static llvm::Error processAugmentationString(const uint8_t *augStr,
cieInfo._augmentationDataLength = offsetInAugmentationData;
len = idx + 1;
- return llvm::Error();
+ return llvm::Error::success();
}
static llvm::Error processCIE(const NormalizedFile &normalizedFile,
@@ -853,7 +1156,7 @@ static llvm::Error processCIE(const NormalizedFile &normalizedFile,
const MachODefinedAtom *func = nullptr;
Reference::Addend addend;
func = findAtomCoveringAddress(normalizedFile, file, funcAddress,
- &addend);
+ addend);
atom->addReference(Reference::KindNamespace::mach_o, handler.kindArch(),
handler.unwindRefToPersonalityFunctionKind(),
PersonalityFunctionField, func, addend);
@@ -867,7 +1170,7 @@ static llvm::Error processCIE(const NormalizedFile &normalizedFile,
cieInfos[atom] = std::move(cieInfo);
- return llvm::Error();
+ return llvm::Error::success();
}
static llvm::Error processFDE(const NormalizedFile &normalizedFile,
@@ -936,7 +1239,7 @@ static llvm::Error processFDE(const NormalizedFile &normalizedFile,
}
Reference::Addend addend;
auto *target = findAtomCoveringAddress(normalizedFile, file,
- targetAddress, &addend);
+ targetAddress, addend);
atom->addReference(Reference::KindNamespace::mach_o, handler.kindArch(),
refKind, refAddress, target, addend);
@@ -1011,7 +1314,7 @@ static llvm::Error processFDE(const NormalizedFile &normalizedFile,
}
}
- return llvm::Error();
+ return llvm::Error::success();
}
llvm::Error addEHFrameReferences(const NormalizedFile &normalizedFile,
@@ -1028,9 +1331,9 @@ llvm::Error addEHFrameReferences(const NormalizedFile &normalizedFile,
// No __eh_frame so nothing to do.
if (!ehFrameSection)
- return llvm::Error();
+ return llvm::Error::success();
- llvm::Error ehFrameErr;
+ llvm::Error ehFrameErr = llvm::Error::success();
CIEInfoMap cieInfos;
file.eachAtomInSection(*ehFrameSection,
@@ -1092,10 +1395,9 @@ llvm::Error parseObjCImageInfo(const Section &sect,
file.setSwiftVersion((flags >> 8) & 0xFF);
- return llvm::Error();
+ return llvm::Error::success();
}
-
/// Converts normalized mach-o file into an lld::File and lld::Atoms.
llvm::Expected<std::unique_ptr<lld::File>>
objectToAtoms(const NormalizedFile &normalizedFile, StringRef path,
@@ -1135,11 +1437,11 @@ normalizedObjectToAtoms(MachOFile *file,
// Create atoms from each section.
for (auto &sect : normalizedFile.sections) {
- DEBUG(llvm::dbgs() << "Creating atoms: "; sect.dump());
+
+ // If this is a debug-info section parse it specially.
if (isDebugInfoSection(sect))
continue;
-
// If the file contains an objc_image_info struct, then we should parse the
// ObjC flags and Swift version.
if (isObjCImageInfo(sect)) {
@@ -1248,7 +1550,11 @@ normalizedObjectToAtoms(MachOFile *file,
for (const DefinedAtom* defAtom : file->defined()) {
reinterpret_cast<const SimpleDefinedAtom*>(defAtom)->sortReferences();
}
- return llvm::Error();
+
+ if (auto err = parseDebugInfo(*file, normalizedFile, copyRefs))
+ return err;
+
+ return llvm::Error::success();
}
llvm::Error
@@ -1279,7 +1585,7 @@ normalizedDylibToAtoms(MachODylibFile *file,
if (dep.kind == llvm::MachO::LC_REEXPORT_DYLIB)
file->addReExportedDylib(dep.path);
}
- return llvm::Error();
+ return llvm::Error::success();
}
void relocatableSectionInfoForContentType(DefinedAtom::ContentType atomType,
@@ -1324,14 +1630,6 @@ normalizedToAtoms(const NormalizedFile &normalizedFile, StringRef path,
}
}
-#ifndef NDEBUG
-void Section::dump(llvm::raw_ostream &OS) const {
- OS << "Section (\"" << segmentName << ", " << sectionName << "\"";
- OS << ", addr: " << llvm::format_hex(address, 16, true);
- OS << ", size: " << llvm::format_hex(content.size(), 8, true) << ")\n";
-}
-#endif
-
} // namespace normalized
} // namespace mach_o
} // namespace lld
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp
index 66be77173983..218170965eca 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp
@@ -234,6 +234,8 @@ struct ScalarBitSetTraits<SectionAttr> {
llvm::MachO::S_ATTR_EXT_RELOC);
io.bitSetCase(value, "S_ATTR_LOC_RELOC",
llvm::MachO::S_ATTR_LOC_RELOC);
+ io.bitSetCase(value, "S_ATTR_DEBUG",
+ llvm::MachO::S_ATTR_DEBUG);
}
};
diff --git a/lib/ReaderWriter/MachO/ObjCPass.cpp b/lib/ReaderWriter/MachO/ObjCPass.cpp
index ba24b3fecdf4..4712d8ca969c 100644
--- a/lib/ReaderWriter/MachO/ObjCPass.cpp
+++ b/lib/ReaderWriter/MachO/ObjCPass.cpp
@@ -103,7 +103,7 @@ public:
// Add the image info.
mergedFile.addAtom(*getImageInfo());
- return llvm::Error();
+ return llvm::Error::success();
}
private:
diff --git a/lib/ReaderWriter/MachO/ShimPass.cpp b/lib/ReaderWriter/MachO/ShimPass.cpp
index cd5367146658..ff559d70eabe 100644
--- a/lib/ReaderWriter/MachO/ShimPass.cpp
+++ b/lib/ReaderWriter/MachO/ShimPass.cpp
@@ -66,7 +66,7 @@ public:
}
// Exit early if no shims needed.
if (_targetToShim.empty())
- return llvm::Error();
+ return llvm::Error::success();
// Sort shim atoms so the layout order is stable.
std::vector<const DefinedAtom *> shims;
@@ -83,7 +83,7 @@ public:
for (const DefinedAtom *shim : shims)
mergedFile.addAtom(*shim);
- return llvm::Error();
+ return llvm::Error::success();
}
private:
diff --git a/lib/ReaderWriter/MachO/StubsPass.cpp b/lib/ReaderWriter/MachO/StubsPass.cpp
index d53b78b24d14..19e2bc592f5c 100644
--- a/lib/ReaderWriter/MachO/StubsPass.cpp
+++ b/lib/ReaderWriter/MachO/StubsPass.cpp
@@ -218,7 +218,7 @@ public:
llvm::Error perform(SimpleFile &mergedFile) override {
// Skip this pass if output format uses text relocations instead of stubs.
if (!this->noTextRelocs())
- return llvm::Error();
+ return llvm::Error::success();
// Scan all references in all atoms.
for (const DefinedAtom *atom : mergedFile.defined()) {
@@ -245,7 +245,7 @@ public:
// Exit early if no stubs needed.
if (_targetToUses.empty())
- return llvm::Error();
+ return llvm::Error::success();
// First add help-common and GOT slots used by lazy binding.
SimpleDefinedAtom *helperCommonAtom =
@@ -323,7 +323,7 @@ public:
lazyOffset += target->name().size() + 12;
}
- return llvm::Error();
+ return llvm::Error::success();
}
private:
diff --git a/lib/ReaderWriter/MachO/TLVPass.cpp b/lib/ReaderWriter/MachO/TLVPass.cpp
index 7a8496c20a4e..e362e507ebf2 100644
--- a/lib/ReaderWriter/MachO/TLVPass.cpp
+++ b/lib/ReaderWriter/MachO/TLVPass.cpp
@@ -107,7 +107,7 @@ private:
for (const TLVPEntryAtom *slot : entries)
mergedFile.addAtom(*slot);
- return llvm::Error();
+ return llvm::Error::success();
}
const DefinedAtom *makeTLVPEntry(const Atom *target) {
diff --git a/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp b/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp
index ee2a9ec10883..59ca43079a6d 100644
--- a/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp
+++ b/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp
@@ -249,6 +249,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(ArchMember)
LLVM_YAML_IS_SEQUENCE_VECTOR(const lld::Reference *)
// Always write DefinedAtoms content bytes as a flow sequence.
LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(ImplicitHex8)
+
// for compatibility with gcc-4.7 in C++11 mode, add extra namespace
namespace llvm {
namespace yaml {
@@ -567,10 +568,10 @@ template <> struct DocumentListTraits<std::vector<const lld::File *> > {
// YAML conversion for const lld::File*
template <> struct MappingTraits<const lld::File *> {
-
class NormArchiveFile : public lld::ArchiveLibraryFile {
public:
- NormArchiveFile(IO &io) : ArchiveLibraryFile(""), _path() {}
+ NormArchiveFile(IO &io) : ArchiveLibraryFile("") {}
+
NormArchiveFile(IO &io, const lld::File *file)
: ArchiveLibraryFile(file->path()), _path(file->path()) {
// If we want to support writing archives, this constructor would
@@ -627,6 +628,7 @@ template <> struct MappingTraits<const lld::File *> {
_undefinedAtomsRef(_undefinedAtoms._atoms),
_sharedLibraryAtomsRef(_sharedLibraryAtoms._atoms),
_absoluteAtomsRef(_absoluteAtoms._atoms) {}
+
NormalizedFile(IO &io, const lld::File *file)
: File(file->path(), kindNormalizedObject), _io(io),
_rnb(new RefNameBuilder(*file)), _path(file->path()),
@@ -673,7 +675,7 @@ template <> struct MappingTraits<const lld::File *> {
}
IO &_io;
- std::unique_ptr<RefNameBuilder> _rnb;
+ std::unique_ptr<RefNameBuilder> _rnb;
StringRef _path;
AtomList<lld::DefinedAtom> _definedAtoms;
AtomList<lld::UndefinedAtom> _undefinedAtoms;
@@ -732,13 +734,12 @@ template <> struct MappingTraits<const lld::File *> {
// YAML conversion for const lld::Reference*
template <> struct MappingTraits<const lld::Reference *> {
-
class NormalizedReference : public lld::Reference {
public:
NormalizedReference(IO &io)
: lld::Reference(lld::Reference::KindNamespace::all,
lld::Reference::KindArch::all, 0),
- _target(nullptr), _targetName(), _offset(0), _addend(0), _tag(0) {}
+ _target(nullptr), _offset(0), _addend(0), _tag(0) {}
NormalizedReference(IO &io, const lld::Reference *ref)
: lld::Reference(ref->kindNamespace(), ref->kindArch(),
@@ -768,6 +769,7 @@ template <> struct MappingTraits<const lld::Reference *> {
setKindValue(_mappedKind.value);
return this;
}
+
void bind(const RefNameResolver &);
static StringRef targetName(IO &io, const lld::Reference *ref);
@@ -804,13 +806,13 @@ template <> struct MappingTraits<const lld::DefinedAtom *> {
class NormalizedAtom : public lld::DefinedAtom {
public:
NormalizedAtom(IO &io)
- : _file(fileFromContext(io)), _name(), _refName(), _contentType(),
- _alignment(1), _content(), _references() {
+ : _file(fileFromContext(io)), _contentType(), _alignment(1) {
static uint32_t ordinalCounter = 1;
_ordinal = ordinalCounter++;
}
+
NormalizedAtom(IO &io, const lld::DefinedAtom *atom)
- : _file(fileFromContext(io)), _name(atom->name()), _refName(),
+ : _file(fileFromContext(io)), _name(atom->name()),
_scope(atom->scope()), _interpose(atom->interposable()),
_merge(atom->merge()), _contentType(atom->contentType()),
_alignment(atom->alignment()), _sectionChoice(atom->sectionChoice()),
@@ -991,11 +993,10 @@ template <> struct MappingTraits<lld::DefinedAtom *> {
// YAML conversion for const lld::UndefinedAtom*
template <> struct MappingTraits<const lld::UndefinedAtom *> {
-
class NormalizedAtom : public lld::UndefinedAtom {
public:
NormalizedAtom(IO &io)
- : _file(fileFromContext(io)), _name(), _canBeNull(canBeNullNever) {}
+ : _file(fileFromContext(io)), _canBeNull(canBeNullNever) {}
NormalizedAtom(IO &io, const lld::UndefinedAtom *atom)
: _file(fileFromContext(io)), _name(atom->name()),
@@ -1059,8 +1060,9 @@ template <> struct MappingTraits<const lld::SharedLibraryAtom *> {
class NormalizedAtom : public lld::SharedLibraryAtom {
public:
NormalizedAtom(IO &io)
- : _file(fileFromContext(io)), _name(), _loadName(), _canBeNull(false),
+ : _file(fileFromContext(io)), _canBeNull(false),
_type(Type::Unknown), _size(0) {}
+
NormalizedAtom(IO &io, const lld::SharedLibraryAtom *atom)
: _file(fileFromContext(io)), _name(atom->name()),
_loadName(atom->loadName()), _canBeNull(atom->canBeNullAtRuntime()),
@@ -1133,11 +1135,11 @@ template <> struct MappingTraits<lld::SharedLibraryAtom *> {
// YAML conversion for const lld::AbsoluteAtom*
template <> struct MappingTraits<const lld::AbsoluteAtom *> {
-
class NormalizedAtom : public lld::AbsoluteAtom {
public:
NormalizedAtom(IO &io)
- : _file(fileFromContext(io)), _name(), _scope(), _value(0) {}
+ : _file(fileFromContext(io)), _scope(), _value(0) {}
+
NormalizedAtom(IO &io, const lld::AbsoluteAtom *atom)
: _file(fileFromContext(io)), _name(atom->name()),
_scope(atom->scope()), _value(atom->value()) {}
@@ -1158,6 +1160,7 @@ template <> struct MappingTraits<const lld::AbsoluteAtom *> {
<< ", " << _name.size() << ")\n");
return this;
}
+
// Extract current File object from YAML I/O parsing context
const lld::File &fileFromContext(IO &io) {
YamlContext *info = reinterpret_cast<YamlContext *>(io.getContext());
@@ -1309,7 +1312,7 @@ public:
const lld::File *fileRef = &file;
yout << fileRef;
- return llvm::Error();
+ return llvm::Error::success();
}
private:
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 88cd128fc4c7..678880b7ff38 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -20,9 +20,9 @@ configure_lit_site_cfg(
)
set(LLD_TEST_DEPS
- FileCheck not llvm-ar llvm-as llvm-dis llvm-nm llc
- lld llvm-config llvm-objdump llvm-readobj yaml2obj obj2yaml
- llvm-mc llvm-nm llvm-lib
+ FileCheck not llvm-ar llvm-as llvm-dis llvm-dwarfdump llvm-nm
+ llc lld llvm-config llvm-objdump llvm-readobj yaml2obj obj2yaml
+ llvm-mc llvm-lib llvm-pdbdump opt
)
if (LLVM_INCLUDE_TESTS)
set(LLD_TEST_DEPS ${LLD_TEST_DEPS} LLDUnitTests)
diff --git a/test/COFF/Inputs/cl-gl.obj b/test/COFF/Inputs/cl-gl.obj
new file mode 100755
index 000000000000..ff746557d412
--- /dev/null
+++ b/test/COFF/Inputs/cl-gl.obj
Binary files differ
diff --git a/test/COFF/Inputs/far-arm-thumb-abs.s b/test/COFF/Inputs/far-arm-thumb-abs.s
new file mode 100644
index 000000000000..9f1b59a55f8c
--- /dev/null
+++ b/test/COFF/Inputs/far-arm-thumb-abs.s
@@ -0,0 +1,2 @@
+.global too_far1
+too_far1 = 0x1401004
diff --git a/test/COFF/Inputs/lto-lazy-reference-dummy.ll b/test/COFF/Inputs/lto-lazy-reference-dummy.ll
new file mode 100644
index 000000000000..0309f89c38f4
--- /dev/null
+++ b/test/COFF/Inputs/lto-lazy-reference-dummy.ll
@@ -0,0 +1,6 @@
+target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32"
+target triple = "i686-pc-windows-msvc18.0.0"
+
+define void @dummy() {
+ ret void
+}
diff --git a/test/COFF/Inputs/lto-lazy-reference-quadruple.ll b/test/COFF/Inputs/lto-lazy-reference-quadruple.ll
new file mode 100644
index 000000000000..99c1d2bdbc5e
--- /dev/null
+++ b/test/COFF/Inputs/lto-lazy-reference-quadruple.ll
@@ -0,0 +1,16 @@
+target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32"
+target triple = "i686-pc-windows-msvc18.0.0"
+
+define double @quadruple(double %x) {
+entry:
+ ; The symbol __real@40800000 is used to materialize the 4.0 constant.
+ %mul = fmul double %x, 4.0
+ ret double %mul
+}
+
+
+declare void @dummy()
+define void @f() {
+ call void @dummy()
+ ret void
+}
diff --git a/test/COFF/arm-thumb-branch-error.s b/test/COFF/arm-thumb-branch-error.s
new file mode 100644
index 000000000000..00b835c58a15
--- /dev/null
+++ b/test/COFF/arm-thumb-branch-error.s
@@ -0,0 +1,10 @@
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-windows-gnu %s -o %t
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-windows-gnu %S/Inputs/far-arm-thumb-abs.s -o %tfar
+// RUN: not lld-link -entry:_start -subsystem:console %t %tfar -out:%t2 2>&1 | FileCheck %s
+// REQUIRES: arm
+ .syntax unified
+ .globl _start
+_start:
+ bl too_far1
+
+// CHECK: relocation out of range
diff --git a/test/COFF/cl-gl.test b/test/COFF/cl-gl.test
new file mode 100644
index 000000000000..306115094771
--- /dev/null
+++ b/test/COFF/cl-gl.test
@@ -0,0 +1,4 @@
+# RUN: not lld-link /out:%t.exe /entry:main %S/Inputs/cl-gl.obj >& %t.log
+# RUN: FileCheck %s < %t.log
+
+# CHECK: is not a native COFF file. Recompile without /GL
diff --git a/test/COFF/common.test b/test/COFF/common.test
index d57ced457d10..007cfcfb6f92 100644
--- a/test/COFF/common.test
+++ b/test/COFF/common.test
@@ -7,7 +7,7 @@
# CHECK: 3005: b8 04 10 00 40
# CHECK: 300a: b8 20 10 00 40
# CHECK: 300f: b8 60 10 00 40
-# CHECK: 3014: b8 80 10 00 40
+# CHECK: 3014: b8 70 10 00 40
--- !COFF
header:
diff --git a/test/COFF/conflict-mangled.test b/test/COFF/conflict-mangled.test
new file mode 100644
index 000000000000..2f028ef6d5fd
--- /dev/null
+++ b/test/COFF/conflict-mangled.test
@@ -0,0 +1,37 @@
+# REQUIRES: system-windows
+# RUN: yaml2obj < %s > %t1.obj
+# RUN: yaml2obj < %s > %t2.obj
+# RUN: not lld-link /out:%t.exe %t1.obj %t2.obj >& %t.log
+# RUN: FileCheck %s < %t.log
+
+# CHECK: duplicate symbol: "int __cdecl mangled(void)" (?mangled@@YAHXZ) in {{.+}}1.obj and in {{.+}}2.obj
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: []
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 000000000000
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 6
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: '?mangled@@YAHXZ'
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/conflict.test b/test/COFF/conflict.test
index 65dda51f498d..a634c7185675 100644
--- a/test/COFF/conflict.test
+++ b/test/COFF/conflict.test
@@ -8,7 +8,7 @@
# RUN: not lld-link /out:%t.exe %t.lto1.obj %t.lto2.obj >& %t.log
# RUN: FileCheck %s < %t.log
-# CHECK: duplicate symbol: foo {{.+}}1.obj and foo {{.+}}2.obj
+# CHECK: duplicate symbol: foo in {{.+}}1.obj and in {{.+}}2.obj
--- !COFF
header:
diff --git a/test/COFF/delayimports32.test b/test/COFF/delayimports32.test
index 328b34504ac5..9c4fcae5b6a5 100644
--- a/test/COFF/delayimports32.test
+++ b/test/COFF/delayimports32.test
@@ -14,7 +14,7 @@ IMPORT-NEXT: Name: std32.dll
IMPORT-NEXT: Attributes: 0x1
IMPORT-NEXT: ModuleHandle: 0x1018
IMPORT-NEXT: ImportAddressTable: 0x1020
-IMPORT-NEXT: ImportNameTable: 0x3040
+IMPORT-NEXT: ImportNameTable: 0x4040
IMPORT-NEXT: BoundDelayImportTable: 0x0
IMPORT-NEXT: UnloadDelayImportTable: 0x0
IMPORT-NEXT: Import {
@@ -71,7 +71,7 @@ BASEREL-NEXT: }
BASEREL-NEXT: ]
DISASM: 202b: 68 20 10 40 00 pushl $4198432
-DISASM-NEXT: 2030: 68 00 30 40 00 pushl $4206592
+DISASM-NEXT: 2030: 68 00 40 40 00 pushl $4210688
DISASM-NEXT: 2035: e8 c6 ff ff ff calll -58 <_main@0>
DISASM-NEXT: 203a: 5a popl %edx
DISASM-NEXT: 203b: 59 popl %ecx
@@ -79,7 +79,7 @@ DISASM-NEXT: 203c: ff e0 jmpl *%eax
DISASM-NEXT: 203e: 51 pushl %ecx
DISASM-NEXT: 203f: 52 pushl %edx
DISASM-NEXT: 2040: 68 24 10 40 00 pushl $4198436
-DISASM-NEXT: 2045: 68 00 30 40 00 pushl $4206592
+DISASM-NEXT: 2045: 68 00 40 40 00 pushl $4210688
DISASM-NEXT: 204a: e8 b1 ff ff ff calll -79 <_main@0>
DISASM-NEXT: 204f: 5a popl %edx
DISASM-NEXT: 2050: 59 popl %ecx
diff --git a/test/COFF/dll.test b/test/COFF/dll.test
index bafd3c233c87..db4b4056552c 100644
--- a/test/COFF/dll.test
+++ b/test/COFF/dll.test
@@ -1,5 +1,4 @@
# REQUIRES: winres
-# REQUIRES: winlib
# RUN: yaml2obj < %p/Inputs/export.yaml > %t.obj
# RUN: lld-link /out:%t.dll /dll %t.obj /export:exportfn1 /export:exportfn2 \
diff --git a/test/COFF/dumppdb.test b/test/COFF/dumppdb.test
new file mode 100644
index 000000000000..787ee2edbeda
--- /dev/null
+++ b/test/COFF/dumppdb.test
@@ -0,0 +1,181 @@
+# RUN: yaml2obj %s > %t.obj
+# RUN: lld-link /debug /pdb:%t.pdb /dumppdb /dll /out:%t.dll /entry:main \
+# RUN: /nodefaultlib %t.obj | FileCheck %s
+
+# CHECK: ArgList (0x1000) {
+# CHECK-NEXT: TypeLeafKind: LF_ARGLIST (0x1201)
+# CHECK-NEXT: NumArgs: 1
+# CHECK-NEXT: Arguments [
+# CHECK-NEXT: ArgType: 0x0
+# CHECK-NEXT: ]
+# CHECK-NEXT: }
+# CHECK-NEXT: Procedure (0x1001) {
+# CHECK-NEXT: TypeLeafKind: LF_PROCEDURE (0x1008)
+# CHECK-NEXT: ReturnType: int (0x74)
+# CHECK-NEXT: CallingConvention: NearC (0x0)
+# CHECK-NEXT: FunctionOptions [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: NumParameters: 0
+# CHECK-NEXT: ArgListType: (<no type>) (0x1000)
+# CHECK-NEXT: }
+# CHECK-NEXT: FuncId (0x1002) {
+# CHECK-NEXT: TypeLeafKind: LF_FUNC_ID (0x1601)
+# CHECK-NEXT: ParentScope: 0x0
+# CHECK-NEXT: FunctionType: int (<no type>) (0x1001)
+# CHECK-NEXT: Name: main
+# CHECK-NEXT: }
+# CHECK-NEXT: StringId (0x1003) {
+# CHECK-NEXT: TypeLeafKind: LF_STRING_ID (0x1605)
+# CHECK-NEXT: Id: 0x0
+# CHECK-NEXT: StringData: D:\b
+# CHECK-NEXT: }
+# CHECK-NEXT: StringId (0x1004) {
+# CHECK-NEXT: TypeLeafKind: LF_STRING_ID (0x1605)
+# CHECK-NEXT: Id: 0x0
+# CHECK-NEXT: StringData: C:\vs14\VC\BIN\amd64\cl.exe
+# CHECK-NEXT: }
+# CHECK-NEXT: StringId (0x1005) {
+# CHECK-NEXT: TypeLeafKind: LF_STRING_ID (0x1605)
+# CHECK-NEXT: Id: 0x0
+# CHECK-NEXT: StringData: -Z7 -c -MT -IC:\vs14\VC\INCLUDE -IC:\vs14\VC\ATLMFC\INCLUDE -I"C:\Program Files (x86)\Windows Kits\10\include\10.0.10150.0\ucrt" -I"C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6\include\um" -I"C:\Program Files (x86)\Windows Kits\8.1\include\shared"
+# CHECK-NEXT: }
+# CHECK-NEXT: StringList (0x1006) {
+# CHECK-NEXT: TypeLeafKind: LF_SUBSTR_LIST (0x1604)
+# CHECK-NEXT: NumArgs: 1
+# CHECK-NEXT: Arguments [
+# CHECK-NEXT: ArgType: -Z7 -c -MT -IC:\vs14\VC\INCLUDE -IC:\vs14\VC\ATLMFC\INCLUDE -I"C:\Program Files (x86)\Windows Kits\10\include\10.0.10150.0\ucrt" -I"C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6\include\um" -I"C:\Program Files (x86)\Windows Kits\8.1\include\shared" (0x1005)
+# CHECK-NEXT: ]
+# CHECK-NEXT: }
+# CHECK-NEXT: StringId (0x1007) {
+# CHECK-NEXT: TypeLeafKind: LF_STRING_ID (0x1605)
+# CHECK-NEXT: Id: (-Z7 -c -MT -IC:\vs14\VC\INCLUDE -IC:\vs14\VC\ATLMFC\INCLUDE -I"C:\Program Files (x86)\Windows Kits\10\include\10.0.10150.0\ucrt" -I"C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6\include\um" -I"C:\Program Files (x86)\Windows Kits\8.1\include\shared") (0x1006)
+# CHECK-NEXT: StringData: -I"C:\Program Files (x86)\Windows Kits\8.1\include\um" -I"C:\Program Files (x86)\Windows Kits\8.1\include\winrt" -TC -X
+# CHECK-NEXT: }
+# CHECK-NEXT: StringId (0x1008) {
+# CHECK-NEXT: TypeLeafKind: LF_STRING_ID (0x1605)
+# CHECK-NEXT: Id: 0x0
+# CHECK-NEXT: StringData: ret42.c
+# CHECK-NEXT: }
+# CHECK-NEXT: StringId (0x1009) {
+# CHECK-NEXT: TypeLeafKind: LF_STRING_ID (0x1605)
+# CHECK-NEXT: Id: 0x0
+# CHECK-NEXT: StringData: D:\b\vc140.pdb
+# CHECK-NEXT: }
+# CHECK-NEXT: BuildInfo (0x100A) {
+# CHECK-NEXT: TypeLeafKind: LF_BUILDINFO (0x1603)
+# CHECK-NEXT: NumArgs: 5
+# CHECK-NEXT: Arguments [
+# CHECK-NEXT: ArgType: D:\b (0x1003)
+# CHECK-NEXT: ArgType: C:\vs14\VC\BIN\amd64\cl.exe (0x1004)
+# CHECK-NEXT: ArgType: ret42.c (0x1008)
+# CHECK-NEXT: ArgType: D:\b\vc140.pdb (0x1009)
+# CHECK-NEXT: ArgType: -I"C:\Program Files (x86)\Windows Kits\8.1\include\um" -I"C:\Program Files (x86)\Windows Kits\8.1\include\winrt" -TC -X (0x1007)
+# CHECK-NEXT: ]
+# CHECK-NEXT: }
+# CHECK-NEXT: UnknownSym {
+# CHECK-NEXT: Kind: 0x0
+# CHECK-NEXT: Length: 6
+# CHECK-NEXT: }
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: .drectve
+ Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
+ Alignment: 1
+ SectionData: 2020202F44454641554C544C49423A224C4942434D5422202F44454641554C544C49423A224F4C444E414D45532220
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ SectionData: 04000000F1000000530000001500011100000000443A5C625C72657434322E6F626A003A003C1100600000D00013000000F259000013000000F25900004D6963726F736F667420285229204F7074696D697A696E6720436F6D70696C65720000F10000004E0000002A00471100000000000000000000000006000000000000000500000002100000000000000000006D61696E001C001210000000000000000000000000000000000000000000000042110002004F110000F2000000200000000000000000000000060000000000000001000000140000000000000001000080F4000000180000000100000010010BFC79AA614B536E3D64B110330D1E580000F30000000E00000000643A5C625C72657434322E63000000F10000000800000006004C110A100000
+ Relocations:
+ - VirtualAddress: 136
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 140
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 192
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 196
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - Name: '.debug$T'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ SectionData: 040000000A00011201000000000000000E0008107400000000000000001000001200011600000000011000006D61696E00F3F2F10E00051600000000443A5C6200F3F2F12200051600000000433A5C767331345C56435C42494E5C616D6436345C636C2E6578650002010516000000002D5A37202D63202D4D54202D49433A5C767331345C56435C494E434C554445202D49433A5C767331345C56435C41544C4D46435C494E434C554445202D4922433A5C50726F6772616D2046696C65732028783836295C57696E646F7773204B6974735C31305C696E636C7564655C31302E302E31303135302E305C7563727422202D4922433A5C50726F6772616D2046696C65732028783836295C57696E646F7773204B6974735C4E4554465853444B5C342E365C696E636C7564655C756D22202D4922433A5C50726F6772616D2046696C65732028783836295C57696E646F7773204B6974735C382E315C696E636C7564655C73686172656422000A00041601000000051000008200051606100000202D4922433A5C50726F6772616D2046696C65732028783836295C57696E646F7773204B6974735C382E315C696E636C7564655C756D22202D4922433A5C50726F6772616D2046696C65732028783836295C57696E646F7773204B6974735C382E315C696E636C7564655C77696E727422202D5443202D5800F3F2F10E0005160000000072657434322E63001600051600000000443A5C625C76633134302E70646200F11A00031605000310000004100000081000000910000007100000F2F1
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: B82A000000C3
+symbols:
+ - Name: '@comp.id'
+ Value: 17062386
+ SectionNumber: -1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: '@feat.00'
+ Value: 2147484048
+ SectionNumber: -1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .drectve
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 47
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 296
+ NumberOfRelocations: 4
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: '.debug$T'
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 576
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 6
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 2139436471
+ Number: 0
+ - Name: main
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/export-exe.test b/test/COFF/export-exe.test
index 7a345160faa9..6d60ce442035 100644
--- a/test/COFF/export-exe.test
+++ b/test/COFF/export-exe.test
@@ -1,4 +1,3 @@
-# REQUIRES: winlib
# RUN: lld-link /entry:main /out:%t.exe /subsystem:windows \
# RUN: %p/Inputs/ret42.obj /export:main
# RUN: llvm-objdump -p %t.exe | FileCheck %s
diff --git a/test/COFF/export.test b/test/COFF/export.test
index 20e069ec3c83..e4a094ce6073 100644
--- a/test/COFF/export.test
+++ b/test/COFF/export.test
@@ -2,7 +2,6 @@
#
# RUN: lld-link /out:%t.dll /dll %t.obj /export:exportfn1 /export:exportfn2
# RUN: llvm-objdump -p %t.dll | FileCheck -check-prefix=CHECK1 %s
-# REQUIRES: winlib
CHECK1: Export Table:
CHECK1: DLL name: export.test.tmp.dll
diff --git a/test/COFF/export32.test b/test/COFF/export32.test
index 86cd27bb9af6..79f5daf62dbf 100644
--- a/test/COFF/export32.test
+++ b/test/COFF/export32.test
@@ -2,7 +2,6 @@
#
# RUN: lld-link /out:%t.dll /dll %t.obj /export:exportfn1 /export:exportfn2
# RUN: llvm-objdump -p %t.dll | FileCheck -check-prefix=CHECK1 %s
-# REQUIRES: winlib
# CHECK1: Export Table:
# CHECK1: DLL name: export32.test.tmp.dll
diff --git a/test/COFF/icf-associative.test b/test/COFF/icf-associative.test
new file mode 100644
index 000000000000..1c63b05d8f22
--- /dev/null
+++ b/test/COFF/icf-associative.test
@@ -0,0 +1,104 @@
+# RUN: yaml2obj < %s > %t.obj
+# RUN: lld-link /entry:foo /out:%t.exe /subsystem:console /include:bar \
+# RUN: /debug /verbose %t.obj > %t.log 2>&1
+# RUN: FileCheck %s < %t.log
+
+# CHECK: Selected foo
+# CHECK: Removed bar
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: []
+sections:
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 4883EC28E8000000004883C428C3
+
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ SectionData: 0000000000000000000000000000
+
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 4883EC28E8000000004883C428C3
+
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ SectionData: FFFFFFFFFFFFFFFFFFFFFFFFFFFF
+
+symbols:
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 14
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 1682752513
+ Number: 0
+ Selection: IMAGE_COMDAT_SELECT_ANY
+
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 14
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 1
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 14
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 1682752513
+ Number: 0
+ Selection: IMAGE_COMDAT_SELECT_ANY
+
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 14
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 3
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+
+ - Name: foo
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+
+ - Name: bar
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/include2.test b/test/COFF/include2.test
index f2379eaede40..528a27319e7b 100644
--- a/test/COFF/include2.test
+++ b/test/COFF/include2.test
@@ -9,6 +9,6 @@
CHECK: include2.test.tmp1.obj
CHECK: include2.test.tmp2.lib
-CHECK: include2.test.tmp3.lib
CHECK: include2.test.tmp2.lib(include2.test.tmp2.obj) for foo
+CHECK: include2.test.tmp3.lib
CHECK: include2.test.tmp3.lib(include2.test.tmp3.obj) for bar
diff --git a/test/COFF/linkrepro.test b/test/COFF/linkrepro.test
new file mode 100644
index 000000000000..b3c13764d81d
--- /dev/null
+++ b/test/COFF/linkrepro.test
@@ -0,0 +1,38 @@
+# cpio fails on windows with "Function not implemented".
+# REQUIRES: shell
+
+# RUN: rm -rf %t.dir
+# RUN: mkdir -p %t.dir/build1 %t.dir/build2 %t.dir/build3
+# RUN: yaml2obj < %p/Inputs/hello32.yaml > %t.obj
+
+# RUN: cd %t.dir/build1
+# RUN: lld-link %t.obj %p/Inputs/std32.lib /subsystem:console \
+# RUN: /entry:main@0 /linkrepro:. /out:%t.exe
+# RUN: cpio -id < repro.cpio
+# RUN: diff %t.obj repro/%:t.obj
+# RUN: diff %p/Inputs/std32.lib repro/%:p/Inputs/std32.lib
+# RUN: FileCheck %s --check-prefix=RSP < repro/response.txt
+
+# RUN: cd %t.dir/build2
+# RUN: lld-link %t.obj /libpath:%p/Inputs /defaultlib:std32 /subsystem:console \
+# RUN: /entry:main@0 /linkrepro:. /out:%t.exe
+# RUN: cpio -id < repro.cpio
+# RUN: diff %t.obj repro/%:t.obj
+# RUN: diff %p/Inputs/std32.lib repro/%:p/Inputs/std32.lib
+# RUN: FileCheck %s --check-prefix=RSP < repro/response.txt
+
+# RUN: cd %t.dir/build3
+# RUN: env LIB=%p/Inputs lld-link %t.obj /defaultlib:std32 /subsystem:console \
+# RUN: /entry:main@0 /linkrepro:. /out:%t.exe
+# RUN: cpio -id < repro.cpio
+# RUN: diff %t.obj repro/%:t.obj
+# RUN: diff %p/Inputs/std32.lib repro/%:p/Inputs/std32.lib
+# RUN: FileCheck %s --check-prefix=RSP < repro/response.txt
+
+# RSP: /subsystem:console
+# RSP: /entry:main@0
+# RSP-NOT: /linkrepro:
+# RSP: /out:
+# RSP: linkrepro.test.tmp.obj
+# RSP-NOT: defaultlib
+# RSP: std32.lib
diff --git a/test/COFF/lldmap.test b/test/COFF/lldmap.test
index a4c2da09ca33..eb61f0e5a6c2 100644
--- a/test/COFF/lldmap.test
+++ b/test/COFF/lldmap.test
@@ -1,6 +1,8 @@
# RUN: yaml2obj < %p/Inputs/ret42.yaml > %t.obj
-# RUN: lld-link /out:%t.exe /entry:main /lldmap:%t.map %t.obj
-# RUN: FileCheck %s < %t.map
+# RUN: lld-link /out:%t.exe /entry:main /lldmap:%T/foo.map %t.obj
+# RUN: FileCheck %s < %T/foo.map
+# RUN: lld-link /out:%T/bar.exe /entry:main /lldmap %t.obj
+# RUN: FileCheck %s < %T/bar.map
# CHECK: .obj:
# CHECK-NEXT: 140001000 .text$mn
diff --git a/test/COFF/lto-lazy-reference.ll b/test/COFF/lto-lazy-reference.ll
new file mode 100644
index 000000000000..22f953975682
--- /dev/null
+++ b/test/COFF/lto-lazy-reference.ll
@@ -0,0 +1,21 @@
+; RUN: llc -mtriple=i686-pc-windows-msvc -filetype=obj -o %T/lto-lazy-reference-quadruple.obj %S/Inputs/lto-lazy-reference-quadruple.ll
+; RUN: llvm-as -o %T/lto-lazy-reference-dummy.bc %S/Inputs/lto-lazy-reference-dummy.ll
+; RUN: rm -f %t.lib
+; RUN: llvm-ar cru %t.lib %T/lto-lazy-reference-quadruple.obj %T/lto-lazy-reference-dummy.bc
+; RUN: llvm-as -o %t.obj %s
+; RUN: lld-link /out:%t.exe /entry:main /subsystem:console %t.obj %t.lib
+
+target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32"
+target triple = "i686-pc-windows-msvc18.0.0"
+
+define double @main(double %x) {
+entry:
+ ; When compiled, this defines the __real@40800000 symbol, which already has a
+ ; lazy definition in the lib file from lto-lazy-reference-quadruple.obj. This
+ ; test makes sure we *don't* try to take the definition from the lazy
+ ; reference, because that can bring in new references to bitcode files after
+ ; LTO, such as lto-lazy-reference-dummy.bc in this case.
+ %mul = fmul double %x, 4.0
+
+ ret double %mul
+}
diff --git a/test/COFF/noentry.test b/test/COFF/noentry.test
index 80f387fe03c8..5e241e46e897 100644
--- a/test/COFF/noentry.test
+++ b/test/COFF/noentry.test
@@ -1,5 +1,4 @@
# REQUIRES: winres
-# REQUIRES: winlib
# RUN: yaml2obj < %p/Inputs/export.yaml > %t.obj
# RUN: lld-link /out:%t.dll /dll %t.obj
diff --git a/test/COFF/order.test b/test/COFF/order.test
index 6a0cee8a32ea..bb0a6e5740c0 100644
--- a/test/COFF/order.test
+++ b/test/COFF/order.test
@@ -9,7 +9,7 @@
# RUN: FileCheck %s < %t.log
CHECK: order.test.tmp1.obj
-CHECK: order.test.tmp3.obj
CHECK: order.test.tmp2.lib
+CHECK: order.test.tmp3.obj
CHECK: order.test.tmp3.lib
CHECK: order.test.tmp2.lib(order.test.tmp2.obj) for foo
diff --git a/test/COFF/pdb.test b/test/COFF/pdb.test
new file mode 100644
index 000000000000..af174b7a2038
--- /dev/null
+++ b/test/COFF/pdb.test
@@ -0,0 +1,421 @@
+# RUN: yaml2obj %s > %t.obj
+# RUN: lld-link /debug /pdb:%t.pdb /dll /out:%t.dll /entry:main /nodefaultlib \
+# RUN: /debugpdb %t.obj
+# RUN: llvm-pdbdump pdb2yaml -stream-metadata -stream-directory -pdb-stream \
+# RUN: -dbi-stream -ipi-stream -tpi-stream %t.pdb | FileCheck %s
+
+# RUN: llvm-pdbdump raw -modules -section-map -section-headers -section-contribs \
+# RUN: -tpi-records %t.pdb | FileCheck -check-prefix RAW %s
+
+# CHECK: MSF:
+# CHECK-NEXT: SuperBlock:
+# CHECK-NEXT: BlockSize: 4096
+# CHECK-NEXT: FreeBlockMap:
+# CHECK-NEXT: NumBlocks: 10
+# CHECK-NEXT: NumDirectoryBytes: 48
+# CHECK-NEXT: Unknown1: 0
+# CHECK-NEXT: BlockMapAddr: 3
+# CHECK-NEXT: NumDirectoryBlocks: 1
+# CHECK-NEXT: DirectoryBlocks: [ 9 ]
+# CHECK-NEXT: NumStreams: 6
+# CHECK-NEXT: FileSize: 40960
+# CHECK-NEXT: StreamSizes: [ 0, 48, 628, 266, 56, 80 ]
+# CHECK-NEXT: StreamMap:
+# CHECK-NEXT: - Stream: [ ]
+# CHECK-NEXT: - Stream: [ 5 ]
+# CHECK-NEXT: - Stream: [ 7 ]
+# CHECK-NEXT: - Stream: [ 6 ]
+# CHECK-NEXT: - Stream: [ 8 ]
+# CHECK-NEXT: - Stream: [ 4 ]
+# CHECK-NEXT: PdbStream:
+# CHECK-NEXT: Age: 1
+# CHECK-NEXT: Guid: '{00000000-0000-0000-0000-000000000000}'
+# CHECK-NEXT: Signature: 0
+# CHECK-NEXT: Version: VC70
+# CHECK-NEXT: NamedStreams:
+# CHECK-NEXT: DbiStream:
+# CHECK-NEXT: VerHeader: V110
+# CHECK-NEXT: Age: 1
+# CHECK-NEXT: BuildNumber: 0
+# CHECK-NEXT: PdbDllVersion: 0
+# CHECK-NEXT: PdbDllRbld: 0
+# CHECK-NEXT: Flags: 0
+# CHECK-NEXT: MachineType: x86
+# CHECK-NEXT: TpiStream:
+# CHECK-NEXT: Version: VC80
+# CHECK-NEXT: Records:
+# CHECK-NEXT: - Kind: LF_ARGLIST
+# CHECK-NEXT: ArgList:
+# CHECK-NEXT: ArgIndices: [ 0 ]
+# CHECK-NEXT: - Kind: LF_PROCEDURE
+# CHECK-NEXT: Procedure:
+# CHECK-NEXT: ReturnType: 116
+# CHECK-NEXT: CallConv: NearC
+# CHECK-NEXT: Options: [ None ]
+# CHECK-NEXT: ParameterCount: 0
+# CHECK-NEXT: ArgumentList: 4096
+# CHECK-NEXT: - Kind: LF_FUNC_ID
+# CHECK-NEXT: FuncId:
+# CHECK-NEXT: ParentScope: 0
+# CHECK-NEXT: FunctionType: 4097
+# CHECK-NEXT: Name: main
+# CHECK-NEXT: - Kind: LF_STRING_ID
+# CHECK-NEXT: StringId:
+# CHECK-NEXT: Id: 0
+# CHECK-NEXT: String: 'D:\b'
+# CHECK-NEXT: - Kind: LF_STRING_ID
+# CHECK-NEXT: StringId:
+# CHECK-NEXT: Id: 0
+# CHECK-NEXT: String: 'C:\vs14\VC\BIN\amd64\cl.exe'
+# CHECK-NEXT: - Kind: LF_STRING_ID
+# CHECK-NEXT: StringId:
+# CHECK-NEXT: Id: 0
+# CHECK-NEXT: String: '-Z7 -c -MT -IC:\vs14\VC\INCLUDE -IC:\vs14\VC\ATLMFC\INCLUDE -I"C:\Program Files (x86)\Windows Kits\10\include\10.0.10150.0\ucrt" -I"C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6\include\um" -I"C:\Program Files (x86)\Windows Kits\8.1\include\shared"'
+# CHECK-NEXT: - Kind: LF_SUBSTR_LIST
+# CHECK-NEXT: ArgList:
+# CHECK-NEXT: ArgIndices: [ 4101 ]
+# CHECK-NEXT: - Kind: LF_STRING_ID
+# CHECK-NEXT: StringId:
+# CHECK-NEXT: Id: 4102
+# CHECK-NEXT: String: ' -I"C:\Program Files (x86)\Windows Kits\8.1\include\um" -I"C:\Program Files (x86)\Windows Kits\8.1\include\winrt" -TC -X'
+# CHECK-NEXT: - Kind: LF_STRING_ID
+# CHECK-NEXT: StringId:
+# CHECK-NEXT: Id: 0
+# CHECK-NEXT: String: ret42.c
+# CHECK-NEXT: - Kind: LF_STRING_ID
+# CHECK-NEXT: StringId:
+# CHECK-NEXT: Id: 0
+# CHECK-NEXT: String: 'D:\b\vc140.pdb'
+# CHECK-NEXT: - Kind: LF_BUILDINFO
+# CHECK-NEXT: BuildInfo:
+# CHECK-NEXT: ArgIndices: [ 4099, 4100, 4104, 4105, 4103 ]
+# CHECK-NEXT: IpiStream:
+# CHECK-NEXT: Version: VC80
+# CHECK-NEXT: Records:
+# CHECK-NEXT: ...
+
+# RAW: Type Info Stream (TPI) {
+# RAW-NEXT: TPI Version: 20040203
+# RAW-NEXT: Record count: 11
+# RAW-NEXT: Records [
+# RAW-NEXT: {
+# RAW-NEXT: ArgList (0x1000) {
+# RAW-NEXT: TypeLeafKind: LF_ARGLIST (0x1201)
+# RAW-NEXT: NumArgs: 1
+# RAW-NEXT: Arguments [
+# RAW-NEXT: ArgType: 0x0
+# RAW-NEXT: ]
+# RAW-NEXT: }
+# RAW-NEXT: }
+# RAW-NEXT: {
+# RAW-NEXT: Procedure (0x1001) {
+# RAW-NEXT: TypeLeafKind: LF_PROCEDURE (0x1008)
+# RAW-NEXT: ReturnType: int (0x74)
+# RAW-NEXT: CallingConvention: NearC (0x0)
+# RAW-NEXT: FunctionOptions [ (0x0)
+# RAW-NEXT: ]
+# RAW-NEXT: NumParameters: 0
+# RAW-NEXT: ArgListType: (<no type>) (0x1000)
+# RAW-NEXT: }
+# RAW-NEXT: }
+# RAW-NEXT: {
+# RAW-NEXT: FuncId (0x1002) {
+# RAW-NEXT: TypeLeafKind: LF_FUNC_ID (0x1601)
+# RAW-NEXT: ParentScope: 0x0
+# RAW-NEXT: FunctionType: int (<no type>) (0x1001)
+# RAW-NEXT: Name: main
+# RAW-NEXT: }
+# RAW-NEXT: }
+# RAW-NEXT: {
+# RAW-NEXT: StringId (0x1003) {
+# RAW-NEXT: TypeLeafKind: LF_STRING_ID (0x1605)
+# RAW-NEXT: Id: 0x0
+# RAW-NEXT: StringData: D:\b
+# RAW-NEXT: }
+# RAW-NEXT: }
+# RAW-NEXT: {
+# RAW-NEXT: StringId (0x1004) {
+# RAW-NEXT: TypeLeafKind: LF_STRING_ID (0x1605)
+# RAW-NEXT: Id: 0x0
+# RAW-NEXT: StringData: C:\vs14\VC\BIN\amd64\cl.exe
+# RAW-NEXT: }
+# RAW-NEXT: }
+# RAW-NEXT: {
+# RAW-NEXT: StringId (0x1005) {
+# RAW-NEXT: TypeLeafKind: LF_STRING_ID (0x1605)
+# RAW-NEXT: Id: 0x0
+# RAW-NEXT: StringData: -Z7 -c -MT -IC:\vs14\VC\INCLUDE -IC:\vs14\VC\ATLMFC\INCLUDE -I"C:\Program Files (x86)\Windows Kits\10\include\10.0.10150.0\ucrt" -I"C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6\include\um" -I"C:\Program Files (x86)\Windows Kits\8.1\include\shared"
+# RAW-NEXT: }
+# RAW-NEXT: }
+# RAW-NEXT: {
+# RAW-NEXT: StringList (0x1006) {
+# RAW-NEXT: TypeLeafKind: LF_SUBSTR_LIST (0x1604)
+# RAW-NEXT: NumArgs: 1
+# RAW-NEXT: Arguments [
+# RAW-NEXT: ArgType: -Z7 -c -MT -IC:\vs14\VC\INCLUDE -IC:\vs14\VC\ATLMFC\INCLUDE -I"C:\Program Files (x86)\Windows Kits\10\include\10.0.10150.0\ucrt" -I"C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6\include\um" -I"C:\Program Files (x86)\Windows Kits\8.1\include\shared" (0x1005)
+# RAW-NEXT: ]
+# RAW-NEXT: }
+# RAW-NEXT: }
+# RAW-NEXT: {
+# RAW-NEXT: StringId (0x1007) {
+# RAW-NEXT: TypeLeafKind: LF_STRING_ID (0x1605)
+# RAW-NEXT: Id: (-Z7 -c -MT -IC:\vs14\VC\INCLUDE -IC:\vs14\VC\ATLMFC\INCLUDE -I"C:\Program Files (x86)\Windows Kits\10\include\10.0.10150.0\ucrt" -I"C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6\include\um" -I"C:\Program Files (x86)\Windows Kits\8.1\include\shared") (0x1006)
+# RAW-NEXT: StringData: -I"C:\Program Files (x86)\Windows Kits\8.1\include\um" -I"C:\Program Files (x86)\Windows Kits\8.1\include\winrt" -TC -X
+# RAW-NEXT: }
+# RAW-NEXT: }
+# RAW-NEXT: {
+# RAW-NEXT: StringId (0x1008) {
+# RAW-NEXT: TypeLeafKind: LF_STRING_ID (0x1605)
+# RAW-NEXT: Id: 0x0
+# RAW-NEXT: StringData: ret42.c
+# RAW-NEXT: }
+# RAW-NEXT: }
+# RAW-NEXT: {
+# RAW-NEXT: StringId (0x1009) {
+# RAW-NEXT: TypeLeafKind: LF_STRING_ID (0x1605)
+# RAW-NEXT: Id: 0x0
+# RAW-NEXT: StringData: D:\b\vc140.pdb
+# RAW-NEXT: }
+# RAW-NEXT: }
+# RAW-NEXT: {
+# RAW-NEXT: BuildInfo (0x100A) {
+# RAW-NEXT: TypeLeafKind: LF_BUILDINFO (0x1603)
+# RAW-NEXT: NumArgs: 5
+# RAW-NEXT: Arguments [
+# RAW-NEXT: ArgType: D:\b (0x1003)
+# RAW-NEXT: ArgType: C:\vs14\VC\BIN\amd64\cl.exe (0x1004)
+# RAW-NEXT: ArgType: ret42.c (0x1008)
+# RAW-NEXT: ArgType: D:\b\vc140.pdb (0x1009)
+# RAW-NEXT: ArgType: -I"C:\Program Files (x86)\Windows Kits\8.1\include\um" -I"C:\Program Files (x86)\Windows Kits\8.1\include\winrt" -TC -X (0x1007)
+# RAW-NEXT: ]
+# RAW-NEXT: }
+# RAW-NEXT: }
+# RAW-NEXT: ]
+# RAW-NEXT: }
+# RAW-NEXT: DBI Stream {
+# RAW-NEXT: Dbi Version: 20091201
+# RAW-NEXT: Age: 1
+# RAW-NEXT: Incremental Linking: No
+# RAW-NEXT: Has CTypes: No
+# RAW-NEXT: Is Stripped: No
+# RAW-NEXT: Machine Type: x86
+# RAW-NEXT: Symbol Record Stream Index: 65535
+# RAW-NEXT: Public Symbol Stream Index: 65535
+# RAW-NEXT: Global Symbol Stream Index: 65535
+# RAW-NEXT: Toolchain Version: 0.0
+# RAW-NEXT: mspdb00.dll version: 0.0.0
+# RAW-NEXT: Modules [
+# RAW-NEXT: {
+# RAW-NEXT: Name: * Linker *
+# RAW-NEXT: Debug Stream Index: 65535
+# RAW-NEXT: Object File Name:
+# RAW-NEXT: Num Files: 0
+# RAW-NEXT: Source File Name Idx: 0
+# RAW-NEXT: Pdb File Name Idx: 0
+# RAW-NEXT: Line Info Byte Size: 0
+# RAW-NEXT: C13 Line Info Byte Size: 0
+# RAW-NEXT: Symbol Byte Size: 0
+# RAW-NEXT: Type Server Index: 0
+# RAW-NEXT: Has EC Info: No
+# RAW-NEXT: }
+# RAW-NEXT: ]
+# RAW-NEXT: }
+# RAW-NEXT: Section Contributions [
+# RAW-NEXT: Contribution {
+# RAW-NEXT: ISect: 0
+# RAW-NEXT: Off: 1140
+# RAW-NEXT: Size: 6
+# RAW-NEXT: Characteristics [ (0x60500020)
+# RAW-NEXT: IMAGE_SCN_ALIGN_16BYTES (0x500000)
+# RAW-NEXT: IMAGE_SCN_CNT_CODE (0x20)
+# RAW-NEXT: IMAGE_SCN_MEM_EXECUTE (0x20000000)
+# RAW-NEXT: IMAGE_SCN_MEM_READ (0x40000000)
+# RAW-NEXT: ]
+# RAW-NEXT: Module {
+# RAW-NEXT: Index: 0
+# RAW-NEXT: Name: * Linker *
+# RAW-NEXT: }
+# RAW-NEXT: Data CRC: 0
+# RAW-NEXT: Reloc CRC: 0
+# RAW-NEXT: }
+# RAW-NEXT: ]
+# RAW-NEXT: Section Map [
+# RAW-NEXT: Entry {
+# RAW-NEXT: Flags [ (0x10D)
+# RAW-NEXT: AddressIs32Bit (0x8)
+# RAW-NEXT: Execute (0x4)
+# RAW-NEXT: IsSelector (0x100)
+# RAW-NEXT: Read (0x1)
+# RAW-NEXT: ]
+# RAW-NEXT: Ovl: 0
+# RAW-NEXT: Group: 0
+# RAW-NEXT: Frame: 1
+# RAW-NEXT: SecName: 65535
+# RAW-NEXT: ClassName: 65535
+# RAW-NEXT: Offset: 0
+# RAW-NEXT: SecByteLength: 6
+# RAW-NEXT: }
+# RAW-NEXT: Entry {
+# RAW-NEXT: Flags [ (0x109)
+# RAW-NEXT: AddressIs32Bit (0x8)
+# RAW-NEXT: IsSelector (0x100)
+# RAW-NEXT: Read (0x1)
+# RAW-NEXT: ]
+# RAW-NEXT: Ovl: 0
+# RAW-NEXT: Group: 0
+# RAW-NEXT: Frame: 2
+# RAW-NEXT: SecName: 65535
+# RAW-NEXT: ClassName: 65535
+# RAW-NEXT: Offset: 0
+# RAW-NEXT: SecByteLength:
+# RAW-NEXT: }
+# RAW-NEXT: Entry {
+# RAW-NEXT: Flags [ (0x208)
+# RAW-NEXT: AddressIs32Bit (0x8)
+# RAW-NEXT: IsAbsoluteAddress (0x200)
+# RAW-NEXT: ]
+# RAW-NEXT: Ovl: 0
+# RAW-NEXT: Group: 0
+# RAW-NEXT: Frame: 3
+# RAW-NEXT: SecName: 65535
+# RAW-NEXT: ClassName: 65535
+# RAW-NEXT: Offset: 0
+# RAW-NEXT: SecByteLength: 4294967295
+# RAW-NEXT: }
+# RAW-NEXT: ]
+# RAW-NEXT: Section Headers [
+# RAW-NEXT: {
+# RAW-NEXT: Name: .text
+# RAW-NEXT: Virtual Size: 6
+# RAW-NEXT: Virtual Address: 4096
+# RAW-NEXT: Size of Raw Data: 512
+# RAW-NEXT: File Pointer to Raw Data: 512
+# RAW-NEXT: File Pointer to Relocations: 0
+# RAW-NEXT: File Pointer to Linenumbers: 0
+# RAW-NEXT: Number of Relocations: 0
+# RAW-NEXT: Number of Linenumbers: 0
+# RAW-NEXT: Characteristics [ (0x60000020)
+# RAW-NEXT: IMAGE_SCN_CNT_CODE (0x20)
+# RAW-NEXT: IMAGE_SCN_MEM_EXECUTE (0x20000000)
+# RAW-NEXT: IMAGE_SCN_MEM_READ (0x40000000)
+# RAW-NEXT: ]
+# RAW-NEXT: }
+# RAW-NEXT: {
+# RAW-NEXT: Name: .rdata
+# RAW-NEXT: Virtual Size:
+# RAW-NEXT: Virtual Address: 8192
+# RAW-NEXT: Size of Raw Data: 512
+# RAW-NEXT: File Pointer to Raw Data: 1024
+# RAW-NEXT: File Pointer to Relocations: 0
+# RAW-NEXT: File Pointer to Linenumbers: 0
+# RAW-NEXT: Number of Relocations: 0
+# RAW-NEXT: Number of Linenumbers: 0
+# RAW-NEXT: Characteristics [ (0x40000040)
+# RAW-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
+# RAW-NEXT: IMAGE_SCN_MEM_READ (0x40000000)
+# RAW-NEXT: ]
+# RAW-NEXT: }
+# RAW-NEXT: ]
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: .drectve
+ Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
+ Alignment: 1
+ SectionData: 2020202F44454641554C544C49423A224C4942434D5422202F44454641554C544C49423A224F4C444E414D45532220
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ SectionData: 04000000F1000000530000001500011100000000443A5C625C72657434322E6F626A003A003C1100600000D00013000000F259000013000000F25900004D6963726F736F667420285229204F7074696D697A696E6720436F6D70696C65720000F10000004E0000002A00471100000000000000000000000006000000000000000500000002100000000000000000006D61696E001C001210000000000000000000000000000000000000000000000042110002004F110000F2000000200000000000000000000000060000000000000001000000140000000000000001000080F4000000180000000100000010010BFC79AA614B536E3D64B110330D1E580000F30000000E00000000643A5C625C72657434322E63000000F10000000800000006004C110A100000
+ Relocations:
+ - VirtualAddress: 136
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 140
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 192
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 196
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - Name: '.debug$T'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ SectionData: 040000000A00011201000000000000000E0008107400000000000000001000001200011600000000011000006D61696E00F3F2F10E00051600000000443A5C6200F3F2F12200051600000000433A5C767331345C56435C42494E5C616D6436345C636C2E6578650002010516000000002D5A37202D63202D4D54202D49433A5C767331345C56435C494E434C554445202D49433A5C767331345C56435C41544C4D46435C494E434C554445202D4922433A5C50726F6772616D2046696C65732028783836295C57696E646F7773204B6974735C31305C696E636C7564655C31302E302E31303135302E305C7563727422202D4922433A5C50726F6772616D2046696C65732028783836295C57696E646F7773204B6974735C4E4554465853444B5C342E365C696E636C7564655C756D22202D4922433A5C50726F6772616D2046696C65732028783836295C57696E646F7773204B6974735C382E315C696E636C7564655C73686172656422000A00041601000000051000008200051606100000202D4922433A5C50726F6772616D2046696C65732028783836295C57696E646F7773204B6974735C382E315C696E636C7564655C756D22202D4922433A5C50726F6772616D2046696C65732028783836295C57696E646F7773204B6974735C382E315C696E636C7564655C77696E727422202D5443202D5800F3F2F10E0005160000000072657434322E63001600051600000000443A5C625C76633134302E70646200F11A00031605000310000004100000081000000910000007100000F2F1
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: B82A000000C3
+symbols:
+ - Name: '@comp.id'
+ Value: 17062386
+ SectionNumber: -1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: '@feat.00'
+ Value: 2147484048
+ SectionNumber: -1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .drectve
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 47
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 296
+ NumberOfRelocations: 4
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: '.debug$T'
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 576
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 6
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 2139436471
+ Number: 0
+ - Name: main
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/reloc-arm.test b/test/COFF/reloc-arm.test
index 11b863d4390b..1f3de619c44b 100644
--- a/test/COFF/reloc-arm.test
+++ b/test/COFF/reloc-arm.test
@@ -5,11 +5,12 @@
# CHECK: .text:
# CHECK: 402000 01104000 00000000 00000000 00000000
# CHECK: 402010 01100000 00000000 00000000 00000000
-# CHECK: 402020 01000100 00004000 00000000 00000000
+# CHECK: 402020 41f20009 c0f24009 00000000 00000000
# CHECK: 402030 fe07e62f 00000000 00000000 00000000
# CHECK: 402040 3e04de2f 00000000 00000000 00000000
# CHECK: 402050 fe07d62f 00000000 00000000 00000000
-# CHECK: 402060 00000000 00000000 00000000 00000000
+# CHECK: 402060 fef0cef7 00000000 00000000 00000000
+# CHECK: 402070 00005000 00000000 00000000 00000000
--- !COFF
header:
@@ -23,7 +24,7 @@ sections:
- Name: .text
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_PURGEABLE, IMAGE_SCN_MEM_16BIT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 4096
- SectionData: 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ SectionData: 00000000000000000000000000000000000000000000000000000000000000004ff6ff79cff6ff79000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000f800000000000000000000000000000000000000000000000000000000
Relocations:
- VirtualAddress: 0
SymbolName: foo
@@ -33,16 +34,22 @@ sections:
Type: 2 # IMAGE_REL_ARM_ADDR32NB
- VirtualAddress: 32
SymbolName: foo
- Type: 17 # IMAGE_REL_AMD64_MOV32T
+ Type: 17 # IMAGE_REL_ARM_MOV32T
- VirtualAddress: 48
SymbolName: foo
- Type: 20 # IMAGE_REL_AMD64_BRANCH24T
+ Type: 20 # IMAGE_REL_ARM_BRANCH24T
- VirtualAddress: 64
SymbolName: foo
Type: 18 # IMAGE_REL_ARM_BRANCH20T
- VirtualAddress: 80
SymbolName: foo
- Type: 21 # IMAGE_REL_AMD64_BLX23T
+ Type: 21 # IMAGE_REL_ARM_BLX23T
+ - VirtualAddress: 96
+ SymbolName: bar
+ Type: 20 # IMAGE_REL_ARM_BRANCH24T
+ - VirtualAddress: 112
+ SymbolName: bar
+ Type: 15 # IMAGE_REL_ARM_SECREL
symbols:
- Name: .aaa
Value: 0
@@ -68,4 +75,10 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: bar
+ Value: 0x500000
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
...
diff --git a/test/COFF/rsds.test b/test/COFF/rsds.test
new file mode 100644
index 000000000000..37ec48b5068e
--- /dev/null
+++ b/test/COFF/rsds.test
@@ -0,0 +1,113 @@
+# RUN: yaml2obj %s > %t.obj
+
+# RUN: lld-link /debug /dll /out:%t.dll /entry:DllMain %t.obj
+# RUN: llvm-readobj -coff-debug-directory %t.dll | FileCheck %s
+
+# RUN: lld-link /debug /pdb:%t.pdb /dll /out:%t.dll /entry:DllMain %t.obj
+# RUN: llvm-readobj -coff-debug-directory %t.dll | FileCheck %s -check-prefix CHECK-PDB
+
+# CHECK: DebugDirectory [
+# CHECK: DebugEntry {
+# CHECK: Characteristics: 0x0
+# CHECK: TimeDateStamp: 1970-01-01 00:00:00 (0x0)
+# CHECK: MajorVersion: 0x0
+# CHECK: MinorVersion: 0x0
+# CHECK: Type: CodeView (0x2)
+# CHECK: SizeOfData: 0x19
+# CHECK: AddressOfRawData:
+# CHECK: PointerToRawData:
+# CHECK: PDBInfo {
+# CHECK: PDBSignature: 0x53445352
+# CHECK: PDBGUID:
+# CHECK: PDBAge: 1
+# CHECK: PDBFileName: {{$}}
+# CHECK: }
+# CHECK: }
+# CHECK: ]
+
+# CHECK-PDB: DebugDirectory [
+# CHECK-PDB: DebugEntry {
+# CHECK-PDB: Characteristics: 0x0
+# CHECK-PDB: TimeDateStamp: 1970-01-01 00:00:00 (0x0)
+# CHECK-PDB: MajorVersion: 0x0
+# CHECK-PDB: MinorVersion: 0x0
+# CHECK-PDB: Type: CodeView (0x2)
+# CHECK-PDB: SizeOfData:
+# CHECK-PDB: AddressOfRawData:
+# CHECK-PDB: PointerToRawData:
+# CHECK-PDB: PDBInfo {
+# CHECK-PDB: PDBSignature: 0x53445352
+# CHECK-PDB: PDBGUID:
+# CHECK-PDB: PDBAge: 1
+# CHECK-PDB: PDBFileName: {{.*}}.pdb
+# CHECK-PDB: }
+# CHECK-PDB: }
+# CHECK-PDB: ]
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_I386
+ Characteristics: [ ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: 31C0C3
+ - Name: .data
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 4
+ SectionData: ''
+ - Name: .bss
+ Characteristics: [ IMAGE_SCN_CNT_UNINITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 4
+ SectionData: ''
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 3
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 3963538403
+ Number: 1
+ - Name: .data
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 0
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 2
+ - Name: .bss
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 0
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 3
+ - Name: '@feat.00'
+ Value: 1
+ SectionNumber: -1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: _DllMain
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/symtab.test b/test/COFF/symtab.test
index 51eb3a374c59..ffaca285d6f0 100644
--- a/test/COFF/symtab.test
+++ b/test/COFF/symtab.test
@@ -36,17 +36,17 @@
# CHECK-NEXT: AuxSymbolCount: 0
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: message
-# CHECK-NEXT: Value: 6
-# CHECK-NEXT: Section: .text2 (3)
+# CHECK-NEXT: Name: MessageBoxA
+# CHECK-NEXT: Value: 80
+# CHECK-NEXT: Section: .text (2)
# CHECK-NEXT: BaseType: Null (0x0)
# CHECK-NEXT: ComplexType: Null (0x0)
-# CHECK-NEXT: StorageClass: Static (0x3)
+# CHECK-NEXT: StorageClass: External (0x2)
# CHECK-NEXT: AuxSymbolCount: 0
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: main
-# CHECK-NEXT: Value: 0
+# CHECK-NEXT: Name: ExitProcess
+# CHECK-NEXT: Value: 64
# CHECK-NEXT: Section: .text (2)
# CHECK-NEXT: BaseType: Null (0x0)
# CHECK-NEXT: ComplexType: Null (0x0)
@@ -54,8 +54,8 @@
# CHECK-NEXT: AuxSymbolCount: 0
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: caption
-# CHECK-NEXT: Value: 0
+# CHECK-NEXT: Name: message
+# CHECK-NEXT: Value: 6
# CHECK-NEXT: Section: .text2 (3)
# CHECK-NEXT: BaseType: Null (0x0)
# CHECK-NEXT: ComplexType: Null (0x0)
@@ -63,26 +63,8 @@
# CHECK-NEXT: AuxSymbolCount: 0
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: abs_symbol
-# CHECK-NEXT: Value: 2662186735
-# CHECK-NEXT: Section: IMAGE_SYM_ABSOLUTE (-1)
-# CHECK-NEXT: BaseType: Null (0x0)
-# CHECK-NEXT: ComplexType: Null (0x0)
-# CHECK-NEXT: StorageClass: External (0x2)
-# CHECK-NEXT: AuxSymbolCount: 0
-# CHECK-NEXT: }
-# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: __imp_ExitProcess
-# CHECK-NEXT: Value: 64
-# CHECK-NEXT: Section: .idata (4)
-# CHECK-NEXT: BaseType: Null (0x0)
-# CHECK-NEXT: ComplexType: Null (0x0)
-# CHECK-NEXT: StorageClass: External (0x2)
-# CHECK-NEXT: AuxSymbolCount: 0
-# CHECK-NEXT: }
-# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: ExitProcess
-# CHECK-NEXT: Value: 64
+# CHECK-NEXT: Name: main
+# CHECK-NEXT: Value: 0
# CHECK-NEXT: Section: .text (2)
# CHECK-NEXT: BaseType: Null (0x0)
# CHECK-NEXT: ComplexType: Null (0x0)
@@ -90,18 +72,18 @@
# CHECK-NEXT: AuxSymbolCount: 0
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: __imp_MessageBoxA
-# CHECK-NEXT: Value: 72
-# CHECK-NEXT: Section: .idata (4)
+# CHECK-NEXT: Name: caption
+# CHECK-NEXT: Value: 0
+# CHECK-NEXT: Section: .text2 (3)
# CHECK-NEXT: BaseType: Null (0x0)
# CHECK-NEXT: ComplexType: Null (0x0)
-# CHECK-NEXT: StorageClass: External (0x2)
+# CHECK-NEXT: StorageClass: Static (0x3)
# CHECK-NEXT: AuxSymbolCount: 0
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: MessageBoxA
-# CHECK-NEXT: Value: 80
-# CHECK-NEXT: Section: .text (2)
+# CHECK-NEXT: Name: abs_symbol
+# CHECK-NEXT: Value: 2662186735
+# CHECK-NEXT: Section: IMAGE_SYM_ABSOLUTE (-1)
# CHECK-NEXT: BaseType: Null (0x0)
# CHECK-NEXT: ComplexType: Null (0x0)
# CHECK-NEXT: StorageClass: External (0x2)
diff --git a/test/ELF/arm-attributes-remove.s b/test/ELF/Inputs/arm-attributes1.s
index 010f366cd7fb..da67c49b6b39 100644
--- a/test/ELF/arm-attributes-remove.s
+++ b/test/ELF/Inputs/arm-attributes1.s
@@ -1,20 +1,4 @@
-// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
-// RUN: ld.lld %t.o -o %t
-// RUN: llvm-readobj -s %t | FileCheck %s
-// RUN: ld.lld %t.o -shared -o %t2
-// RUN: llvm-readobj -s %t2 | FileCheck %s
-// RUN: ld.lld %t.o -r -o %t3
-// RUN: llvm-readobj -s %t3 | FileCheck %s
-
-// The .ARM.attributes section should be removed from executables and
-// shared objects.
-
-// At present we remove it from the -r object output as well which isn't ideal.
-// Unfortunately combining per-object attributes cannot be safely done by just
-// concatentation of input sections.
-
-// CHECK-NOT: Name: .ARM.attributes
-// REQUIRES: arm
+// Input that generates an object with a populated SHT_ARM_ATTRIBUTES section
.text
.syntax unified
.eabi_attribute 67, "2.09" @ Tag_conformance
@@ -38,8 +22,8 @@
.eabi_attribute 26, 2 @ Tag_ABI_enum_size
.eabi_attribute 14, 0 @ Tag_ABI_PCS_R9_use
.eabi_attribute 68, 1 @ Tag_Virtualization_use
- .globl _start
+ .globl func
.p2align 2
- .type _start,%function
-_start:
+ .type func,%function
+func:
bx lr
diff --git a/test/ELF/Inputs/arm-exidx-cantunwind.s b/test/ELF/Inputs/arm-exidx-cantunwind.s
new file mode 100644
index 000000000000..679cc7513096
--- /dev/null
+++ b/test/ELF/Inputs/arm-exidx-cantunwind.s
@@ -0,0 +1,40 @@
+// Functions that will generate a .ARM.exidx section with SHF_LINK_ORDER
+// dependency on the progbits section containing the .cantunwind directive
+ .syntax unified
+ .section .func1, "ax",%progbits
+ .globl func1
+func1:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ .section .func2, "ax", %progbits
+ .globl func2
+func2:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ .section .func3, "ax",%progbits
+ .globl func3
+func3:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ .section .text, "ax",%progbits
+ .globl func4
+func4:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+ .globl func5
+func5:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
diff --git a/test/ELF/Inputs/arm-shared.s b/test/ELF/Inputs/arm-shared.s
new file mode 100644
index 000000000000..4330a1db1591
--- /dev/null
+++ b/test/ELF/Inputs/arm-shared.s
@@ -0,0 +1,8 @@
+.syntax unified
+.global bar2
+.type bar2, %function
+bar2:
+
+.global zed2
+.type zed2, %function
+zed2:
diff --git a/test/ELF/Inputs/arm-tls-get-addr.s b/test/ELF/Inputs/arm-tls-get-addr.s
new file mode 100644
index 000000000000..f3212ad974be
--- /dev/null
+++ b/test/ELF/Inputs/arm-tls-get-addr.s
@@ -0,0 +1,13 @@
+ .syntax unified
+ .text
+ .globl __tls_get_addr
+ .type __tls_get_addr,%function
+__tls_get_addr:
+ bx lr
+
+.section .tbss,"awT",%nobits
+ .p2align 2
+y:
+ .space 4
+ .globl y
+ .type y, %object
diff --git a/test/ELF/Inputs/bad-archive.a b/test/ELF/Inputs/bad-archive.a
new file mode 100644
index 000000000000..c9c6fbfd9146
--- /dev/null
+++ b/test/ELF/Inputs/bad-archive.a
@@ -0,0 +1,2 @@
+!<arch>
+this is malformed archive used in bad-archive.s
diff --git a/test/ELF/Inputs/comment-gc.s b/test/ELF/Inputs/comment-gc.s
new file mode 100644
index 000000000000..2453ce9d7367
--- /dev/null
+++ b/test/ELF/Inputs/comment-gc.s
@@ -0,0 +1 @@
+.ident "bar"
diff --git a/test/ELF/Inputs/conflict-debug.s b/test/ELF/Inputs/conflict-debug.s
new file mode 100644
index 000000000000..03fb01331815
--- /dev/null
+++ b/test/ELF/Inputs/conflict-debug.s
@@ -0,0 +1,5 @@
+.file 1 "conflict-debug.s"
+.globl zed
+.loc 1 4
+zed:
+ nop
diff --git a/test/ELF/Inputs/gdb-index-a.elf b/test/ELF/Inputs/gdb-index-a.elf
new file mode 100644
index 000000000000..9b90b0dc233e
--- /dev/null
+++ b/test/ELF/Inputs/gdb-index-a.elf
Binary files differ
diff --git a/test/ELF/Inputs/gdb-index-b.elf b/test/ELF/Inputs/gdb-index-b.elf
new file mode 100644
index 000000000000..b3356d8c773b
--- /dev/null
+++ b/test/ELF/Inputs/gdb-index-b.elf
Binary files differ
diff --git a/test/ELF/Inputs/i386-tls-got.s b/test/ELF/Inputs/i386-tls-got.s
new file mode 100644
index 000000000000..42d16b1af169
--- /dev/null
+++ b/test/ELF/Inputs/i386-tls-got.s
@@ -0,0 +1,5 @@
+ .type foobar,@object
+ .section .tdata,"awT",@progbits
+ .globl foobar
+foobar:
+ .long 42
diff --git a/test/ELF/Inputs/icf-non-mergeable.s b/test/ELF/Inputs/icf-non-mergeable.s
new file mode 100644
index 000000000000..0c42a654963e
--- /dev/null
+++ b/test/ELF/Inputs/icf-non-mergeable.s
@@ -0,0 +1,8 @@
+.globl d1, d2
+.section .data.d1, "aw"
+d1:
+ .quad 0
+
+.section .data.d2, "aw"
+d2:
+ .quad 0
diff --git a/test/ELF/Inputs/invalid-shstrndx.so b/test/ELF/Inputs/invalid-shstrndx.so
deleted file mode 100755
index dc332b9292ea..000000000000
--- a/test/ELF/Inputs/invalid-shstrndx.so
+++ /dev/null
Binary files differ
diff --git a/test/ELF/Inputs/mips-concatenated-abiflags.o b/test/ELF/Inputs/mips-concatenated-abiflags.o
new file mode 100644
index 000000000000..404530919fc3
--- /dev/null
+++ b/test/ELF/Inputs/mips-concatenated-abiflags.o
Binary files differ
diff --git a/test/ELF/Inputs/mips-fnpic.s b/test/ELF/Inputs/mips-fnpic.s
new file mode 100644
index 000000000000..d8bf0ba34f52
--- /dev/null
+++ b/test/ELF/Inputs/mips-fnpic.s
@@ -0,0 +1,6 @@
+ .option pic0
+ .text
+ .global fnpic
+ .type fnpic, @function
+fnpic:
+ nop
diff --git a/test/ELF/Inputs/mips-fpic.s b/test/ELF/Inputs/mips-fpic.s
new file mode 100644
index 000000000000..b7af281c3253
--- /dev/null
+++ b/test/ELF/Inputs/mips-fpic.s
@@ -0,0 +1,6 @@
+ .option pic2
+ .text
+ .global fpic
+ .type fpic, @function
+fpic:
+ nop
diff --git a/test/ELF/Inputs/mips-gp0-non-zero.o b/test/ELF/Inputs/mips-gp0-non-zero.o
new file mode 100755
index 000000000000..4c8234336baa
--- /dev/null
+++ b/test/ELF/Inputs/mips-gp0-non-zero.o
Binary files differ
diff --git a/test/ELF/Inputs/mips-n32-rels.o b/test/ELF/Inputs/mips-n32-rels.o
new file mode 100644
index 000000000000..88cbce699e6d
--- /dev/null
+++ b/test/ELF/Inputs/mips-n32-rels.o
Binary files differ
diff --git a/test/ELF/Inputs/relocatable-tls.s b/test/ELF/Inputs/relocatable-tls.s
new file mode 100644
index 000000000000..296beb474c87
--- /dev/null
+++ b/test/ELF/Inputs/relocatable-tls.s
@@ -0,0 +1 @@
+callq __tls_get_addr@PLT
diff --git a/test/ELF/Inputs/relocation-relative-absolute.s b/test/ELF/Inputs/relocation-relative-absolute.s
new file mode 100644
index 000000000000..2341f69b162a
--- /dev/null
+++ b/test/ELF/Inputs/relocation-relative-absolute.s
@@ -0,0 +1,2 @@
+.globl answer
+answer = 42
diff --git a/test/ELF/Inputs/shared2-x86-64.s b/test/ELF/Inputs/shared2-x86-64.s
new file mode 100644
index 000000000000..925e6a427fac
--- /dev/null
+++ b/test/ELF/Inputs/shared2-x86-64.s
@@ -0,0 +1,9 @@
+.global bar2
+.type bar2, @function
+bar2:
+ ret
+
+.global zed2
+.type zed2, @function
+zed2:
+ ret
diff --git a/test/ELF/Inputs/shf-info-link.test b/test/ELF/Inputs/shf-info-link.test
new file mode 100644
index 000000000000..b877969168e8
--- /dev/null
+++ b/test/ELF/Inputs/shf-info-link.test
@@ -0,0 +1,21 @@
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ - Name: .rela.text
+ Type: SHT_RELA
+ Link: .symtab
+ Info: .text
+ Relocations:
+ - Offset: 0x0000000000000000
+ Symbol: foo
+ Type: R_X86_64_64
+Symbols:
+ Global:
+ - Name: foo
diff --git a/test/ELF/Inputs/startstop-shared2.s b/test/ELF/Inputs/startstop-shared2.s
new file mode 100644
index 000000000000..414bf6dc876e
--- /dev/null
+++ b/test/ELF/Inputs/startstop-shared2.s
@@ -0,0 +1,2 @@
+.globl __start_foo
+__start_foo:
diff --git a/test/ELF/Inputs/symbol-override.s b/test/ELF/Inputs/symbol-override.s
index 21f3ae244c77..ab3bf8dc74b0 100644
--- a/test/ELF/Inputs/symbol-override.s
+++ b/test/ELF/Inputs/symbol-override.s
@@ -11,6 +11,6 @@ nop
.globl do
.type do,@function
-do:
+do:
callq foo@PLT
callq bar@PLT
diff --git a/test/ELF/Inputs/uabs_label.s b/test/ELF/Inputs/uabs_label.s
new file mode 100644
index 000000000000..b2a67216a592
--- /dev/null
+++ b/test/ELF/Inputs/uabs_label.s
@@ -0,0 +1,4 @@
+# Sample label to test R_AARCH64_MOVW_UABS relocations
+
+.globl uabs_label
+uabs_label = 0xF000E000D000C
diff --git a/test/ELF/Inputs/undef-debug.s b/test/ELF/Inputs/undef-debug.s
new file mode 100644
index 000000000000..db8aaf16e6bd
--- /dev/null
+++ b/test/ELF/Inputs/undef-debug.s
@@ -0,0 +1,11 @@
+.file 1 "dir/undef-debug.s"
+.loc 1 3
+ .quad zed3
+
+.section .text.1,"ax"
+.loc 1 7
+ .quad zed4
+
+.section .text.2,"ax"
+.loc 1 11
+ .quad zed5
diff --git a/test/ELF/Inputs/use-bar.s b/test/ELF/Inputs/use-bar.s
new file mode 100644
index 000000000000..cc82b6fbb143
--- /dev/null
+++ b/test/ELF/Inputs/use-bar.s
@@ -0,0 +1,2 @@
+.section .bar,"a"
+ .quad _bar
diff --git a/test/ELF/Inputs/verdef-defaultver.s b/test/ELF/Inputs/verdef-defaultver.s
index 2e1d1c36fb92..6664d62c90f5 100644
--- a/test/ELF/Inputs/verdef-defaultver.s
+++ b/test/ELF/Inputs/verdef-defaultver.s
@@ -1,5 +1,5 @@
-b@LIBSAMPLE_1.0 = b_1
-b@@LIBSAMPLE_2.0 = b_2
+b@V1 = b_1
+b@@V2 = b_2
.globl a
.type a,@function
diff --git a/test/ELF/aarch64-abs16.s b/test/ELF/aarch64-abs16.s
index e41abc43ee0d..c4f5b3e44b58 100644
--- a/test/ELF/aarch64-abs16.s
+++ b/test/ELF/aarch64-abs16.s
@@ -18,7 +18,7 @@ _start:
// S + A = 0xffff
// 11002: S = 0x100, A = -0x8100
// S + A = 0x8000
-// CHECK-NEXT: 11000 ffff0080
+// CHECK-NEXT: 20000 ffff0080
// RUN: not ld.lld %t.o %t255.o -o %t2
// | FileCheck %s --check-prefix=OVERFLOW
diff --git a/test/ELF/aarch64-abs32.s b/test/ELF/aarch64-abs32.s
index c2460d15e825..b051692374b1 100644
--- a/test/ELF/aarch64-abs32.s
+++ b/test/ELF/aarch64-abs32.s
@@ -14,11 +14,11 @@ _start:
// RUN: llvm-objdump -s -section=.data %t2 | FileCheck %s
// CHECK: Contents of section .data:
-// 11000: S = 0x100, A = 0xfffffeff
+// 20000: S = 0x100, A = 0xfffffeff
// S + A = 0xffffffff
-// 11004: S = 0x100, A = -0x80000100
+// 20004: S = 0x100, A = -0x80000100
// S + A = 0x80000000
-// CHECK-NEXT: 11000 ffffffff 00000080
+// CHECK-NEXT: 20000 ffffffff 00000080
// RUN: not ld.lld %t.o %t255.o -o %t2
// | FileCheck %s --check-prefix=OVERFLOW
diff --git a/test/ELF/aarch64-condb-reloc.s b/test/ELF/aarch64-condb-reloc.s
index 630e3430f4e5..b2af445f4b6d 100644
--- a/test/ELF/aarch64-condb-reloc.s
+++ b/test/ELF/aarch64-condb-reloc.s
@@ -12,21 +12,21 @@
# 0x1102c - 16 = 0x1101c
# CHECK: Disassembly of section .text:
# CHECK-NEXT: _foo:
-# CHECK-NEXT: 11000: {{.*}} nop
-# CHECK-NEXT: 11004: {{.*}} nop
-# CHECK-NEXT: 11008: {{.*}} nop
-# CHECK-NEXT: 1100c: {{.*}} nop
+# CHECK-NEXT: 20000: {{.*}} nop
+# CHECK-NEXT: 20004: {{.*}} nop
+# CHECK-NEXT: 20008: {{.*}} nop
+# CHECK-NEXT: 2000c: {{.*}} nop
# CHECK: _bar:
-# CHECK-NEXT: 11010: {{.*}} nop
-# CHECK-NEXT: 11014: {{.*}} nop
-# CHECK-NEXT: 11018: {{.*}} nop
+# CHECK-NEXT: 20010: {{.*}} nop
+# CHECK-NEXT: 20014: {{.*}} nop
+# CHECK-NEXT: 20018: {{.*}} nop
# CHECK: _dah:
-# CHECK-NEXT: 1101c: {{.*}} nop
-# CHECK-NEXT: 11020: {{.*}} nop
+# CHECK-NEXT: 2001c: {{.*}} nop
+# CHECK-NEXT: 20020: {{.*}} nop
# CHECK: _start:
-# CHECK-NEXT: 11024: {{.*}} b.eq #-36
-# CHECK-NEXT: 11028: {{.*}} b.eq #-24
-# CHECK-NEXT: 1102c: {{.*}} b.eq #-16
+# CHECK-NEXT: 20024: {{.*}} b.eq #-36
+# CHECK-NEXT: 20028: {{.*}} b.eq #-24
+# CHECK-NEXT: 2002c: {{.*}} b.eq #-16
#DSOREL: Section {
#DSOREL: Index:
@@ -36,8 +36,8 @@
#DSOREL-NEXT: SHF_ALLOC
#DSOREL-NEXT: SHF_WRITE
#DSOREL-NEXT: ]
-#DSOREL-NEXT: Address: 0x3000
-#DSOREL-NEXT: Offset: 0x3000
+#DSOREL-NEXT: Address: 0x30000
+#DSOREL-NEXT: Offset: 0x30000
#DSOREL-NEXT: Size: 48
#DSOREL-NEXT: Link: 0
#DSOREL-NEXT: Info: 0
@@ -46,51 +46,51 @@
#DSOREL-NEXT: }
#DSOREL: Relocations [
#DSOREL-NEXT: Section ({{.*}}) .rela.plt {
-#DSOREL-NEXT: 0x3018 R_AARCH64_JUMP_SLOT _foo
-#DSOREL-NEXT: 0x3020 R_AARCH64_JUMP_SLOT _bar
-#DSOREL-NEXT: 0x3028 R_AARCH64_JUMP_SLOT _dah
+#DSOREL-NEXT: 0x30018 R_AARCH64_JUMP_SLOT _foo
+#DSOREL-NEXT: 0x30020 R_AARCH64_JUMP_SLOT _bar
+#DSOREL-NEXT: 0x30028 R_AARCH64_JUMP_SLOT _dah
#DSOREL-NEXT: }
#DSOREL-NEXT:]
#DSO: Disassembly of section .text:
#DSO-NEXT: _foo:
-#DSO-NEXT: 1000: {{.*}} nop
-#DSO-NEXT: 1004: {{.*}} nop
-#DSO-NEXT: 1008: {{.*}} nop
-#DSO-NEXT: 100c: {{.*}} nop
+#DSO-NEXT: 10000: {{.*}} nop
+#DSO-NEXT: 10004: {{.*}} nop
+#DSO-NEXT: 10008: {{.*}} nop
+#DSO-NEXT: 1000c: {{.*}} nop
#DSO: _bar:
-#DSO-NEXT: 1010: {{.*}} nop
-#DSO-NEXT: 1014: {{.*}} nop
-#DSO-NEXT: 1018: {{.*}} nop
+#DSO-NEXT: 10010: {{.*}} nop
+#DSO-NEXT: 10014: {{.*}} nop
+#DSO-NEXT: 10018: {{.*}} nop
#DSO: _dah:
-#DSO-NEXT: 101c: {{.*}} nop
-#DSO-NEXT: 1020: {{.*}} nop
+#DSO-NEXT: 1001c: {{.*}} nop
+#DSO-NEXT: 10020: {{.*}} nop
#DSO: _start:
-#DSO-NEXT: 1024: {{.*}} b.eq #44
-#DSO-NEXT: 1028: {{.*}} b.eq #56
-#DSO-NEXT: 102c: {{.*}} b.eq #68
+#DSO-NEXT: 10024: {{.*}} b.eq #44
+#DSO-NEXT: 10028: {{.*}} b.eq #56
+#DSO-NEXT: 1002c: {{.*}} b.eq #68
#DSO-NEXT: Disassembly of section .plt:
#DSO-NEXT: .plt:
-#DSO-NEXT: 1030: {{.*}} stp x16, x30, [sp, #-16]!
-#DSO-NEXT: 1034: {{.*}} adrp x16, #8192
-#DSO-NEXT: 1038: {{.*}} ldr x17, [x16, #16]
-#DSO-NEXT: 103c: {{.*}} add x16, x16, #16
-#DSO-NEXT: 1040: {{.*}} br x17
-#DSO-NEXT: 1044: {{.*}} nop
-#DSO-NEXT: 1048: {{.*}} nop
-#DSO-NEXT: 104c: {{.*}} nop
-#DSO-NEXT: 1050: {{.*}} adrp x16, #8192
-#DSO-NEXT: 1054: {{.*}} ldr x17, [x16, #24]
-#DSO-NEXT: 1058: {{.*}} add x16, x16, #24
-#DSO-NEXT: 105c: {{.*}} br x17
-#DSO-NEXT: 1060: {{.*}} adrp x16, #8192
-#DSO-NEXT: 1064: {{.*}} ldr x17, [x16, #32]
-#DSO-NEXT: 1068: {{.*}} add x16, x16, #32
-#DSO-NEXT: 106c: {{.*}} br x17
-#DSO-NEXT: 1070: {{.*}} adrp x16, #8192
-#DSO-NEXT: 1074: {{.*}} ldr x17, [x16, #40]
-#DSO-NEXT: 1078: {{.*}} add x16, x16, #40
-#DSO-NEXT: 107c: {{.*}} br x17
+#DSO-NEXT: 10030: {{.*}} stp x16, x30, [sp, #-16]!
+#DSO-NEXT: 10034: {{.*}} adrp x16, #131072
+#DSO-NEXT: 10038: {{.*}} ldr x17, [x16, #16]
+#DSO-NEXT: 1003c: {{.*}} add x16, x16, #16
+#DSO-NEXT: 10040: {{.*}} br x17
+#DSO-NEXT: 10044: {{.*}} nop
+#DSO-NEXT: 10048: {{.*}} nop
+#DSO-NEXT: 1004c: {{.*}} nop
+#DSO-NEXT: 10050: {{.*}} adrp x16, #131072
+#DSO-NEXT: 10054: {{.*}} ldr x17, [x16, #24]
+#DSO-NEXT: 10058: {{.*}} add x16, x16, #24
+#DSO-NEXT: 1005c: {{.*}} br x17
+#DSO-NEXT: 10060: {{.*}} adrp x16, #131072
+#DSO-NEXT: 10064: {{.*}} ldr x17, [x16, #32]
+#DSO-NEXT: 10068: {{.*}} add x16, x16, #32
+#DSO-NEXT: 1006c: {{.*}} br x17
+#DSO-NEXT: 10070: {{.*}} adrp x16, #131072
+#DSO-NEXT: 10074: {{.*}} ldr x17, [x16, #40]
+#DSO-NEXT: 10078: {{.*}} add x16, x16, #40
+#DSO-NEXT: 1007c: {{.*}} br x17
.globl _start
_start:
diff --git a/test/ELF/aarch64-copy.s b/test/ELF/aarch64-copy.s
index 6e0af27a0c6b..ffecf2f8a283 100644
--- a/test/ELF/aarch64-copy.s
+++ b/test/ELF/aarch64-copy.s
@@ -22,7 +22,7 @@ _start:
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x13000
+// CHECK-NEXT: Address: 0x40000
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 24
// CHECK-NEXT: Link:
@@ -32,19 +32,19 @@ _start:
// CHECK: Relocations [
// CHECK-NEXT: Section ({{.*}}) .rela.dyn {
// CHECK-NEXT: Relocation {
-// CHECK-NEXT: Offset: 0x13000
+// CHECK-NEXT: Offset: 0x40000
// CHECK-NEXT: Type: R_AARCH64_COPY
// CHECK-NEXT: Symbol: x
// CHECK-NEXT: Addend: 0x0
// CHECK-NEXT: }
// CHECK-NEXT: Relocation {
-// CHECK-NEXT: Offset: 0x13010
+// CHECK-NEXT: Offset: 0x40010
// CHECK-NEXT: Type: R_AARCH64_COPY
// CHECK-NEXT: Symbol: y
// CHECK-NEXT: Addend: 0x0
// CHECK-NEXT: }
// CHECK-NEXT: Relocation {
-// CHECK-NEXT: Offset: 0x13014
+// CHECK-NEXT: Offset: 0x40014
// CHECK-NEXT: Type: R_AARCH64_COPY
// CHECK-NEXT: Symbol: z
// CHECK-NEXT: Addend: 0x0
@@ -54,21 +54,21 @@ _start:
// CHECK: Symbols [
// CHECK: Name: x
-// CHECK-NEXT: Value: 0x13000
+// CHECK-NEXT: Value: 0x40000
// CHECK-NEXT: Size: 4
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: Object
// CHECK-NEXT: Other:
// CHECK-NEXT: Section: .bss
// CHECK: Name: y
-// CHECK-NEXT: Value: 0x13010
+// CHECK-NEXT: Value: 0x40010
// CHECK-NEXT: Size: 4
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: Object
// CHECK-NEXT: Other:
// CHECK-NEXT: Section: .bss
// CHECK: Name: z
-// CHECK-NEXT: Value: 0x13014
+// CHECK-NEXT: Value: 0x40014
// CHECK-NEXT: Size: 4
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: Object
@@ -78,16 +78,16 @@ _start:
// CODE: Disassembly of section .text:
// CODE-NEXT: _start:
-// S(x) = 0x13000, A = 0, P = 0x11000
-// S + A - P = 0x2000 = 8208
-// CODE-NEXT: 11000: {{.*}} adr x1, #8192
-// S(y) = 0x13010, A = 0, P = 0x11004
-// Page(S + A) - Page(P) = 0x13000 - 0x11000 = 0x2000 = 8192
-// CODE-NEXT: 11004: {{.*}} adrp x2, #8192
-// S(y) = 0x13010, A = 0
+// S(x) = 0x40000, A = 0, P = 0x20000
+// S + A - P = 0x20000 = 131072
+// CODE-NEXT: 20000: {{.*}} adr x1, #131072
+// S(y) = 0x40010, A = 0, P = 0x20004
+// Page(S + A) - Page(P) = 0x40000 - 0x20000 = 0x20000 = 131072
+// CODE-NEXT: 20004: {{.*}} adrp x2, #131072
+// S(y) = 0x40010, A = 0
// (S + A) & 0xFFF = 0x10 = 16
-// CODE-NEXT: 11008: {{.*}} add x2, x2, #16
+// CODE-NEXT: 20008: {{.*}} add x2, x2, #16
// RODATA: Contents of section .rodata:
-// S(z) = 0x13014
-// RODATA-NEXT: 101c8 14300100
+// S(z) = 0x40014
+// RODATA-NEXT: 101c8 14000400
diff --git a/test/ELF/aarch64-copy2.s b/test/ELF/aarch64-copy2.s
index af99d8ec2aee..6f72e21470ae 100644
--- a/test/ELF/aarch64-copy2.s
+++ b/test/ELF/aarch64-copy2.s
@@ -19,7 +19,7 @@ _start:
// CHECK-NEXT: Section: Undefined
// CHECK: Name: foo
-// CHECK-NEXT: Value: 0x11030
+// CHECK-NEXT: Value: 0x20030
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: Function
diff --git a/test/ELF/aarch64-data-relocs.s b/test/ELF/aarch64-data-relocs.s
index 30770836f787..19e11bc3925c 100644
--- a/test/ELF/aarch64-data-relocs.s
+++ b/test/ELF/aarch64-data-relocs.s
@@ -12,12 +12,12 @@ _start:
// S = 0x100, A = 0x24
// S + A = 0x124
// CHECK: Contents of section .R_AARCH64_ABS64:
-// CHECK-NEXT: 11000 24010000 00000000
+// CHECK-NEXT: 20000 24010000 00000000
.section .R_AARCH64_PREL64, "ax",@progbits
.xword foo - . + 0x24
-// S = 0x100, A = 0x24, P = 0x11008
-// S + A - P = 0xfffffffffffef11c
+// S = 0x100, A = 0x24, P = 0x20008
+// S + A - P = 0xfffffffffffe011c
// CHECK: Contents of section .R_AARCH64_PREL64:
-// CHECK-NEXT: 11008 1cf1feff ffffffff
+// CHECK-NEXT: 20008 1c01feff ffffffff
diff --git a/test/ELF/aarch64-fpic-abs16.s b/test/ELF/aarch64-fpic-abs16.s
index e123f57447a5..539242a8d217 100644
--- a/test/ELF/aarch64-fpic-abs16.s
+++ b/test/ELF/aarch64-fpic-abs16.s
@@ -1,7 +1,7 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-none-freebsd %s -o %t.o
// RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-// CHECK: relocation R_AARCH64_ABS16 cannot be used against shared object; recompile with -fPIC.
+// CHECK: {{.*}}:(.data+0x0): relocation R_AARCH64_ABS16 cannot be used against shared object; recompile with -fPIC.
.data
.hword foo
diff --git a/test/ELF/aarch64-fpic-add_abs_lo12_nc.s b/test/ELF/aarch64-fpic-add_abs_lo12_nc.s
index 7e0b6b833006..0ebd0fdde3a5 100644
--- a/test/ELF/aarch64-fpic-add_abs_lo12_nc.s
+++ b/test/ELF/aarch64-fpic-add_abs_lo12_nc.s
@@ -1,7 +1,7 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-none-freebsd %s -o %t.o
// RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-// CHECK: can't create dynamic relocation R_AARCH64_ADD_ABS_LO12_NC against readonly segment
+// CHECK: {{.*}}.o:(.text+0x0): can't create dynamic relocation R_AARCH64_ADD_ABS_LO12_NC against symbol 'dat' defined in {{.*}}.o
add x0, x0, :lo12:dat
.data
diff --git a/test/ELF/aarch64-fpic-adr_prel_lo21.s b/test/ELF/aarch64-fpic-adr_prel_lo21.s
index 501a724616e3..d01749600e71 100644
--- a/test/ELF/aarch64-fpic-adr_prel_lo21.s
+++ b/test/ELF/aarch64-fpic-adr_prel_lo21.s
@@ -1,7 +1,7 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-none-freebsd %s -o %t.o
// RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-// CHECK: can't create dynamic relocation R_AARCH64_ADR_PREL_LO21 against readonly segment
+// CHECK: {{.*}}.o:(.text+0x0): can't create dynamic relocation R_AARCH64_ADR_PREL_LO21 against symbol 'dat' defined in {{.*}}.o
adr x0, dat
.data
diff --git a/test/ELF/aarch64-fpic-adr_prel_pg_hi21.s b/test/ELF/aarch64-fpic-adr_prel_pg_hi21.s
index 572ecffa6d71..3da4b2ece517 100644
--- a/test/ELF/aarch64-fpic-adr_prel_pg_hi21.s
+++ b/test/ELF/aarch64-fpic-adr_prel_pg_hi21.s
@@ -1,7 +1,7 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-none-freebsd %s -o %t.o
// RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-// CHECK: can't create dynamic relocation R_AARCH64_ADR_PREL_PG_HI21 against readonly segment
+// CHECK: {{.*}}.o:(.text+0x0): can't create dynamic relocation R_AARCH64_ADR_PREL_PG_HI21 against symbol 'dat' defined in {{.*}}.o
adrp x0, dat
.data
diff --git a/test/ELF/aarch64-fpic-ldst32_abs_lo12_nc.s b/test/ELF/aarch64-fpic-ldst32_abs_lo12_nc.s
index 85772f10fcc7..8df582186b23 100644
--- a/test/ELF/aarch64-fpic-ldst32_abs_lo12_nc.s
+++ b/test/ELF/aarch64-fpic-ldst32_abs_lo12_nc.s
@@ -1,7 +1,7 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-none-freebsd %s -o %t.o
// RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-// CHECK: can't create dynamic relocation R_AARCH64_LDST32_ABS_LO12_NC against readonly segment
+// CHECK: {{.*}}.o:(.text+0x0): can't create dynamic relocation R_AARCH64_LDST32_ABS_LO12_NC against symbol 'dat' defined in {{.*}}.o
ldr s4, [x0, :lo12:dat]
.data
diff --git a/test/ELF/aarch64-fpic-ldst64_abs_lo12_nc.s b/test/ELF/aarch64-fpic-ldst64_abs_lo12_nc.s
index d9f7b0c9ad1b..fe80e654277f 100644
--- a/test/ELF/aarch64-fpic-ldst64_abs_lo12_nc.s
+++ b/test/ELF/aarch64-fpic-ldst64_abs_lo12_nc.s
@@ -1,7 +1,7 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-none-freebsd %s -o %t.o
// RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-// CHECK: can't create dynamic relocation R_AARCH64_LDST64_ABS_LO12_NC against readonly segment
+// CHECK: {{.*}}.o:(.text+0x0): can't create dynamic relocation R_AARCH64_LDST64_ABS_LO12_NC against symbol 'dat' defined in {{.*}}.o
ldr x0, [x0, :lo12:dat]
.data
diff --git a/test/ELF/aarch64-fpic-ldst8_abs_lo12_nc.s b/test/ELF/aarch64-fpic-ldst8_abs_lo12_nc.s
index 20e1bba49d1e..8442062bed97 100644
--- a/test/ELF/aarch64-fpic-ldst8_abs_lo12_nc.s
+++ b/test/ELF/aarch64-fpic-ldst8_abs_lo12_nc.s
@@ -1,7 +1,7 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-none-freebsd %s -o %t.o
// RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-// CHECK: can't create dynamic relocation R_AARCH64_LDST8_ABS_LO12_NC against readonly segment
+// CHECK: {{.*}}.o:(.text+0x0): can't create dynamic relocation R_AARCH64_LDST8_ABS_LO12_NC against symbol 'dat' defined in {{.*}}.o
ldrsb x0, [x1, :lo12:dat]
.data
diff --git a/test/ELF/aarch64-fpic-prel16.s b/test/ELF/aarch64-fpic-prel16.s
index 52e2402a4831..3cb76cabd06b 100644
--- a/test/ELF/aarch64-fpic-prel16.s
+++ b/test/ELF/aarch64-fpic-prel16.s
@@ -1,7 +1,7 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-none-freebsd %s -o %t.o
// RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-// CHECK: relocation R_AARCH64_PREL16 cannot be used against shared object; recompile with -fPIC.
+// CHECK: {{.*}}:(.data+0x0): relocation R_AARCH64_PREL16 cannot be used against shared object; recompile with -fPIC.
.data
.hword foo - .
diff --git a/test/ELF/aarch64-fpic-prel32.s b/test/ELF/aarch64-fpic-prel32.s
index 72ba58f84d56..1c701e93cf84 100644
--- a/test/ELF/aarch64-fpic-prel32.s
+++ b/test/ELF/aarch64-fpic-prel32.s
@@ -1,7 +1,7 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-none-freebsd %s -o %t.o
// RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-// CHECK: relocation R_AARCH64_PREL32 cannot be used against shared object; recompile with -fPIC.
+// CHECK: {{.*}}:(.data+0x0): relocation R_AARCH64_PREL32 cannot be used against shared object; recompile with -fPIC.
.data
.word foo - .
diff --git a/test/ELF/aarch64-fpic-prel64.s b/test/ELF/aarch64-fpic-prel64.s
index 53a4820962b6..0f5f08a88eb3 100644
--- a/test/ELF/aarch64-fpic-prel64.s
+++ b/test/ELF/aarch64-fpic-prel64.s
@@ -1,7 +1,7 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-none-freebsd %s -o %t.o
// RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-// CHECK: relocation R_AARCH64_PREL64 cannot be used against shared object; recompile with -fPIC.
+// CHECK: {{.*}}:(.data+0x0): relocation R_AARCH64_PREL64 cannot be used against shared object; recompile with -fPIC.
.data
.xword foo - .
diff --git a/test/ELF/aarch64-gnu-ifunc-plt.s b/test/ELF/aarch64-gnu-ifunc-plt.s
new file mode 100644
index 000000000000..50e63f5257eb
--- /dev/null
+++ b/test/ELF/aarch64-gnu-ifunc-plt.s
@@ -0,0 +1,85 @@
+// RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux-gnu %S/Inputs/shared2.s -o %t1.o
+// RUN: ld.lld %t1.o --shared -o %t.so
+// RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux-gnu %s -o %t.o
+// RUN: ld.lld %t.so %t.o -o %tout
+// RUN: llvm-objdump -d %tout | FileCheck %s --check-prefix=DISASM
+// RUN: llvm-objdump -s %tout | FileCheck %s --check-prefix=GOTPLT
+// RUN: llvm-readobj -r -dynamic-table %tout | FileCheck %s
+// REQUIRES: aarch64
+
+// Check that the IRELATIVE relocations are after the JUMP_SLOT in the plt
+// CHECK: Relocations [
+// CHECK-NEXT: Section (4) .rela.plt {
+// CHECK: 0x40018 R_AARCH64_JUMP_SLOT bar2 0x0
+// CHECK-NEXT: 0x40020 R_AARCH64_JUMP_SLOT zed2 0x0
+// CHECK-NEXT: 0x40028 R_AARCH64_IRELATIVE - 0x20000
+// CHECK-NEXT: 0x40030 R_AARCH64_IRELATIVE - 0x20004
+// CHECK-NEXT: }
+// CHECK-NEXT: ]
+
+// Check that .got.plt entries point back to PLT header
+// GOTPLT: Contents of section .got.plt:
+// GOTPLT-NEXT: 40000 00000000 00000000 00000000 00000000
+// GOTPLT-NEXT: 40010 00000000 00000000 20000200 00000000
+// GOTPLT-NEXT: 40020 20000200 00000000 20000200 00000000
+// GOTPLT-NEXT: 40030 20000200 00000000
+
+// Check that the PLTRELSZ tag includes the IRELATIVE relocations
+// CHECK: DynamicSection [
+// CHECK: 0x0000000000000002 PLTRELSZ 96 (bytes)
+
+// Check that a PLT header is written and the ifunc entries appear last
+// DISASM: Disassembly of section .text:
+// DISASM-NEXT: foo:
+// DISASM-NEXT: 20000: c0 03 5f d6 ret
+// DISASM: bar:
+// DISASM-NEXT: 20004: c0 03 5f d6 ret
+// DISASM: _start:
+// DISASM-NEXT: 20008: 16 00 00 94 bl #88
+// DISASM-NEXT: 2000c: 19 00 00 94 bl #100
+// DISASM-NEXT: 20010: 0c 00 00 94 bl #48
+// DISASM-NEXT: 20014: 0f 00 00 94 bl #60
+// DISASM-NEXT: Disassembly of section .plt:
+// DISASM-NEXT: .plt:
+// DISASM-NEXT: 20020: f0 7b bf a9 stp x16, x30, [sp, #-16]!
+// DISASM-NEXT: 20024: 10 01 00 90 adrp x16, #131072
+// DISASM-NEXT: 20028: 11 0a 40 f9 ldr x17, [x16, #16]
+// DISASM-NEXT: 2002c: 10 42 00 91 add x16, x16, #16
+// DISASM-NEXT: 20030: 20 02 1f d6 br x17
+// DISASM-NEXT: 20034: 1f 20 03 d5 nop
+// DISASM-NEXT: 20038: 1f 20 03 d5 nop
+// DISASM-NEXT: 2003c: 1f 20 03 d5 nop
+// DISASM-NEXT: 20040: 10 01 00 90 adrp x16, #131072
+// DISASM-NEXT: 20044: 11 0e 40 f9 ldr x17, [x16, #24]
+// DISASM-NEXT: 20048: 10 62 00 91 add x16, x16, #24
+// DISASM-NEXT: 2004c: 20 02 1f d6 br x17
+// DISASM-NEXT: 20050: 10 01 00 90 adrp x16, #131072
+// DISASM-NEXT: 20054: 11 12 40 f9 ldr x17, [x16, #32]
+// DISASM-NEXT: 20058: 10 82 00 91 add x16, x16, #32
+// DISASM-NEXT: 2005c: 20 02 1f d6 br x17
+// DISASM-NEXT: 20060: 10 01 00 90 adrp x16, #131072
+// DISASM-NEXT: 20064: 11 16 40 f9 ldr x17, [x16, #40]
+// DISASM-NEXT: 20068: 10 a2 00 91 add x16, x16, #40
+// DISASM-NEXT: 2006c: 20 02 1f d6 br x17
+// DISASM-NEXT: 20070: 10 01 00 90 adrp x16, #131072
+// DISASM-NEXT: 20074: 11 1a 40 f9 ldr x17, [x16, #48]
+// DISASM-NEXT: 20078: 10 c2 00 91 add x16, x16, #48
+// DISASM-NEXT: 2007c: 20 02 1f d6 br x17
+
+.text
+.type foo STT_GNU_IFUNC
+.globl foo
+foo:
+ ret
+
+.type bar STT_GNU_IFUNC
+.globl bar
+bar:
+ ret
+
+.globl _start
+_start:
+ bl foo
+ bl bar
+ bl bar2
+ bl zed2
diff --git a/test/ELF/aarch64-gnu-ifunc.s b/test/ELF/aarch64-gnu-ifunc.s
index 351d0ed0440a..46f4a292d2ea 100644
--- a/test/ELF/aarch64-gnu-ifunc.s
+++ b/test/ELF/aarch64-gnu-ifunc.s
@@ -15,15 +15,15 @@
// CHECK-NEXT: Address: [[RELA:.*]]
// CHECK-NEXT: Offset: 0x158
// CHECK-NEXT: Size: 48
-// CHECK-NEXT: Link: 5
+// CHECK-NEXT: Link: 6
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 8
// CHECK-NEXT: EntrySize: 24
// CHECK-NEXT: }
// CHECK: Relocations [
// CHECK-NEXT: Section ({{.*}}) .rela.plt {
-// CHECK-NEXT: 0x12018 R_AARCH64_IRELATIVE
-// CHECK-NEXT: 0x12020 R_AARCH64_IRELATIVE
+// CHECK-NEXT: 0x30000 R_AARCH64_IRELATIVE
+// CHECK-NEXT: 0x30008 R_AARCH64_IRELATIVE
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK: Symbols [
@@ -38,7 +38,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: $x.0
-// CHECK-NEXT: Value: 0x11000
+// CHECK-NEXT: Value: 0x20000
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -69,7 +69,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: _start
-// CHECK-NEXT: Value: 0x11008
+// CHECK-NEXT: Value: 0x20008
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: None
@@ -78,7 +78,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: bar
-// CHECK-NEXT: Value: 0x11004
+// CHECK-NEXT: Value: 0x20004
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: GNU_IFunc
@@ -87,7 +87,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: foo
-// CHECK-NEXT: Value: 0x11000
+// CHECK-NEXT: Value: 0x20000
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: GNU_IFunc
@@ -98,34 +98,27 @@
// 344 = 0x158
// 392 = 0x188
-// DISASM: Disassembly of section .text:
+
+// DISASM: Disassembly of section .text:
// DISASM-NEXT: foo:
-// DISASM-NEXT: 11000: c0 03 5f d6 ret
-// DISASM: bar:
-// DISASM-NEXT: 11004: c0 03 5f d6 ret
+// DISASM-NEXT: 20000: c0 03 5f d6 ret
+// DISASM: bar:
+// DISASM-NEXT: 20004: c0 03 5f d6 ret
// DISASM: _start:
-// DISASM-NEXT: 11008: 0e 00 00 94 bl #56
-// DISASM-NEXT: 1100c: 11 00 00 94 bl #68
-// DISASM-NEXT: 11010: 42 60 05 91 add x2, x2, #344
-// DISASM-NEXT: 11014: 42 20 06 91 add x2, x2, #392
+// DISASM-NEXT: 20008: 06 00 00 94 bl #24
+// DISASM-NEXT: 2000c: 09 00 00 94 bl #36
+// DISASM-NEXT: 20010: 42 60 05 91 add x2, x2, #344
+// DISASM-NEXT: 20014: 42 20 06 91 add x2, x2, #392
// DISASM-NEXT: Disassembly of section .plt:
// DISASM-NEXT: .plt:
-// DISASM-NEXT: 11020: f0 7b bf a9 stp x16, x30, [sp, #-16]!
-// DISASM-NEXT: 11024: 10 00 00 b0 adrp x16, #4096
-// DISASM-NEXT: 11028: 11 0a 40 f9 ldr x17, [x16, #16]
-// DISASM-NEXT: 1102c: 10 42 00 91 add x16, x16, #16
-// DISASM-NEXT: 11030: 20 02 1f d6 br x17
-// DISASM-NEXT: 11034: 1f 20 03 d5 nop
-// DISASM-NEXT: 11038: 1f 20 03 d5 nop
-// DISASM-NEXT: 1103c: 1f 20 03 d5 nop
-// DISASM-NEXT: 11040: 10 00 00 b0 adrp x16, #4096
-// DISASM-NEXT: 11044: 11 0e 40 f9 ldr x17, [x16, #24]
-// DISASM-NEXT: 11048: 10 62 00 91 add x16, x16, #24
-// DISASM-NEXT: 1104c: 20 02 1f d6 br x17
-// DISASM-NEXT: 11050: 10 00 00 b0 adrp x16, #4096
-// DISASM-NEXT: 11054: 11 12 40 f9 ldr x17, [x16, #32]
-// DISASM-NEXT: 11058: 10 82 00 91 add x16, x16, #32
-// DISASM-NEXT: 1105c: 20 02 1f d6 br x17
+// DISASM-NEXT: 20020: 90 00 00 90 adrp x16, #65536
+// DISASM-NEXT: 20024: 11 02 40 f9 ldr x17, [x16]
+// DISASM-NEXT: 20028: 10 02 00 91 add x16, x16, #0
+// DISASM-NEXT: 2002c: 20 02 1f d6 br x17
+// DISASM-NEXT: 20030: 90 00 00 90 adrp x16, #65536
+// DISASM-NEXT: 20034: 11 06 40 f9 ldr x17, [x16, #8]
+// DISASM-NEXT: 20038: 10 22 00 91 add x16, x16, #8
+// DISASM-NEXT: 2003c: 20 02 1f d6 br x17
.text
.type foo STT_GNU_IFUNC
diff --git a/test/ELF/aarch64-got-reloc.s b/test/ELF/aarch64-got-reloc.s
new file mode 100644
index 000000000000..fec1ad6a1d22
--- /dev/null
+++ b/test/ELF/aarch64-got-reloc.s
@@ -0,0 +1,30 @@
+// REQUIRES: aarch64
+// RUN: llvm-mc -filetype=obj -triple=aarch64-none-freebsd %s -o %t.o
+// RUN: ld.lld %t.o -o %t
+// RUN: llvm-readobj -s --section-data %t | FileCheck %s
+
+// CHECK: Name: .got
+// CHECK-NEXT: Type: SHT_PROGBITS
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: SHF_WRITE
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x30000
+// CHECK-NEXT: Offset: 0x20000
+// CHECK-NEXT: Size: 8
+// CHECK-NEXT: Link: 0
+// CHECK-NEXT: Info: 0
+// CHECK-NEXT: AddressAlignment: 8
+// CHECK-NEXT: EntrySize: 0
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: 0000: 00000000 00000000 |........|
+// CHECK-NEXT: )
+
+ .globl _start
+_start:
+ adrp x8, :got:foo
+ ldr x8, [x8, :got_lo12:foo]
+ ldr w0, [x8]
+ ret
+
+ .weak foo
diff --git a/test/ELF/aarch64-prel16.s b/test/ELF/aarch64-prel16.s
index 083cda908608..4ae1f87e2081 100644
--- a/test/ELF/aarch64-prel16.s
+++ b/test/ELF/aarch64-prel16.s
@@ -14,7 +14,7 @@ _start:
// the change of the address of the .data section.
// You may found the correct address in the aarch64_abs16.s test,
// if it is already fixed. Then, update addends accordingly.
-// RUN: ld.lld %t.o %t256.o -o %t2
+// RUN: ld.lld -z max-page-size=4096 %t.o %t256.o -o %t2
// RUN: llvm-objdump -s -section=.data %t2 | FileCheck %s
// CHECK: Contents of section .data:
diff --git a/test/ELF/aarch64-prel32.s b/test/ELF/aarch64-prel32.s
index c440b984eaa6..302f4521f46b 100644
--- a/test/ELF/aarch64-prel32.s
+++ b/test/ELF/aarch64-prel32.s
@@ -14,7 +14,7 @@ _start:
// the change of the address of the .data section.
// You may found the correct address in the aarch64_abs32.s test,
// if it is already fixed. Then, update addends accordingly.
-// RUN: ld.lld %t.o %t256.o -o %t2
+// RUN: ld.lld -z max-page-size=4096 %t.o %t256.o -o %t2
// RUN: llvm-objdump -s -section=.data %t2 | FileCheck %s
// CHECK: Contents of section .data:
diff --git a/test/ELF/aarch64-relocs.s b/test/ELF/aarch64-relocs.s
index 90541329587a..9d02bd599b25 100644
--- a/test/ELF/aarch64-relocs.s
+++ b/test/ELF/aarch64-relocs.s
@@ -1,5 +1,6 @@
# RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-freebsd %s -o %t
-# RUN: ld.lld %t -o %t2
+# RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-freebsd %p/Inputs/uabs_label.s -o %t2.o
+# RUN: ld.lld %t %t2.o -o %t2
# RUN: llvm-objdump -d %t2 | FileCheck %s
# REQUIRES: aarch64
@@ -23,13 +24,13 @@ mystr:
.asciz "blah"
.size mystr, 4
-# S = 0x11012, A = 0x4, P = 0x11012
+# S = 0x20012, A = 0x4, P = 0x20012
# PAGE(S + A) = 0x11000
# PAGE(P) = 0x11000
#
# CHECK: Disassembly of section .R_AARCH64_ADR_PREL_PG_H121:
# CHECK-NEXT: $x.2:
-# CHECK-NEXT: 11012: 01 00 00 90 adrp x1, #0
+# CHECK-NEXT: 20012: 01 00 00 90 adrp x1, #0
.section .R_AARCH64_ADD_ABS_LO12_NC,"ax",@progbits
add x0, x0, :lo12:.L.str
@@ -37,13 +38,13 @@ mystr:
.asciz "blah"
.size mystr, 4
-# S = 0x1101b, A = 0x4
+# S = 0x2001b, A = 0x4
# R = (S + A) & 0xFFF = 0x1f
# R << 10 = 0x7c00
#
# CHECK: Disassembly of section .R_AARCH64_ADD_ABS_LO12_NC:
# CHECK-NEXT: $x.4:
-# CHECK-NEXT: 1101b: 00 7c 00 91 add x0, x0, #31
+# CHECK-NEXT: 2001b: 00 7c 00 91 add x0, x0, #31
.section .R_AARCH64_LDST64_ABS_LO12_NC,"ax",@progbits
ldr x28, [x27, :lo12:foo]
@@ -51,12 +52,12 @@ foo:
.asciz "foo"
.size mystr, 3
-# S = 0x11024, A = 0x4
+# S = 0x20024, A = 0x4
# R = ((S + A) & 0xFFF) << 7 = 0x00001400
# 0x00001400 | 0xf940177c = 0xf940177c
# CHECK: Disassembly of section .R_AARCH64_LDST64_ABS_LO12_NC:
# CHECK-NEXT: $x.6:
-# CHECK-NEXT: 11024: 7c 17 40 f9 ldr x28, [x27, #40]
+# CHECK-NEXT: 20024: 7c 17 40 f9 ldr x28, [x27, #40]
.section .SUB,"ax",@progbits
nop
@@ -65,33 +66,33 @@ sub:
# CHECK: Disassembly of section .SUB:
# CHECK-NEXT: $x.8:
-# CHECK-NEXT: 1102c: 1f 20 03 d5 nop
+# CHECK-NEXT: 2002c: 1f 20 03 d5 nop
# CHECK: sub:
-# CHECK-NEXT: 11030: 1f 20 03 d5 nop
+# CHECK-NEXT: 20030: 1f 20 03 d5 nop
.section .R_AARCH64_CALL26,"ax",@progbits
call26:
bl sub
-# S = 0x1102c, A = 0x4, P = 0x11034
+# S = 0x2002c, A = 0x4, P = 0x20034
# R = S + A - P = -0x4 = 0xfffffffc
# (R & 0x0ffffffc) >> 2 = 0x03ffffff
# 0x94000000 | 0x03ffffff = 0x97ffffff
# CHECK: Disassembly of section .R_AARCH64_CALL26:
# CHECK-NEXT: call26:
-# CHECK-NEXT: 11034: ff ff ff 97 bl #-4
+# CHECK-NEXT: 20034: ff ff ff 97 bl #-4
.section .R_AARCH64_JUMP26,"ax",@progbits
jump26:
b sub
-# S = 0x1102c, A = 0x4, P = 0x11038
+# S = 0x2002c, A = 0x4, P = 0x20038
# R = S + A - P = -0x8 = 0xfffffff8
# (R & 0x0ffffffc) >> 2 = 0x03fffffe
# 0x14000000 | 0x03fffffe = 0x17fffffe
# CHECK: Disassembly of section .R_AARCH64_JUMP26:
# CHECK-NEXT: jump26:
-# CHECK-NEXT: 11038: fe ff ff 17 b #-8
+# CHECK-NEXT: 20038: fe ff ff 17 b #-8
.section .R_AARCH64_LDST32_ABS_LO12_NC,"ax",@progbits
ldst32:
@@ -100,12 +101,12 @@ foo32:
.asciz "foo"
.size mystr, 3
-# S = 0x1103c, A = 0x4
+# S = 0x2003c, A = 0x4
# R = ((S + A) & 0xFFC) << 8 = 0x00004000
# 0x00004000 | 0xbd4000a4 = 0xbd4040a4
# CHECK: Disassembly of section .R_AARCH64_LDST32_ABS_LO12_NC:
# CHECK-NEXT: ldst32:
-# CHECK-NEXT: 1103c: a4 40 40 bd ldr s4, [x5, #64]
+# CHECK-NEXT: 2003c: a4 40 40 bd ldr s4, [x5, #64]
.section .R_AARCH64_LDST8_ABS_LO12_NC,"ax",@progbits
ldst8:
@@ -114,12 +115,12 @@ foo8:
.asciz "foo"
.size mystr, 3
-# S = 0x11044, A = 0x4
+# S = 0x20044, A = 0x4
# R = ((S + A) & 0xFFF) << 10 = 0x00012000
# 0x00012000 | 0x398001ab = 0x398121ab
# CHECK: Disassembly of section .R_AARCH64_LDST8_ABS_LO12_NC:
# CHECK-NEXT: ldst8:
-# CHECK-NEXT: 11044: ab 21 81 39 ldrsb x11, [x13, #72]
+# CHECK-NEXT: 20044: ab 21 81 39 ldrsb x11, [x13, #72]
.section .R_AARCH64_LDST128_ABS_LO12_NC,"ax",@progbits
ldst128:
@@ -128,25 +129,46 @@ foo128:
.asciz "foo"
.size mystr, 3
-# S = 0x1104c, A = 0x4
+# S = 0x2004c, A = 0x4
# R = ((S + A) & 0xFF8) << 6 = 0x00001400
# 0x00001400 | 0x3dc00274 = 0x3dc01674
# CHECK: Disassembly of section .R_AARCH64_LDST128_ABS_LO12_NC:
# CHECK: ldst128:
-# CHECK: 1104c: 74 16 c0 3d ldr q20, [x19, #80]
+# CHECK: 2004c: 74 16 c0 3d ldr q20, [x19, #80]
#foo128:
-# 11050: 66 6f 6f 00 .word
+# 20050: 66 6f 6f 00 .word
.section .R_AARCH64_LDST16_ABS_LO12_NC,"ax",@progbits
ldst16:
ldr h17, [x19, :lo12:foo16]
+ ldrh w1, [x19, :lo12:foo16]
+ ldrh w2, [x19, :lo12:foo16 + 2]
foo16:
.asciz "foo"
- .size mystr, 3
+ .size mystr, 4
-# S = 0x11054, A = 0x4
+# S = 0x20054, A = 0x4
# R = ((S + A) & 0x0FFC) << 9 = 0xb000
# 0xb000 | 0x7d400271 = 0x7d40b271
# CHECK: Disassembly of section .R_AARCH64_LDST16_ABS_LO12_NC:
# CHECK-NEXT: ldst16:
-# CHECK-NEXT: 11054: 71 b2 40 7d ldr h17, [x19, #88]
+# CHECK-NEXT: 20054: 71 c2 40 7d ldr h17, [x19, #96]
+# CHECK-NEXT: 20058: 61 c2 40 79 ldrh w1, [x19, #96]
+# CHECK-NEXT: 2005c: 62 c6 40 79 ldrh w2, [x19, #98]
+
+.section .R_AARCH64_MOVW_UABS,"ax",@progbits
+movz1:
+ movk x12, #:abs_g0_nc:uabs_label
+ movk x13, #:abs_g1_nc:uabs_label
+ movk x14, #:abs_g2_nc:uabs_label
+ movz x15, #:abs_g3:uabs_label
+ movk x16, #:abs_g3:uabs_label
+
+## 4222124650659840 == (0xF << 48)
+# CHECK: Disassembly of section .R_AARCH64_MOVW_UABS:
+# CHECK-NEXT: movz1:
+# CHECK-NEXT: 8c 01 80 f2 movk x12, #12
+# CHECK-NEXT: ad 01 a0 f2 movk x13, #13, lsl #16
+# CHECK-NEXT: ce 01 c0 f2 movk x14, #14, lsl #32
+# CHECK-NEXT: ef 01 e0 d2 mov x15, #4222124650659840
+# CHECK-NEXT: f0 01 e0 f2 movk x16, #15, lsl #48
diff --git a/test/ELF/aarch64-tls-gdie.s b/test/ELF/aarch64-tls-gdie.s
index 709cc53a8c47..c66ea6cfcadb 100644
--- a/test/ELF/aarch64-tls-gdie.s
+++ b/test/ELF/aarch64-tls-gdie.s
@@ -21,14 +21,14 @@ _start:
// SEC-NEXT: SHF_ALLOC
// SEC-NEXT: SHF_WRITE
// SEC-NEXT: ]
-// SEC-NEXT: Address: 0x120B0
+// SEC-NEXT: Address: 0x300B0
-// page(0x120B0) - page(0x11004) = 4096
+// page(0x300B0) - page(0x20004) = 65536
// 0x0B0 = 176
// CHECK: _start:
-// CHECK-NEXT: 11000: {{.*}} nop
-// CHECK-NEXT: 11004: {{.*}} adrp x0, #4096
-// CHECK-NEXT: 11008: {{.*}} ldr x0, [x0, #176]
-// CHECK-NEXT: 1100c: {{.*}} nop
-// CHECK-NEXT: 11010: {{.*}} nop
+// CHECK-NEXT: 20000: {{.*}} nop
+// CHECK-NEXT: 20004: {{.*}} adrp x0, #65536
+// CHECK-NEXT: 20008: {{.*}} ldr x0, [x0, #176]
+// CHECK-NEXT: 2000c: {{.*}} nop
+// CHECK-NEXT: 20010: {{.*}} nop
diff --git a/test/ELF/aarch64-tls-gdle.s b/test/ELF/aarch64-tls-gdle.s
index dc0c02a31d1c..a111cacefd29 100644
--- a/test/ELF/aarch64-tls-gdle.s
+++ b/test/ELF/aarch64-tls-gdle.s
@@ -12,10 +12,10 @@
# TCB size = 0x16 and foo is first element from TLS register.
# CHECK: Disassembly of section .text:
# CHECK: _start:
-# CHECK: 11000: 00 00 a0 d2 movz x0, #0, lsl #16
-# CHECK: 11004: 00 02 80 f2 movk x0, #16
-# CHECK: 11008: 1f 20 03 d5 nop
-# CHECK: 1100c: 1f 20 03 d5 nop
+# CHECK: 20000: 00 00 a0 d2 movz x0, #0, lsl #16
+# CHECK: 20004: 00 02 80 f2 movk x0, #16
+# CHECK: 20008: 1f 20 03 d5 nop
+# CHECK: 2000c: 1f 20 03 d5 nop
.globl _start
_start:
diff --git a/test/ELF/aarch64-tls-ie.s b/test/ELF/aarch64-tls-ie.s
index 0cec402b294a..81ca326aff54 100644
--- a/test/ELF/aarch64-tls-ie.s
+++ b/test/ELF/aarch64-tls-ie.s
@@ -15,8 +15,8 @@
#RELOC-NEXT: SHF_ALLOC
#RELOC-NEXT: SHF_WRITE
#RELOC-NEXT: ]
-#RELOC-NEXT: Address: 0x120B0
-#RELOC-NEXT: Offset: 0x20B0
+#RELOC-NEXT: Address: 0x300B0
+#RELOC-NEXT: Offset: 0x200B0
#RELOC-NEXT: Size: 16
#RELOC-NEXT: Link: 0
#RELOC-NEXT: Info: 0
@@ -25,21 +25,21 @@
#RELOC-NEXT: }
#RELOC: Relocations [
#RELOC-NEXT: Section ({{.*}}) .rela.dyn {
-#RELOC-NEXT: 0x120B8 R_AARCH64_TLS_TPREL64 bar 0x0
-#RELOC-NEXT: 0x120B0 R_AARCH64_TLS_TPREL64 foo 0x0
+#RELOC-NEXT: 0x300B8 R_AARCH64_TLS_TPREL64 bar 0x0
+#RELOC-NEXT: 0x300B0 R_AARCH64_TLS_TPREL64 foo 0x0
#RELOC-NEXT: }
#RELOC-NEXT:]
-# Page(0x120B0) - Page(0x11000) = 0x1000 = 4096
-# 0x120B0 & 0xff8 = 0xB0 = 176
-# Page(0x120B8) - Page(0x11000) = 0x1000 = 4096
-# 0x120B8 & 0xff8 = 0xB8 = 184
+# Page(0x300B0) - Page(0x20000) = 0x10000 = 65536
+# 0x300B0 & 0xff8 = 0xB0 = 176
+# Page(0x300B8) - Page(0x20000) = 0x10000 = 65536
+# 0x300B8 & 0xff8 = 0xB8 = 184
#CHECK: Disassembly of section .text:
#CHECK: _start:
-#CHECK: 11000: 00 00 00 b0 adrp x0, #4096
-#CHECK: 11004: 00 58 40 f9 ldr x0, [x0, #176]
-#CHECK: 11008: 00 00 00 b0 adrp x0, #4096
-#CHECK: 1100c: 00 5c 40 f9 ldr x0, [x0, #184]
+#CHECK: 20000: 80 00 00 90 adrp x0, #65536
+#CHECK: 20004: 00 58 40 f9 ldr x0, [x0, #176]
+#CHECK: 20008: 80 00 00 90 adrp x0, #65536
+#CHECK: 2000c: 00 5c 40 f9 ldr x0, [x0, #184]
.globl _start
_start:
diff --git a/test/ELF/aarch64-tls-iele.s b/test/ELF/aarch64-tls-iele.s
index 91efc092a2cf..208b5cdd5446 100644
--- a/test/ELF/aarch64-tls-iele.s
+++ b/test/ELF/aarch64-tls-iele.s
@@ -12,10 +12,10 @@
# TCB size = 0x16 and foo is first element from TLS register.
# CHECK: Disassembly of section .text:
# CHECK: _start:
-# CHECK-NEXT: 11000: 00 00 a0 d2 movz x0, #0, lsl #16
-# CHECK-NEXT: 11004: 80 02 80 f2 movk x0, #20
-# CHECK-NEXT: 11008: 00 00 a0 d2 movz x0, #0, lsl #16
-# CHECK-NEXT: 1100c: 00 02 80 f2 movk x0, #16
+# CHECK-NEXT: 20000: 00 00 a0 d2 movz x0, #0, lsl #16
+# CHECK-NEXT: 20004: 80 02 80 f2 movk x0, #20
+# CHECK-NEXT: 20008: 00 00 a0 d2 movz x0, #0, lsl #16
+# CHECK-NEXT: 2000c: 00 02 80 f2 movk x0, #16
.section .tdata
.align 2
diff --git a/test/ELF/aarch64-tls-le.s b/test/ELF/aarch64-tls-le.s
index 22bd0ef2dad7..df943f7f091a 100644
--- a/test/ELF/aarch64-tls-le.s
+++ b/test/ELF/aarch64-tls-le.s
@@ -17,9 +17,9 @@ _start:
# TCB size = 0x16 and foo is first element from TLS register.
#CHECK: Disassembly of section .text:
#CHECK: _start:
-#CHECK: 11000: 40 d0 3b d5 mrs x0, TPIDR_EL0
-#CHECK: 11004: 00 00 00 91 add x0, x0, #0
-#CHECK: 11008: 00 40 00 91 add x0, x0, #16
+#CHECK: 20000: 40 d0 3b d5 mrs x0, TPIDR_EL0
+#CHECK: 20004: 00 00 40 91 add x0, x0, #0, lsl #12
+#CHECK: 20008: 00 40 00 91 add x0, x0, #16
.type v1,@object
.section .tbss,"awT",@nobits
diff --git a/test/ELF/aarch64-tls-static.s b/test/ELF/aarch64-tls-static.s
index c8508ee58b7f..24306d5d7ee3 100644
--- a/test/ELF/aarch64-tls-static.s
+++ b/test/ELF/aarch64-tls-static.s
@@ -23,15 +23,15 @@ bar:
// SEC-NEXT: SHF_ALLOC
// SEC-NEXT: SHF_WRITE
// SEC-NEXT: ]
-// SEC-NEXT: Address: 0x2098
-// SEC-NEXT: Offset: 0x2098
+// SEC-NEXT: Address: 0x20098
+// SEC-NEXT: Offset: 0x20098
// SEC-NEXT: Size: 16
-// page(0x2098) - page(0x1000) = 4096
+// page(0x20098) - page(0x10000) = 65536
// 0x98 = 152
// CHECK: foo:
-// CHECK-NEXT: 1000: {{.*}} adrp x0, #4096
-// CHECK-NEXT: 1004: {{.*}} ldr x1, [x0, #152]
-// CHECK-NEXT: 1008: {{.*}} add x0, x0, #152
-// CHECK-NEXT: 100c: {{.*}} blr x1
+// CHECK-NEXT: 10000: {{.*}} adrp x0, #65536
+// CHECK-NEXT: 10004: {{.*}} ldr x1, [x0, #152]
+// CHECK-NEXT: 10008: {{.*}} add x0, x0, #152
+// CHECK-NEXT: 1000c: {{.*}} blr x1
diff --git a/test/ELF/aarch64-tlsdesc.s b/test/ELF/aarch64-tlsdesc.s
index f8c73aff243d..09dfd04d8ac9 100644
--- a/test/ELF/aarch64-tlsdesc.s
+++ b/test/ELF/aarch64-tlsdesc.s
@@ -4,21 +4,69 @@
// RUN: llvm-objdump -d %t.so | FileCheck %s
// RUN: llvm-readobj -r %t.so | FileCheck --check-prefix=REL %s
+ .text
adrp x0, :tlsdesc:a
ldr x1, [x0, :tlsdesc_lo12:a]
add x0, x0, :tlsdesc_lo12:a
.tlsdesccall a
blr x1
-// CHECK: 1000: {{.*}} adrp x0, #4096
-// CHECK-NEXT: 1004: {{.*}} ldr x1, [x0, #144]
-// CHECK-NEXT: 1008: {{.*}} add x0, x0, #144
-// CHECK-NEXT: 100c: {{.*}} blr x1
+// Create relocation against local TLS symbols where linker should
+// create target specific dynamic TLSDESC relocation where addend is
+// the symbol VMA in tls block.
+// CHECK: 10000: {{.*}} adrp x0, #65536
+// CHECK-NEXT: 10004: {{.*}} ldr x1, [x0, #144]
+// CHECK-NEXT: 10008: {{.*}} add x0, x0, #144
+// CHECK-NEXT: 1000c: {{.*}} blr x1
+
+ adrp x0, :tlsdesc:local1
+ ldr x1, [x0, :tlsdesc_lo12:local1]
+ add x0, x0, :tlsdesc_lo12:local1
+ .tlsdesccall a
+ blr x1
+
+// CHECK: 10010: {{.*}} adrp x0, #65536
+// CHECK-NEXT: 10014: {{.*}} ldr x1, [x0, #160]
+// CHECK-NEXT: 10018: {{.*}} add x0, x0, #160
+// CHECK-NEXT: 1001c: {{.*}} blr x1
+
+ adrp x0, :tlsdesc:local2
+ ldr x1, [x0, :tlsdesc_lo12:local2]
+ add x0, x0, :tlsdesc_lo12:local2
+ .tlsdesccall a
+ blr x1
+
+// CHECK: 10020: {{.*}} adrp x0, #65536
+// CHECK-NEXT: 10024: {{.*}} ldr x1, [x0, #176]
+// CHECK-NEXT: 10028: {{.*}} add x0, x0, #176
+// CHECK-NEXT: 1002c: {{.*}} blr x1
+
+ .section .tbss,"awT",@nobits
+ .type local1,@object
+ .p2align 2
+local1:
+ .word 0
+ .size local1, 4
+
+ .type local2,@object
+ .p2align 3
+local2:
+ .xword 0
+ .size local2, 8
+
+
+// 0x1000 + 4096 + 160 = 0x20A0
+// 0x1000 + 4096 + 176 = 0x20B0
// 0x1000 + 4096 + 144 = 0x2090
+// R_AARCH64_TLSDESC - 0x0 -> start of tls block
+// R_AARCH64_TLSDESC - 0x8 -> align (sizeof (local1), 8)
+
// REL: Relocations [
// REL-NEXT: Section (4) .rela.dyn {
-// REL-NEXT: 0x2090 R_AARCH64_TLSDESC a 0x0
+// REL-NEXT: 0x200A0 R_AARCH64_TLSDESC - 0x0
+// REL-NEXT: 0x200B0 R_AARCH64_TLSDESC - 0x8
+// REL-NEXT: 0x20090 R_AARCH64_TLSDESC a 0x0
// REL-NEXT: }
// REL-NEXT: ]
diff --git a/test/ELF/aarch64-tstbr14-reloc.s b/test/ELF/aarch64-tstbr14-reloc.s
index 1dc1bdf3c09a..6523a9c1cc9d 100644
--- a/test/ELF/aarch64-tstbr14-reloc.s
+++ b/test/ELF/aarch64-tstbr14-reloc.s
@@ -7,25 +7,25 @@
# RUN: llvm-readobj -s -r %t3 | FileCheck -check-prefix=DSOREL %s
# REQUIRES: aarch64
-# 0x1101c - 28 = 0x11000
-# 0x11020 - 16 = 0x11010
-# 0x11024 - 36 = 0x11000
-# 0x11028 - 24 = 0x11010
+# 0x1101c - 28 = 0x20000
+# 0x11020 - 16 = 0x20010
+# 0x11024 - 36 = 0x20000
+# 0x11028 - 24 = 0x20010
# CHECK: Disassembly of section .text:
# CHECK-NEXT: _foo:
-# CHECK-NEXT: 11000: {{.*}} nop
-# CHECK-NEXT: 11004: {{.*}} nop
-# CHECK-NEXT: 11008: {{.*}} nop
-# CHECK-NEXT: 1100c: {{.*}} nop
+# CHECK-NEXT: 20000: {{.*}} nop
+# CHECK-NEXT: 20004: {{.*}} nop
+# CHECK-NEXT: 20008: {{.*}} nop
+# CHECK-NEXT: 2000c: {{.*}} nop
# CHECK: _bar:
-# CHECK-NEXT: 11010: {{.*}} nop
-# CHECK-NEXT: 11014: {{.*}} nop
-# CHECK-NEXT: 11018: {{.*}} nop
+# CHECK-NEXT: 20010: {{.*}} nop
+# CHECK-NEXT: 20014: {{.*}} nop
+# CHECK-NEXT: 20018: {{.*}} nop
# CHECK: _start:
-# CHECK-NEXT: 1101c: {{.*}} tbnz w3, #15, #-28
-# CHECK-NEXT: 11020: {{.*}} tbnz w3, #15, #-16
-# CHECK-NEXT: 11024: {{.*}} tbz x6, #45, #-36
-# CHECK-NEXT: 11028: {{.*}} tbz x6, #45, #-24
+# CHECK-NEXT: 2001c: {{.*}} tbnz w3, #15, #-28
+# CHECK-NEXT: 20020: {{.*}} tbnz w3, #15, #-16
+# CHECK-NEXT: 20024: {{.*}} tbz x6, #45, #-36
+# CHECK-NEXT: 20028: {{.*}} tbz x6, #45, #-24
#DSOREL: Section {
#DSOREL: Index:
@@ -35,8 +35,8 @@
#DSOREL-NEXT: SHF_ALLOC
#DSOREL-NEXT: SHF_WRITE
#DSOREL-NEXT: ]
-#DSOREL-NEXT: Address: 0x3000
-#DSOREL-NEXT: Offset: 0x3000
+#DSOREL-NEXT: Address: 0x30000
+#DSOREL-NEXT: Offset: 0x30000
#DSOREL-NEXT: Size: 40
#DSOREL-NEXT: Link: 0
#DSOREL-NEXT: Info: 0
@@ -45,48 +45,48 @@
#DSOREL-NEXT: }
#DSOREL: Relocations [
#DSOREL-NEXT: Section ({{.*}}) .rela.plt {
-#DSOREL-NEXT: 0x3018 R_AARCH64_JUMP_SLOT _foo
-#DSOREL-NEXT: 0x3020 R_AARCH64_JUMP_SLOT _bar
+#DSOREL-NEXT: 0x30018 R_AARCH64_JUMP_SLOT _foo
+#DSOREL-NEXT: 0x30020 R_AARCH64_JUMP_SLOT _bar
#DSOREL-NEXT: }
#DSOREL-NEXT:]
#DSO: Disassembly of section .text:
#DSO-NEXT: _foo:
-#DSO-NEXT: 1000: {{.*}} nop
-#DSO-NEXT: 1004: {{.*}} nop
-#DSO-NEXT: 1008: {{.*}} nop
-#DSO-NEXT: 100c: {{.*}} nop
+#DSO-NEXT: 10000: {{.*}} nop
+#DSO-NEXT: 10004: {{.*}} nop
+#DSO-NEXT: 10008: {{.*}} nop
+#DSO-NEXT: 1000c: {{.*}} nop
#DSO: _bar:
-#DSO-NEXT: 1010: {{.*}} nop
-#DSO-NEXT: 1014: {{.*}} nop
-#DSO-NEXT: 1018: {{.*}} nop
+#DSO-NEXT: 10010: {{.*}} nop
+#DSO-NEXT: 10014: {{.*}} nop
+#DSO-NEXT: 10018: {{.*}} nop
#DSO: _start:
-# 0x101c + 52 = 0x1050 = PLT[1]
-# 0x1020 + 64 = 0x1060 = PLT[2]
-# 0x1024 + 44 = 0x1050 = PLT[1]
-# 0x1028 + 56 = 0x1060 = PLT[2]
-#DSO-NEXT: 101c: {{.*}} tbnz w3, #15, #52
-#DSO-NEXT: 1020: {{.*}} tbnz w3, #15, #64
-#DSO-NEXT: 1024: {{.*}} tbz x6, #45, #44
-#DSO-NEXT: 1028: {{.*}} tbz x6, #45, #56
+# 0x1001c + 52 = 0x10050 = PLT[1]
+# 0x10020 + 64 = 0x10060 = PLT[2]
+# 0x10024 + 44 = 0x10050 = PLT[1]
+# 0x10028 + 56 = 0x10060 = PLT[2]
+#DSO-NEXT: 1001c: {{.*}} tbnz w3, #15, #52
+#DSO-NEXT: 10020: {{.*}} tbnz w3, #15, #64
+#DSO-NEXT: 10024: {{.*}} tbz x6, #45, #44
+#DSO-NEXT: 10028: {{.*}} tbz x6, #45, #56
#DSO-NEXT: Disassembly of section .plt:
#DSO-NEXT: .plt:
-#DSO-NEXT: 1030: {{.*}} stp x16, x30, [sp, #-16]!
-#DSO-NEXT: 1034: {{.*}} adrp x16, #8192
-#DSO-NEXT: 1038: {{.*}} ldr x17, [x16, #16]
-#DSO-NEXT: 103c: {{.*}} add x16, x16, #16
-#DSO-NEXT: 1040: {{.*}} br x17
-#DSO-NEXT: 1044: {{.*}} nop
-#DSO-NEXT: 1048: {{.*}} nop
-#DSO-NEXT: 104c: {{.*}} nop
-#DSO-NEXT: 1050: {{.*}} adrp x16, #8192
-#DSO-NEXT: 1054: {{.*}} ldr x17, [x16, #24]
-#DSO-NEXT: 1058: {{.*}} add x16, x16, #24
-#DSO-NEXT: 105c: {{.*}} br x17
-#DSO-NEXT: 1060: {{.*}} adrp x16, #8192
-#DSO-NEXT: 1064: {{.*}} ldr x17, [x16, #32]
-#DSO-NEXT: 1068: {{.*}} add x16, x16, #32
-#DSO-NEXT: 106c: {{.*}} br x17
+#DSO-NEXT: 10030: {{.*}} stp x16, x30, [sp, #-16]!
+#DSO-NEXT: 10034: {{.*}} adrp x16, #131072
+#DSO-NEXT: 10038: {{.*}} ldr x17, [x16, #16]
+#DSO-NEXT: 1003c: {{.*}} add x16, x16, #16
+#DSO-NEXT: 10040: {{.*}} br x17
+#DSO-NEXT: 10044: {{.*}} nop
+#DSO-NEXT: 10048: {{.*}} nop
+#DSO-NEXT: 1004c: {{.*}} nop
+#DSO-NEXT: 10050: {{.*}} adrp x16, #131072
+#DSO-NEXT: 10054: {{.*}} ldr x17, [x16, #24]
+#DSO-NEXT: 10058: {{.*}} add x16, x16, #24
+#DSO-NEXT: 1005c: {{.*}} br x17
+#DSO-NEXT: 10060: {{.*}} adrp x16, #131072
+#DSO-NEXT: 10064: {{.*}} ldr x17, [x16, #32]
+#DSO-NEXT: 10068: {{.*}} add x16, x16, #32
+#DSO-NEXT: 1006c: {{.*}} br x17
.globl _start
_start:
diff --git a/test/ELF/aarch64-undefined-weak.s b/test/ELF/aarch64-undefined-weak.s
new file mode 100644
index 000000000000..74fef669ed74
--- /dev/null
+++ b/test/ELF/aarch64-undefined-weak.s
@@ -0,0 +1,45 @@
+// RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1
+// RUN: llvm-objdump -triple=aarch64-none-linux -d %t2 | FileCheck %s
+// REQUIRES: aarch64
+
+// Check that the ARM 64-bit ABI rules for undefined weak symbols are applied.
+// Branch instructions are resolved to the next instruction. Undefined
+// Symbols in relative are resolved to the place so S - P + A = A.
+
+ .weak target
+
+ .text
+ .global _start
+_start:
+// R_AARCH64_JUMP26
+ b target
+// R_AARCH64_CALL26
+ bl target
+// R_AARCH64_CONDBR19
+ b.eq target
+// R_AARCH64_TSTBR14
+ cbz x1, target
+// R_AARCH64_ADR_PREL_LO21
+ adr x0, target
+// R_AARCH64_ADR_PREL_PG_HI21
+ adrp x0, target
+// R_AARCH64_PREL32
+ .word target - .
+// R_AARCH64_PREL64
+ .xword target - .
+// R_AARCH64_PREL16
+ .hword target - .
+
+// CHECK: Disassembly of section .text:
+// 131076 = 0x20004
+// CHECK: 20000: 01 80 00 14 b #131076
+// CHECK-NEXT: 20004: 02 80 00 94 bl #131080
+// CHECK-NEXT: 20008: 60 00 10 54 b.eq #131084
+// CHECK-NEXT: 2000c: 81 00 10 b4 cbz x1, #131088
+// CHECK-NEXT: 20010: 00 00 00 10 adr x0, #0
+// CHECK-NEXT: 20014: 00 00 00 90 adrp x0, #0
+// CHECK: 20018: 00 00 00 00 .word 0x00000000
+// CHECK-NEXT: 2001c: 00 00 00 00 .word 0x00000000
+// CHECK-NEXT: 20020: 00 00 00 00 .word 0x00000000
+// CHECK-NEXT: 20024: 00 00 .short 0x0000
diff --git a/test/ELF/abs-conflict.s b/test/ELF/abs-conflict.s
new file mode 100644
index 000000000000..7837a6bae50a
--- /dev/null
+++ b/test/ELF/abs-conflict.s
@@ -0,0 +1,16 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: ld.lld %t.o %t.o -o %t.so -shared
+// RUN: llvm-readobj --dyn-symbols %t.so | FileCheck %s
+
+// CHECK: Name: foo
+// CHECK-NEXT: Value: 0x123
+
+.global foo
+foo = 0x123
+
+// RUN: echo ".global foo; foo = 0x124" > %t2.s
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %t2.s -o %t2.o
+// RUN: not ld.lld %t.o %t2.o -o %t.so -shared 2>&1 | FileCheck --check-prefix=DUP %s
+
+// DUP: duplicate symbol 'foo'
diff --git a/test/ELF/abs-hidden.s b/test/ELF/abs-hidden.s
index b93c2c692919..5fad4cf6c40d 100644
--- a/test/ELF/abs-hidden.s
+++ b/test/ELF/abs-hidden.s
@@ -25,7 +25,7 @@
// 0x2060 - (0x1000 + 8) = 1058
// CHECK-NEXT: )
-// CHECK: Name: .got (38)
+// CHECK: Name: .got
// CHECK-NEXT: Type: SHT_PROGBITS
// CHECK-NEXT: Flags [
// CHECK-NEXT: SHF_ALLOC
diff --git a/test/ELF/allow-multiple-definition.s b/test/ELF/allow-multiple-definition.s
index 89ea8c865822..e4637e1a5367 100644
--- a/test/ELF/allow-multiple-definition.s
+++ b/test/ELF/allow-multiple-definition.s
@@ -15,10 +15,10 @@
# meet symbol should be used.
# CHECK: _bar:
-# CHECK-NEXT: 11000: b8 01 00 00 00 movl $1, %eax
+# CHECK-NEXT: 201000: b8 01 00 00 00 movl $1, %eax
# REVERT: _bar:
-# REVERT-NEXT: 11000: b8 02 00 00 00 movl $2, %eax
+# REVERT-NEXT: 201000: b8 02 00 00 00 movl $2, %eax
.globl _bar
.type _bar, @function
diff --git a/test/ELF/amdgpu-entry.s b/test/ELF/amdgpu-entry.s
deleted file mode 100644
index 2a47b1778de8..000000000000
--- a/test/ELF/amdgpu-entry.s
+++ /dev/null
@@ -1,16 +0,0 @@
-# RUN: llvm-mc -filetype=obj -triple amdgcn--amdhsa -mcpu=kaveri %s -o %t.o
-# RUN: not ld.lld -e kernel0 %t.o -o %t
-
-# REQUIRES: amdgpu
-
-.hsa_code_object_version 1,0
-.hsa_code_object_isa 7,0,0,"AMD","AMDGPU"
-
-.hsatext
-.globl kernel0
-.align 256
-.amdgpu_hsa_kernel kernel0
-kernel0:
- s_endpgm
-.Lfunc_end0:
- .size kernel0, .Lfunc_end0-kernel0
diff --git a/test/ELF/amdgpu-globals.s b/test/ELF/amdgpu-globals.s
index 7f46b989b955..f30c0ed40157 100644
--- a/test/ELF/amdgpu-globals.s
+++ b/test/ELF/amdgpu-globals.s
@@ -1,5 +1,5 @@
# RUN: llvm-mc -filetype=obj -triple amdgcn--amdhsa -mcpu=kaveri %s -o %t.o
-# RUN: lld -flavor gnu %t.o -o %t
+# RUN: ld.lld %t.o -o %t
# RUN: llvm-readobj -sections -symbols -program-headers %t | FileCheck %s
# REQUIRES: amdgpu
diff --git a/test/ELF/amdgpu-relocs.s b/test/ELF/amdgpu-relocs.s
index 58c9582a84f1..965a812b14b9 100644
--- a/test/ELF/amdgpu-relocs.s
+++ b/test/ELF/amdgpu-relocs.s
@@ -1,33 +1,93 @@
# RUN: llvm-mc -filetype=obj -triple=amdgcn--amdhsa -mcpu=fiji %s -o %t.o
# RUN: ld.lld -shared %t.o -o %t.so
# RUN: llvm-readobj -r %t.so | FileCheck %s
+# RUN: llvm-objdump -s %t.so | FileCheck %s --check-prefix=OBJDUMP
# REQUIRES: amdgpu
- .text
+.text
kernel0:
- s_mov_b32 s0, common_var@GOTPCREL+4
- s_mov_b32 s0, extern_var@GOTPCREL+4
- s_mov_b32 s0, local_var+4
- s_mov_b32 s0, global_var@GOTPCREL+4
- s_mov_b32 s0, weak_var@GOTPCREL+4
- s_mov_b32 s0, weakref_var@GOTPCREL+4
+ s_mov_b32 s0, common_var0@GOTPCREL+4
+ s_mov_b32 s0, common_var1@gotpcrel32@lo+4
+ s_mov_b32 s0, common_var2@gotpcrel32@hi+4
+
+ s_mov_b32 s0, global_var0@GOTPCREL+4
+ s_mov_b32 s0, global_var1@gotpcrel32@lo+4
+ s_mov_b32 s0, global_var2@gotpcrel32@hi+4
+
+ s_mov_b32 s0, extern_var0@GOTPCREL+4
+ s_mov_b32 s0, extern_var1@gotpcrel32@lo+4
+ s_mov_b32 s0, extern_var2@gotpcrel32@hi+4
+
+ s_mov_b32 s0, weak_var0@GOTPCREL+4
+ s_mov_b32 s0, weak_var1@gotpcrel32@lo+4
+ s_mov_b32 s0, weak_var2@gotpcrel32@hi+4
+
+ s_mov_b32 s0, weakref_var0@GOTPCREL+4
+ s_mov_b32 s0, weakref_var1@gotpcrel32@lo+4
+ s_mov_b32 s0, weakref_var2@gotpcrel32@hi+4
+
+ s_mov_b32 s0, local_var0+4
+ s_mov_b32 s0, local_var1@rel32@lo+4
+ s_mov_b32 s0, local_var2@rel32@hi+4
+
s_endpgm
- .comm common_var,1024,4
- .globl global_var
- .local local_var
- .weak weak_var
- .weakref weakref_var, weakref_alias_var
+ .comm common_var0,1024,4
+ .comm common_var1,1024,4
+ .comm common_var2,1024,4
+ .globl global_var0
+ .globl global_var1
+ .globl global_var1
+ .weak weak_var0
+ .weak weak_var1
+ .weak weak_var2
+ .weakref weakref_var0, weakref_alias_var0
+ .weakref weakref_var1, weakref_alias_var1
+ .weakref weakref_var2, weakref_alias_var2
+ .local local_var0
+ .local local_var1
+ .local local_var2
+
+# R_AMDGPU_ABS32:
+.section nonalloc, "w", @progbits
+ .long var0, common_var2+4
+ .long var1, common_var1+8
+ .long var2, common_var0+12
-# The relocation for local_var should be resolved by the linker.
+# R_AMDGPU_ABS64:
+.type ptr, @object
+.data
+ .globl ptr
+ .p2align 3
+ptr:
+ .quad temp
+ .size ptr, 8
+
+# The relocation for local_var{0, 1, 2} and var should be resolved by the
+# linker.
# CHECK: Relocations [
# CHECK: .rela.dyn {
-# CHECK-NEXT: R_AMDGPU_ABS64 common_var 0x0
-# CHECK-NEXT: R_AMDGPU_ABS64 extern_var 0x0
-# CHECK-NEXT: R_AMDGPU_ABS64 global_var 0x0
-# CHECK-NEXT: R_AMDGPU_ABS64 weak_var 0x0
-# CHECK-NEXT: R_AMDGPU_ABS64 weakref_alias_var 0x0
+# CHECK-NEXT: R_AMDGPU_ABS64 common_var0 0x0
+# CHECK-NEXT: R_AMDGPU_ABS64 common_var1 0x0
+# CHECK-NEXT: R_AMDGPU_ABS64 common_var2 0x0
+# CHECK-NEXT: R_AMDGPU_ABS64 extern_var0 0x0
+# CHECK-NEXT: R_AMDGPU_ABS64 extern_var1 0x0
+# CHECK-NEXT: R_AMDGPU_ABS64 extern_var2 0x0
+# CHECK-NEXT: R_AMDGPU_ABS64 global_var0 0x0
+# CHECK-NEXT: R_AMDGPU_ABS64 global_var1 0x0
+# CHECK-NEXT: R_AMDGPU_ABS64 global_var2 0x0
+# CHECK-NEXT: R_AMDGPU_ABS64 temp 0x0
+# CHECK-NEXT: R_AMDGPU_ABS64 weak_var0 0x0
+# CHECK-NEXT: R_AMDGPU_ABS64 weak_var1 0x0
+# CHECK-NEXT: R_AMDGPU_ABS64 weak_var2 0x0
+# CHECK-NEXT: R_AMDGPU_ABS64 weakref_alias_var0 0x0
+# CHECK-NEXT: R_AMDGPU_ABS64 weakref_alias_var1 0x0
+# CHECK-NEXT: R_AMDGPU_ABS64 weakref_alias_var2 0x0
# CHECK-NEXT: }
# CHECK-NEXT: ]
+
+# OBJDUMP: Contents of section nonalloc:
+# OBJDUMP-NEXT: 0000 00000000 14380000 00000000 18340000
+# OBJDUMP-NEXT: 00000000 1c300000
diff --git a/test/ELF/arm-attributes.s b/test/ELF/arm-attributes.s
new file mode 100644
index 000000000000..14517e8fc789
--- /dev/null
+++ b/test/ELF/arm-attributes.s
@@ -0,0 +1,183 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/arm-attributes1.s -o %t1.o
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t2.o
+
+// RUN: ld.lld %t1.o %t2.o -o %t
+// RUN: llvm-readobj -arm-attributes %t | FileCheck %s
+// RUN: ld.lld %t1.o %t2.o -shared -o %t2
+// RUN: llvm-readobj -arm-attributes %t2 | FileCheck %s
+// RUN: ld.lld %t1.o %t2.o -r -o %t3
+// RUN: llvm-readobj -arm-attributes %t3 | FileCheck %s
+// REQUIRES: arm
+
+// Check that we retain only 1 SHT_ARM_ATTRIBUTES section. At present we do not
+// try and merge or use the contents of SHT_ARM_ATTRIBUTES sections. We just
+// pass the first one through.
+ .text
+ .syntax unified
+ .eabi_attribute 67, "2.09" @ Tag_conformance
+ .cpu cortex-a8
+ .eabi_attribute 6, 10 @ Tag_CPU_arch
+ .eabi_attribute 7, 65 @ Tag_CPU_arch_profile
+ .eabi_attribute 8, 1 @ Tag_ARM_ISA_use
+ .eabi_attribute 9, 2 @ Tag_THUMB_ISA_use
+ .fpu neon
+ .eabi_attribute 15, 1 @ Tag_ABI_PCS_RW_data
+ .eabi_attribute 16, 1 @ Tag_ABI_PCS_RO_data
+ .eabi_attribute 17, 2 @ Tag_ABI_PCS_GOT_use
+ .eabi_attribute 20, 1 @ Tag_ABI_FP_denormal
+ .eabi_attribute 21, 1 @ Tag_ABI_FP_exceptions
+ .eabi_attribute 23, 3 @ Tag_ABI_FP_number_model
+ .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access
+ .eabi_attribute 24, 1 @ Tag_ABI_align_needed
+ .eabi_attribute 25, 1 @ Tag_ABI_align_preserved
+ .eabi_attribute 38, 1 @ Tag_ABI_FP_16bit_format
+ .eabi_attribute 18, 4 @ Tag_ABI_PCS_wchar_t
+ .eabi_attribute 26, 2 @ Tag_ABI_enum_size
+ .eabi_attribute 14, 0 @ Tag_ABI_PCS_R9_use
+ .eabi_attribute 68, 1 @ Tag_Virtualization_use
+ .globl _start
+ .p2align 2
+ .type _start,%function
+_start:
+ .globl func
+ bl func
+ bx lr
+
+// CHECK: BuildAttributes {
+// CHECK-NEXT: FormatVersion: 0x41
+// CHECK-NEXT: Section 1 {
+// CHECK-NEXT: SectionLength: 72
+// CHECK-NEXT: Vendor: aeabi
+// CHECK-NEXT: Tag: Tag_File (0x1)
+// CHECK-NEXT: Size: 62
+// CHECK-NEXT: FileAttributes {
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 67
+// CHECK-NEXT: TagName: conformance
+// CHECK-NEXT: Value: 2.09
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 5
+// CHECK-NEXT: TagName: CPU_name
+// CHECK-NEXT: Value: cortex-a8
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 6
+// CHECK-NEXT: Value: 10
+// CHECK-NEXT: TagName: CPU_arch
+// CHECK-NEXT: Description: ARM v7
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 7
+// CHECK-NEXT: Value: 65
+// CHECK-NEXT: TagName: CPU_arch_profile
+// CHECK-NEXT: Description: Application
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 8
+// CHECK-NEXT: Value: 1
+// CHECK-NEXT: TagName: ARM_ISA_use
+// CHECK-NEXT: Description: Permitted
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 9
+// CHECK-NEXT: Value: 2
+// CHECK-NEXT: TagName: THUMB_ISA_use
+// CHECK-NEXT: Description: Thumb-2
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 10
+// CHECK-NEXT: Value: 3
+// CHECK-NEXT: TagName: FP_arch
+// CHECK-NEXT: Description: VFPv3
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 12
+// CHECK-NEXT: Value: 1
+// CHECK-NEXT: TagName: Advanced_SIMD_arch
+// CHECK-NEXT: Description: NEONv1
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 14
+// CHECK-NEXT: Value: 0
+// CHECK-NEXT: TagName: ABI_PCS_R9_use
+// CHECK-NEXT: Description: v6
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 15
+// CHECK-NEXT: Value: 1
+// CHECK-NEXT: TagName: ABI_PCS_RW_data
+// CHECK-NEXT: Description: PC-relative
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 16
+// CHECK-NEXT: Value: 1
+// CHECK-NEXT: TagName: ABI_PCS_RO_data
+// CHECK-NEXT: Description: PC-relative
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 17
+// CHECK-NEXT: Value: 2
+// CHECK-NEXT: TagName: ABI_PCS_GOT_use
+// CHECK-NEXT: Description: GOT-Indirect
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 18
+// CHECK-NEXT: Value: 4
+// CHECK-NEXT: TagName: ABI_PCS_wchar_t
+// CHECK-NEXT: Description: 4-byte
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 20
+// CHECK-NEXT: Value: 1
+// CHECK-NEXT: TagName: ABI_FP_denormal
+// CHECK-NEXT: Description: IEEE-754
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 21
+// CHECK-NEXT: Value: 1
+// CHECK-NEXT: TagName: ABI_FP_exceptions
+// CHECK-NEXT: Description: IEEE-754
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 23
+// CHECK-NEXT: Value: 3
+// CHECK-NEXT: TagName: ABI_FP_number_model
+// CHECK-NEXT: Description: IEEE-754
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 24
+// CHECK-NEXT: Value: 1
+// CHECK-NEXT: TagName: ABI_align_needed
+// CHECK-NEXT: Description: 8-byte alignment
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 25
+// CHECK-NEXT: Value: 1
+// CHECK-NEXT: TagName: ABI_align_preserved
+// CHECK-NEXT: Description: 8-byte data alignment
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 26
+// CHECK-NEXT: Value: 2
+// CHECK-NEXT: TagName: ABI_enum_size
+// CHECK-NEXT: Description: Int32
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 34
+// CHECK-NEXT: Value: 1
+// CHECK-NEXT: TagName: CPU_unaligned_access
+// CHECK-NEXT: Description: v6-style
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 38
+// CHECK-NEXT: Value: 1
+// CHECK-NEXT: TagName: ABI_FP_16bit_format
+// CHECK-NEXT: Description: IEEE-754
+// CHECK-NEXT: }
+// CHECK-NEXT: Attribute {
+// CHECK-NEXT: Tag: 68
+// CHECK-NEXT: Value: 1
+// CHECK-NEXT: TagName: Virtualization_use
+// CHECK-NEXT: Description: TrustZone
+// CHECK-NEXT: }
diff --git a/test/ELF/arm-blx.s b/test/ELF/arm-blx.s
index cccb1324fc8b..159eee51c74c 100644
--- a/test/ELF/arm-blx.s
+++ b/test/ELF/arm-blx.s
@@ -1,6 +1,7 @@
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/far-arm-thumb-abs.s -o %tfar
// RUN: echo "SECTIONS { \
+// RUN: . = 0xb4; \
// RUN: .callee1 : { *(.callee_low) } \
// RUN: .callee2 : { *(.callee_arm_low) } \
// RUN: .caller : { *(.text) } \
diff --git a/test/ELF/arm-branch.s b/test/ELF/arm-branch.s
index 38266fabf852..986863d3d80c 100644
--- a/test/ELF/arm-branch.s
+++ b/test/ELF/arm-branch.s
@@ -1,6 +1,7 @@
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/far-arm-abs.s -o %tfar
// RUN: echo "SECTIONS { \
+// RUN: . = 0xb4; \
// RUN: .callee1 : { *(.callee_low) } \
// RUN: .caller : { *(.text) } \
// RUN: .callee2 : { *(.callee_high) } } " > %t.script
diff --git a/test/ELF/arm-data-prel.s b/test/ELF/arm-data-prel.s
index 590d8118ae6e..a8c0c280b220 100644
--- a/test/ELF/arm-data-prel.s
+++ b/test/ELF/arm-data-prel.s
@@ -1,8 +1,8 @@
// RUN: llvm-mc %s -triple=armv7-unknown-linux-gnueabi -filetype=obj -o %t.o
// RUN: echo "SECTIONS { \
// RUN: .text : { *(.text) } \
-// RUN: .ARM.exidx : { *(.ARM.exidx) } \
-// RUN: .ARM.exidx.TEST1 : { *(.ARM.exidx.TEST1) } \
+// RUN: .prel.test : { *(.ARM.exidx) } \
+// RUN: .prel.test.TEST1 : { *(.ARM.exidx.TEST1) } \
// RUN: .TEST1 : { *(.TEST1) } } " > %t.script
// RUN: ld.lld --script %t.script %t.o -o %t
// RUN: llvm-readobj -s -sd %t | FileCheck --check-prefix=CHECK %s
@@ -47,7 +47,7 @@ __aeabi_unwind_cpp_pr0:
// The expected value of the exception table is
// Word0 0 in bit 31, -4 encoded in 31-bit signed offset
// Word1 Inline table entry EHT Inline Personality Routine #0
-// CHECK: Name: .ARM.exidx
+// CHECK: Name: .prel.test
// CHECK: SectionData (
// CHECK: 0000: FCFFFF7F B0B0B080
// CHECK: )
@@ -57,7 +57,7 @@ __aeabi_unwind_cpp_pr0:
// Word1 Inline table entry EHT Inline Personality Routine #0
// set vsp = r11
// pop r11, r14
-// CHECK: Name: .ARM.exidx.TEST1
+// CHECK: Name: .prel.test.TEST1
// CHECK: SectionData (
// CHECK: 0000: 08000000 80849B80
// CHECK: )
diff --git a/test/ELF/arm-eabi-version.s b/test/ELF/arm-eabi-version.s
new file mode 100644
index 000000000000..727b805fddd7
--- /dev/null
+++ b/test/ELF/arm-eabi-version.s
@@ -0,0 +1,14 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
+// RUN: ld.lld -static %t.o -o %tout
+// RUN: llvm-readobj -file-headers %tout | FileCheck %s
+// REQUIRES: arm
+ .syntax unified
+ .text
+ .globl _start
+_start:
+ bx lr
+
+// CHECK: Flags [
+// CHECK-NEXT: 0x1000000
+// CHECK-NEXT: 0x4000000
+// CHECK-NEXT: ]
diff --git a/test/ELF/arm-exidx-canunwind.s b/test/ELF/arm-exidx-canunwind.s
new file mode 100644
index 000000000000..e98ec0ec8978
--- /dev/null
+++ b/test/ELF/arm-exidx-canunwind.s
@@ -0,0 +1,99 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1
+// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s
+// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-EXIDX %s
+// RUN: llvm-readobj --program-headers --sections %t2 | FileCheck -check-prefix=CHECK-PT %s
+// REQUIRES: arm
+
+// Test that inline unwinding table entries and references to .ARM.extab
+// entries survive the re-ordering of the .ARM.exidx section
+
+ .syntax unified
+ // Will produce an ARM.exidx entry with inline unwinding instructions
+ .section .text.func1, "ax",%progbits
+ .global func1
+func1:
+ .fnstart
+ bx lr
+ .save {r7, lr}
+ .setfp r7, sp, #0
+ .fnend
+
+ // Unwinding instructions for .text2 too large for an inline entry ARM.exidx
+ // entry. A separate .ARM.extab section is created to hold the unwind entries
+ // The .ARM.exidx table entry has a reference to the .ARM.extab section.
+ .section .text.func2, "ax",%progbits
+ .global func2
+func2:
+ .fnstart
+ bx lr
+ .personality __gxx_personality_v0
+ .handlerdata
+ .long 0
+ .section .text.func2
+ .fnend
+
+ // Dummy implementation of personality routines to satisfy reference from
+ // exception tables
+ .section .text.__gcc_personality_v0, "ax", %progbits
+ .global __gxx_personality_v0
+__gxx_personality_v0:
+ bx lr
+
+ .section .text.__aeabi_unwind_cpp_pr0, "ax", %progbits
+ .global __aeabi_unwind_cpp_pr0
+__aeabi_unwind_cpp_pr0:
+ bx lr
+
+ .text
+ .global _start
+_start:
+ bl func1
+ bl func2
+ bx lr
+
+// CHECK: Disassembly of section .text:
+// CHECK-NEXT: _start:
+// CHECK-NEXT: 11000: 01 00 00 eb bl #4 <func1>
+// CHECK-NEXT: 11004: 01 00 00 eb bl #4 <func2>
+// CHECK-NEXT: 11008: 1e ff 2f e1 bx lr
+// CHECK: func1:
+// CHECK-NEXT: 1100c: 1e ff 2f e1 bx lr
+// CHECK: func2:
+// CHECK-NEXT: 11010: 1e ff 2f e1 bx lr
+// CHECK: __gxx_personality_v0:
+// CHECK-NEXT: 11014: 1e ff 2f e1 bx lr
+// CHECK: __aeabi_unwind_cpp_pr0:
+// CHECK-NEXT: 11018: 1e ff 2f e1 bx lr
+
+// CHECK-EXIDX: Contents of section .ARM.exidx:
+// 100d4 + f38 = 1100c = func1 (inline unwinding data)
+// 100dc + f34 = 11010 = func2 (100e0 + c = 100ec = .ARM.extab entry)
+// CHECK-EXIDX-NEXT: 100d4 380f0000 08849780 340f0000 0c000000
+// 100e4 + f30 = 11014 = terminate = func2 + sizeof(func2)
+// CHECK-EXIDX-NEXT: 100e4 300f0000 01000000
+// CHECK-EXIDX-NEXT: Contents of section .ARM.extab.text.func2:
+// 100ec + f28 = 11014 = __gxx_personality_v0
+// CHECK-EXIDX-NEXT: 100ec 280f0000 b0b0b000 00000000
+
+// CHECK-PT: Name: .ARM.exidx
+// CHECK-PT-NEXT: Type: SHT_ARM_EXIDX (0x70000001)
+// CHECK-PT-NEXT: Flags [
+// CHECK-PT-NEXT: SHF_ALLOC
+// CHECK-PT-NEXT: SHF_LINK_ORDER
+// CHECK-PT-NEXT: ]
+// CHECK-PT-NEXT: Address: 0x100D4
+// CHECK-PT-NEXT: Offset: 0xD4
+// CHECK-PT-NEXT: Size: 24
+
+// CHECK-PT: Type: PT_ARM_EXIDX (0x70000001)
+// CHECK-PT-NEXT: Offset: 0xD4
+// CHECK-PT-NEXT: VirtualAddress: 0x100D4
+// CHECK-PT-NEXT: PhysicalAddress: 0x100D4
+// CHECK-PT-NEXT: FileSize: 24
+// CHECK-PT-NEXT: MemSize: 24
+// CHECK-PT-NEXT: Flags [ (0x4)
+// CHECK-PT-NEXT: PF_R (0x4)
+// CHECK-PT-NEXT: ]
+// CHECK-PT-NEXT: Alignment: 4
+// CHECK-PT-NEXT: }
diff --git a/test/ELF/arm-exidx-gc.s b/test/ELF/arm-exidx-gc.s
new file mode 100644
index 000000000000..b1a5be54aa2b
--- /dev/null
+++ b/test/ELF/arm-exidx-gc.s
@@ -0,0 +1,125 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2 --gc-sections 2>&1
+// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s
+// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-EXIDX %s
+// REQUIRES: arm
+
+// Test the behavior of .ARM.exidx sections under garbage collection
+// A .ARM.exidx section is live if it has a relocation to a live executable
+// section.
+// A .ARM.exidx section may have a relocation to a .ARM.extab section, if the
+// .ARM.exidx is live then the .ARM.extab section is live
+
+ .syntax unified
+ .section .text.func1, "ax",%progbits
+ .global func1
+func1:
+ .fnstart
+ bx lr
+ .save {r7, lr}
+ .setfp r7, sp, #0
+ .fnend
+
+ .section .text.unusedfunc1, "ax",%progbits
+ .global unusedfunc1
+unusedfunc1:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ // Unwinding instructions for .text2 too large for an inline entry ARM.exidx
+ // entry. A separate .ARM.extab section is created to hold the unwind entries
+ // The .ARM.exidx table entry has a reference to the .ARM.extab section.
+ .section .text.func2, "ax",%progbits
+ .global func2
+func2:
+ .fnstart
+ bx lr
+ .personality __gxx_personality_v0
+ .handlerdata
+ .section .text.func2
+ .fnend
+
+ // An unused function with a reference to a .ARM.extab section. Both should
+ // be removed by gc.
+ .section .text.unusedfunc2, "ax",%progbits
+ .global unusedfunc2
+unusedfunc2:
+ .fnstart
+ bx lr
+ .personality __gxx_personality_v1
+ .handlerdata
+ .section .text.unusedfunc2
+ .fnend
+
+ // Dummy implementation of personality routines to satisfy reference from
+ // exception tables
+ .section .text.__gcc_personality_v0, "ax", %progbits
+ .global __gxx_personality_v0
+__gxx_personality_v0:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ .section .text.__gcc_personality_v1, "ax", %progbits
+ .global __gxx_personality_v1
+__gxx_personality_v1:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ .section .text.__aeabi_unwind_cpp_pr0, "ax", %progbits
+ .global __aeabi_unwind_cpp_pr0
+__aeabi_unwind_cpp_pr0:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+// Entry point for GC
+ .text
+ .global _start
+_start:
+ bl func1
+ bl func2
+ bx lr
+
+// GC should have only removed unusedfunc1 and unusedfunc2 the personality
+// routines are kept alive by references from live .ARM.exidx and .ARM.extab
+// sections
+// CHECK: Disassembly of section .text:
+// CHECK-NEXT: _start:
+// CHECK-NEXT: 11000: 01 00 00 eb bl #4 <func1>
+// CHECK-NEXT: 11004: 01 00 00 eb bl #4 <func2>
+// CHECK-NEXT: 11008: 1e ff 2f e1 bx lr
+// CHECK: func1:
+// CHECK-NEXT: 1100c: 1e ff 2f e1 bx lr
+// CHECK: func2:
+// CHECK-NEXT: 11010: 1e ff 2f e1 bx lr
+// CHECK: __gxx_personality_v0:
+// CHECK-NEXT: 11014: 1e ff 2f e1 bx lr
+// CHECK: __aeabi_unwind_cpp_pr0:
+// CHECK-NEXT: 11018: 1e ff 2f e1 bx lr
+
+// GC should have removed table entries for unusedfunc1, unusedfunc2
+// and __gxx_personality_v1
+// CHECK-NOT: unusedfunc1
+// CHECK-NOT: unusedfunc2
+// CHECK-NOT: __gxx_personality_v1
+
+// CHECK-EXIDX-NOT: Contents of section .ARM.extab.text.unusedfunc2:
+// CHECK-EXIDX: Contents of section .ARM.exidx:
+// 100d4 + f38 = 1100c = func1
+// 100dc + f34 = 11010 = func2 (100e0 + 1c = 100fc = .ARM.extab.text.func2)
+// CHECK-EXIDX-NEXT: 100d4 380f0000 08849780 340f0000 1c000000
+// 100e4 + f30 = 11014 = __gxx_personality_v0
+// 100ec + f2c = 11018 = __aeabi_unwind_cpp_pr0
+// CHECK-EXIDX-NEXT: 100e4 300f0000 01000000 2c0f0000 01000000
+// 100f4 + f28 = 1101c = __aeabi_unwind_cpp_pr0 + sizeof(__aeabi_unwind_cpp_pr0)
+// CHECK-EXIDX-NEXT: 100f4 280f0000 01000000
+// CHECK-EXIDX-NEXT: Contents of section .ARM.extab.text.func2:
+// 100fc + f18 = 11014 = __gxx_personality_v0
+// CHECK-EXIDX-NEXT: 100fc 180f0000 b0b0b000
diff --git a/test/ELF/arm-exidx-link.s b/test/ELF/arm-exidx-link.s
new file mode 100644
index 000000000000..50f981261d9d
--- /dev/null
+++ b/test/ELF/arm-exidx-link.s
@@ -0,0 +1,25 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
+// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: llvm-readobj -s %t.so | FileCheck %s
+
+// CHECK: Name: .ARM.exidx
+// CHECK-NEXT: Type: SHT_ARM_EXIDX
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: SHF_LINK_ORDER
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address:
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size:
+// CHECK-NEXT: Link: [[INDEX:.*]]
+
+// CHECK: Index: [[INDEX]]
+// CHECK-NEXT: Name: .text
+
+
+ f:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
diff --git a/test/ELF/arm-exidx-order.s b/test/ELF/arm-exidx-order.s
new file mode 100644
index 000000000000..951c71a4c33e
--- /dev/null
+++ b/test/ELF/arm-exidx-order.s
@@ -0,0 +1,169 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/arm-exidx-cantunwind.s -o %tcantunwind
+// RUN: ld.lld %t %tcantunwind -o %t2 2>&1
+// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s
+// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-EXIDX %s
+// RUN: llvm-readobj --program-headers --sections %t2 | FileCheck -check-prefix=CHECK-PT %s
+// Use Linker script to place .ARM.exidx in between .text and orphan sections
+// RUN: echo "SECTIONS { \
+// RUN: .text 0x11000 : { *(.text*) } \
+// RUN: .ARM.exidx : { *(.ARM.exidx) } } " > %t.script
+// RUN: ld.lld --script %t.script %tcantunwind %t -o %t3 2>&1
+// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t3 | FileCheck -check-prefix=CHECK-SCRIPT %s
+// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t3 | FileCheck -check-prefix=CHECK-SCRIPT-EXIDX %s
+// REQUIRES: arm
+
+// Each assembler created .ARM.exidx section has the SHF_LINK_ORDER flag set
+// with the sh_link containing the section index of the executable section
+// containing the function it describes. The linker must combine the .ARM.exidx
+// InputSections in the same order that it has combined the executable section,
+// such that the combined .ARM.exidx OutputSection can be used as a binary
+// search table.
+
+ .syntax unified
+ .section .text, "ax",%progbits
+ .globl _start
+_start:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ .section .text.f1, "ax", %progbits
+ .globl f1
+f1:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ .section .text.f2, "ax", %progbits
+ .globl f2
+f2:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+ .globl f3
+f3:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+// Check default no linker script order.
+
+// CHECK: Disassembly of section .text:
+// CHECK: _start:
+// CHECK-NEXT: 11000: 1e ff 2f e1 bx lr
+// CHECK: f1:
+// CHECK-NEXT: 11004: 1e ff 2f e1 bx lr
+// CHECK: f2:
+// CHECK-NEXT: 11008: 1e ff 2f e1 bx lr
+// CHECK: f3:
+// CHECK-NEXT: 1100c: 1e ff 2f e1 bx lr
+// CHECK: func4:
+// CHECK-NEXT: 11010: 1e ff 2f e1 bx lr
+// CHECK: func5:
+// CHECK-NEXT: 11014: 1e ff 2f e1 bx lr
+// CHECK: Disassembly of section .func1:
+// CHECK-NEXT: func1:
+// CHECK-NEXT: 11018: 1e ff 2f e1 bx lr
+// CHECK: Disassembly of section .func2:
+// CHECK-NEXT: func2:
+// CHECK-NEXT: 1101c: 1e ff 2f e1 bx lr
+// CHECK: Disassembly of section .func3:
+// CHECK-NEXT: func3:
+// CHECK-NEXT: 11020: 1e ff 2f e1 bx lr
+
+// Each .ARM.exidx section has two 4 byte fields
+// Field 1 is the 31-bit offset to the function. The top bit is used to
+// indicate whether Field 2 is a pointer or an inline table entry.
+// Field 2 is either a pointer to a .ARM.extab section or an inline table
+// In this example all Field 2 entries are inline can't unwind (0x1)
+// We expect to see the entries in the same order as the functions
+
+// CHECK-EXIDX: Contents of section .ARM.exidx:
+// 100d4 + f2c = 11000 = _start
+// 100dc + f28 = 11004 = f1
+// CHECK-EXIDX-NEXT: 100d4 2c0f0000 01000000 280f0000 01000000
+// 100e4 + f24 = 11008 = f2
+// 100ec + f20 = 1100c = f3
+// CHECK-EXIDX-NEXT: 100e4 240f0000 01000000 200f0000 01000000
+// 100f4 + f1c = 11010 = func4
+// 100fc + f18 = 11014 = func5
+// CHECK-EXIDX-NEXT: 100f4 1c0f0000 01000000 180f0000 01000000
+// 10104 + f14 = 11018 = func1
+// 1010c + f10 = 1101c = func2
+// CHECK-EXIDX-NEXT: 10104 140f0000 01000000 100f0000 01000000
+// 10114 + f0c = 11020 = func3
+// CHECK-EXIDX-NEXT: 10114 0c0f0000 01000000
+
+// Check that PT_ARM_EXIDX program header has been generated that describes
+// the .ARM.exidx output section
+// CHECK-PT: Name: .ARM.exidx
+// CHECK-PT-NEXT: Type: SHT_ARM_EXIDX (0x70000001)
+// CHECK-PT-NEXT: Flags [
+// CHECK-PT-NEXT: SHF_ALLOC
+// CHECK-PT-NEXT: SHF_LINK_ORDER
+// CHECK-PT-NEXT: ]
+// CHECK-PT-NEXT: Address: 0x100D4
+// CHECK-PT-NEXT: Offset: 0xD4
+// CHECK-PT-NEXT: Size: 80
+
+// CHECK-PT: Type: PT_ARM_EXIDX (0x70000001)
+// CHECK-PT-NEXT: Offset: 0xD4
+// CHECK-PT-NEXT: VirtualAddress: 0x100D4
+// CHECK-PT-NEXT: PhysicalAddress: 0x100D4
+// CHECK-PT-NEXT: FileSize: 80
+// CHECK-PT-NEXT: MemSize: 80
+// CHECK-PT-NEXT: Flags [ (0x4)
+// CHECK-PT-NEXT: PF_R (0x4)
+// CHECK-PT-NEXT: ]
+// CHECK-PT-NEXT: Alignment: 4
+// CHECK-PT-NEXT: }
+
+
+// Check linker script order. The .ARM.exidx section will be inserted after
+// the .text section but before the orphan sections
+
+// CHECK-SCRIPT: Disassembly of section .text:
+// CHECK-SCRIPT-NEXT: func4:
+// CHECK-SCRIPT-NEXT: 11000: 1e ff 2f e1 bx lr
+// CHECK-SCRIPT: func5:
+// CHECK-SCRIPT-NEXT: 11004: 1e ff 2f e1 bx lr
+// CHECK-SCRIPT: _start:
+// CHECK-SCRIPT-NEXT: 11008: 1e ff 2f e1 bx lr
+// CHECK-SCRIPT: f1:
+// CHECK-SCRIPT-NEXT: 1100c: 1e ff 2f e1 bx lr
+// CHECK-SCRIPT: f2:
+// CHECK-SCRIPT-NEXT: 11010: 1e ff 2f e1 bx lr
+// CHECK-SCRIPT: f3:
+// CHECK-SCRIPT-NEXT: 11014: 1e ff 2f e1 bx lr
+// CHECK-SCRIPT-NEXT: Disassembly of section .func1:
+// CHECK-SCRIPT-NEXT: func1:
+// CHECK-SCRIPT-NEXT: 11068: 1e ff 2f e1 bx lr
+// CHECK-SCRIPT-NEXT: Disassembly of section .func2:
+// CHECK-SCRIPT-NEXT: func2:
+// CHECK-SCRIPT-NEXT: 1106c: 1e ff 2f e1 bx lr
+// CHECK-SCRIPT-NEXT: Disassembly of section .func3:
+// CHECK-SCRIPT-NEXT: func3:
+// CHECK-SCRIPT-NEXT: 11070: 1e ff 2f e1 bx lr
+
+// Check that the .ARM.exidx section is sorted in order as the functions
+// The offset in field 1, is 32-bit so in the binary the most significant bit
+// 11018 - 18 = 11000 func4
+// 11020 - 1c = 11004 func5
+// CHECK-SCRIPT-EXIDX: 11018 e8ffff7f 01000000 e4ffff7f 01000000
+// 11028 - 20 = 11008 _start
+// 11030 - 24 = 1100c f1
+// CHECK-SCRIPT-EXIDX-NEXT: 11028 e0ffff7f 01000000 dcffff7f 01000000
+// 11038 - 28 = 11010 f2
+// 11040 - 2c = 11014 f3
+// CHECK-SCRIPT-EXIDX-NEXT: 11038 d8ffff7f 01000000 d4ffff7f 01000000
+// 11048 + 20 = 11068 func1
+// 11050 + 1c = 1106c func2
+// CHECK-SCRIPT-EXIDX-NEXT: 11048 20000000 01000000 1c000000 01000000
+// 11058 + 18 = 11070 func3
+// 11060 + 14 = 11074 func3 + sizeof(func3)
+// CHECK-SCRIPT-EXIDX-NEXT: 11058 18000000 01000000 14000000 01000000
diff --git a/test/ELF/arm-exidx-output.s b/test/ELF/arm-exidx-output.s
new file mode 100644
index 000000000000..dca43a359934
--- /dev/null
+++ b/test/ELF/arm-exidx-output.s
@@ -0,0 +1,44 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1
+// RUN: llvm-readobj -sections %t2 | FileCheck %s
+// REQUIRES: arm
+
+// Check that only a single .ARM.exidx output section is created when
+// there are input sections of the form .ARM.exidx.<section-name>. The
+// assembler creates the .ARM.exidx input sections with the .cantunwind
+// directive
+ .syntax unified
+ .section .text, "ax",%progbits
+ .globl _start
+_start:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ .section .text.f1, "ax", %progbits
+ .globl f1
+f1:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ .section .text.f2, "ax", %progbits
+ .globl f2
+f2:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+// CHECK: Section {
+// CHECK: Name: .ARM.exidx
+// CHECK-NEXT: Type: SHT_ARM_EXIDX (0x70000001)
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: SHF_LINK_ORDER
+// CHECK-NEXT: ]
+
+// CHECK-NOT: Name: .ARM.exidx.text.f1
+// CHECK-NOT: Name: .ARM.exidx.text.f2
diff --git a/test/ELF/arm-exidx-relocatable.s b/test/ELF/arm-exidx-relocatable.s
new file mode 100644
index 000000000000..1b6ee3f23a4f
--- /dev/null
+++ b/test/ELF/arm-exidx-relocatable.s
@@ -0,0 +1,132 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/arm-exidx-cantunwind.s -o %tcantunwind
+// Check that relocatable link maintains SHF_LINK_ORDER
+// RUN: ld.lld -r %t %tcantunwind -o %t4 2>&1
+// RUN: llvm-readobj -s %t4 | FileCheck %s
+// REQUIRES: arm
+
+// Each assembler created .ARM.exidx section has the SHF_LINK_ORDER flag set
+// with the sh_link containing the section index of the executable section
+// containing the function it describes. To maintain this property in
+// relocatable links we pass through the .ARM.exidx section, the section it
+// it has a sh_link to, and the associated relocation sections uncombined.
+
+ .syntax unified
+ .section .text, "ax",%progbits
+ .globl _start
+_start:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ .section .text.f1, "ax", %progbits
+ .globl f1
+f1:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ .section .text.f2, "ax", %progbits
+ .globl f2
+f2:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+ .globl f3
+f3:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+// CHECK: Index: 1
+// CHECK-NEXT: Name: .text
+
+// CHECK: Name: .ARM.exidx
+// CHECK-NEXT: Type: SHT_ARM_EXIDX (0x70000001)
+// CHECK-NEXT: Flags [ (0x82)
+// CHECK-NEXT: SHF_ALLOC (0x2)
+// CHECK-NEXT: SHF_LINK_ORDER (0x80)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size: 24
+// CHECK-NEXT: Link: 1
+
+
+// CHECK: Index: 4
+// CHECK-NEXT: Name: .text.f1
+
+// CHECK: Name: .ARM.exidx.text.f1
+// CHECK-NEXT: Type: SHT_ARM_EXIDX (0x70000001)
+// CHECK-NEXT: Flags [ (0x82)
+// CHECK-NEXT: SHF_ALLOC (0x2)
+// CHECK-NEXT: SHF_LINK_ORDER (0x80)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size: 8
+// CHECK-NEXT: Link: 4
+
+
+// CHECK: Index: 7
+// CHECK-NEXT: Name: .text.f2
+
+// CHECK: Name: .ARM.exidx.text.f2
+// CHECK-NEXT: Type: SHT_ARM_EXIDX (0x70000001)
+// CHECK-NEXT: Flags [ (0x82)
+// CHECK-NEXT: SHF_ALLOC (0x2)
+// CHECK-NEXT: SHF_LINK_ORDER (0x80)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size: 16
+// CHECK-NEXT: Link: 7
+
+
+// CHECK: Index: 10
+// CHECK-NEXT: Name: .func1
+
+// CHECK: Name: .ARM.exidx.func1
+// CHECK-NEXT: Type: SHT_ARM_EXIDX (0x70000001)
+// CHECK-NEXT: Flags [ (0x82)
+// CHECK-NEXT: SHF_ALLOC (0x2)
+// CHECK-NEXT: SHF_LINK_ORDER (0x80)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size: 8
+// CHECK-NEXT: Link: 10
+
+
+// CHECK: Index: 13
+// CHECK-NEXT: Name: .func2
+
+// CHECK: Name: .ARM.exidx.func2
+// CHECK-NEXT: Type: SHT_ARM_EXIDX (0x70000001)
+// CHECK-NEXT: Flags [ (0x82)
+// CHECK-NEXT: SHF_ALLOC (0x2)
+// CHECK-NEXT: SHF_LINK_ORDER (0x80)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size: 8
+// CHECK-NEXT: Link: 13
+
+
+// CHECK: Index: 16
+// CHECK-NEXT: Name: .func3
+
+// CHECK: Name: .ARM.exidx.func3
+// CHECK-NEXT: Type: SHT_ARM_EXIDX (0x70000001)
+// CHECK-NEXT: Flags [ (0x82)
+// CHECK-NEXT: SHF_ALLOC (0x2)
+// CHECK-NEXT: SHF_LINK_ORDER (0x80)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size: 8
+// CHECK-NEXT: Link: 16
diff --git a/test/ELF/arm-exidx-sentinel-norelocatable.s b/test/ELF/arm-exidx-sentinel-norelocatable.s
new file mode 100644
index 000000000000..4a5b64d8cd18
--- /dev/null
+++ b/test/ELF/arm-exidx-sentinel-norelocatable.s
@@ -0,0 +1,17 @@
+// RUN: llvm-mc %s -triple=armv7-unknown-linux-gnueabi -filetype=obj -o %t.o
+// RUN: ld.lld -r %t.o -o %t
+// REQUIRES: arm
+// RUN: llvm-readobj -s %t | FileCheck %s
+// Check that when doing a relocatable link we don't add a terminating entry
+// to the .ARM.exidx section
+ .syntax unified
+ .text
+_start:
+ .fnstart
+ .cantunwind
+ bx lr
+ .fnend
+
+// Expect 1 table entry of size 8
+// CHECK: Name: .ARM.exidx
+// CHECK: Size: 8
diff --git a/test/ELF/arm-exidx-sentinel-orphan.s b/test/ELF/arm-exidx-sentinel-orphan.s
new file mode 100644
index 000000000000..c054fe940db6
--- /dev/null
+++ b/test/ELF/arm-exidx-sentinel-orphan.s
@@ -0,0 +1,23 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// Use Linker script without .ARM.exidx Output Section so it is treated as
+// an orphan. We must still add the sentinel table entry
+// RUN: echo "SECTIONS { \
+// RUN: .text 0x11000 : { *(.text*) } \
+// RUN: } " > %t.script
+// RUN: ld.lld --script %t.script %t -o %t2
+// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s
+// REQUIRES: arm
+
+ .syntax unified
+ .text
+ .global _start
+_start:
+ .fnstart
+ .cantunwind
+ bx lr
+ .fnend
+
+// CHECK: Contents of section .ARM.exidx:
+// 11004 - 4 = 0x11000 = _start
+// 1100c - 8 = 0x11004 = _start + sizeof(_start)
+// CHECK-NEXT: 11004 fcffff7f 01000000 f8ffff7f 01000000
diff --git a/test/ELF/arm-exidx-shared.s b/test/ELF/arm-exidx-shared.s
new file mode 100644
index 000000000000..86b2d1fe00d8
--- /dev/null
+++ b/test/ELF/arm-exidx-shared.s
@@ -0,0 +1,45 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t --shared -o %t2 2>&1
+// RUN: llvm-readobj --relocations %t2 | FileCheck %s
+// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-EXTAB %s
+// REQUIRES: arm
+
+// Check that the relative R_ARM_PREL31 relocation can access a PLT entry
+// for when the personality routine is referenced from a shared library.
+// Also check that the R_ARM_NONE no-op relocation can be used in a shared
+// library.
+ .syntax unified
+// Will produce an ARM.exidx entry with an R_ARM_NONE relocation to
+// __aeabi_unwind_cpp_pr0
+ .section .text.func1, "ax",%progbits
+ .global func1
+func1:
+ .fnstart
+ bx lr
+ .fnend
+
+// Will produce a R_ARM_PREL31 relocation with respect to the PLT entry of
+// __gxx_personality_v0
+ .section .text.func2, "ax",%progbits
+ .global func2
+func2:
+ .fnstart
+ bx lr
+ .personality __gxx_personality_v0
+ .handlerdata
+ .long 0
+ .section .text.func2
+ .fnend
+
+ .section .text.__aeabi_unwind_cpp_pr0, "ax", %progbits
+ .global __aeabi_unwind_cpp_pr0
+__aeabi_unwind_cpp_pr0:
+ bx lr
+
+// CHECK: Relocations [
+// CHECK-NEXT: Section (6) .rel.plt {
+// CHECK-NEXT: 0x300C R_ARM_JUMP_SLOT __gxx_personality_v0
+
+// CHECK-EXTAB: Contents of section .ARM.extab.text.func2:
+// 014c + 0ed8 = 0x1024 = __gxx_personality_v0(PLT)
+// CHECK-EXTAB-NEXT: 014c d80e0000 b0b0b000 00000000
diff --git a/test/ELF/arm-gnu-ifunc-plt.s b/test/ELF/arm-gnu-ifunc-plt.s
new file mode 100644
index 000000000000..30ef66a3b775
--- /dev/null
+++ b/test/ELF/arm-gnu-ifunc-plt.s
@@ -0,0 +1,93 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-linux-gnueabihf %S/Inputs/arm-shared.s -o %t1.o
+// RUN: ld.lld %t1.o --shared -o %t.so
+// RUN: llvm-mc -filetype=obj -triple=armv7a-linux-gnueabihf %s -o %t.o
+// RUN: ld.lld %t.so %t.o -o %tout
+// RUN: llvm-objdump -triple=armv7a-linux-gnueabihf -d %tout | FileCheck %s --check-prefix=DISASM
+// RUN: llvm-objdump -s %tout | FileCheck %s --check-prefix=GOTPLT
+// RUN: llvm-readobj -r -dynamic-table %tout | FileCheck %s
+// REQUIRES: arm
+
+// Check that the IRELATIVE relocations are last in the .got
+// CHECK: Relocations [
+// CHECK-NEXT: Section (4) .rel.dyn {
+// CHECK-NEXT: 0x12078 R_ARM_GLOB_DAT bar2 0x0
+// CHECK-NEXT: 0x1207C R_ARM_GLOB_DAT zed2 0x0
+// CHECK-NEXT: 0x12080 R_ARM_IRELATIVE - 0x0
+// CHECK-NEXT: 0x12084 R_ARM_IRELATIVE - 0x0
+// CHECK-NEXT: }
+// CHECK-NEXT: Section (5) .rel.plt {
+// CHECK-NEXT: 0x1300C R_ARM_JUMP_SLOT bar2 0x0
+// CHECK-NEXT: 0x13010 R_ARM_JUMP_SLOT zed2 0x0
+// CHECK-NEXT: }
+// CHECK-NEXT: ]
+
+// Check that the GOT entries refer back to the ifunc resolver
+// GOTPLT: Contents of section .got:
+// GOTPLT-NEXT: 12078 00000000 00000000 00100100 04100100
+// GOTPLT-NEXT: Contents of section .got.plt:
+// GOTPLT-NEXT: 13000 00000000 00000000 00000000 20100100
+// GOTPLT-NEXT: 13010 20100100
+
+// DISASM: Disassembly of section .text:
+// DISASM-NEXT: foo:
+// DISASM-NEXT: 11000: 1e ff 2f e1 bx lr
+// DISASM: bar:
+// DISASM-NEXT: 11004: 1e ff 2f e1 bx lr
+// DISASM: _start:
+// DISASM-NEXT: 11008: 14 00 00 eb bl #80
+// DISASM-NEXT: 1100c: 17 00 00 eb bl #92
+// DISASM: 11010: 00 00 00 00 .word 0x00000000
+// DISASM-NEXT: 11014: 04 00 00 00 .word 0x00000004
+// DISASM: 11018: 05 00 00 eb bl #20
+// DISASM-NEXT: 1101c: 08 00 00 eb bl #32
+// DISASM-NEXT: Disassembly of section .plt:
+// DISASM-NEXT: .plt:
+// DISASM-NEXT: 11020: 04 e0 2d e5 str lr, [sp, #-4]!
+// DISASM-NEXT: 11024: 04 e0 9f e5 ldr lr, [pc, #4]
+// DISASM-NEXT: 11028: 0e e0 8f e0 add lr, pc, lr
+// DISASM-NEXT: 1102c: 08 f0 be e5 ldr pc, [lr, #8]!
+// DISASM-NEXT: 11030: d0 1f 00 00
+// DISASM-NEXT: 11034: 04 c0 9f e5 ldr r12, [pc, #4]
+// DISASM-NEXT: 11038: 0f c0 8c e0 add r12, r12, pc
+// DISASM-NEXT: 1103c: 00 f0 9c e5 ldr pc, [r12]
+// DISASM-NEXT: 11040: cc 1f 00 00
+// DISASM-NEXT: 11044: 04 c0 9f e5 ldr r12, [pc, #4]
+// DISASM-NEXT: 11048: 0f c0 8c e0 add r12, r12, pc
+// DISASM-NEXT: 1104c: 00 f0 9c e5 ldr pc, [r12]
+// DISASM-NEXT: 11050: c0 1f 00 00
+// Alignment to 16 byte boundary not strictly necessary on ARM, but harmless
+// DISASM-NEXT: 11054: 00 00 00 00 andeq r0, r0, r0
+// DISASM-NEXT: 11058: 00 00 00 00 andeq r0, r0, r0
+// DISASM-NEXT: 1105c: 00 00 00 00 andeq r0, r0, r0
+// DISASM-NEXT: 11060: 04 c0 9f e5 ldr r12, [pc, #4]
+// DISASM-NEXT: 11064: 0f c0 8c e0 add r12, r12, pc
+// DISASM-NEXT: 11068: 00 f0 9c e5 ldr pc, [r12]
+// DISASM-NEXT: 1106c: 14 10 00 00
+// DISASM-NEXT: 11070: 04 c0 9f e5 ldr r12, [pc, #4]
+// DISASM-NEXT: 11074: 0f c0 8c e0 add r12, r12, pc
+// DISASM-NEXT: 11078: 00 f0 9c e5 ldr pc, [r12]
+// DISASM-NEXT: 1107c: 08 10 00 00
+
+
+.syntax unified
+.text
+.type foo STT_GNU_IFUNC
+.globl foo
+foo:
+ bx lr
+
+.type bar STT_GNU_IFUNC
+.globl bar
+bar:
+ bx lr
+
+.globl _start
+_start:
+ bl foo
+ bl bar
+ // Create entries in the .got and .rel.dyn so that we don't just have
+ // IRELATIVE
+ .word bar2(got)
+ .word zed2(got)
+ bl bar2
+ bl zed2
diff --git a/test/ELF/arm-gnu-ifunc.s b/test/ELF/arm-gnu-ifunc.s
index c1e8a7183530..214a8824787c 100644
--- a/test/ELF/arm-gnu-ifunc.s
+++ b/test/ELF/arm-gnu-ifunc.s
@@ -24,53 +24,65 @@ _start:
movw r0,:lower16:__rel_iplt_end
movt r0,:upper16:__rel_iplt_end
-// CHECK: Sections [
-// CHECK: Section {
-// CHECK: Index: 1
-// CHECK-NEXT: Name: .rel.plt
-// CHECK-NEXT: Type: SHT_REL
-// CHECK-NEXT: Flags [
-// CHECK-NEXT: SHF_ALLOC
-// CHECK-NEXT: ]
-// CHECK-NEXT: Address: [[REL:.*]]
-// CHECK-NEXT: Offset:
-// CHECK-NEXT: Size: 16
-// CHECK-NEXT: Link:
-// CHECK-NEXT: Info:
-// CHECK-NEXT: AddressAlignment: 4
-// CHECK-NEXT: EntrySize: 8
-// CHECK-NEXT: }
-// CHECK: Relocations [
-// CHECK-NEXT: Section (1) .rel.plt {
-// CHECK-NEXT: 0x1200C R_ARM_IRELATIVE
-// CHECK-NEXT: 0x12010 R_ARM_IRELATIVE
-// CHECK-NEXT: }
-// CHECK-NEXT:]
-// CHECK: Symbols [
-// CHECK: Symbol {
-// CHECK: Name: __rel_iplt_end
-// CHECK-NEXT: Value: 0x100E4
-// CHECK-NEXT: Size: 0
-// CHECK-NEXT: Binding: Local
-// CHECK-NEXT: Type: None
-// CHECK-NEXT: Other [
-// CHECK-NEXT: STV_HIDDEN
-// CHECK-NEXT: ]
-// CHECK-NEXT: Section: .rel.plt
-// CHECK-NEXT: }
-// CHECK-NEXT: Symbol {
-// CHECK-NEXT: Name: __rel_iplt_start
-// CHECK-NEXT: Value: 0x100D4
-// CHECK-NEXT: Size: 0
-// CHECK-NEXT: Binding: Local
-// CHECK-NEXT: Type: None
-// CHECK-NEXT: Other [
-// CHECK-NEXT: STV_HIDDEN
-// CHECK-NEXT: ]
-// CHECK-NEXT: Section: .rel.plt
-// CHECK-NEXT: }
+// CHECK: Sections [
+// CHECK: Section {
+// CHECK: Section {
+// CHECK: Name: .rel.dyn
+// CHECK-NEXT: Type: SHT_REL
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x100F4
+// CHECK-NEXT: Offset: 0xF4
+// CHECK-NEXT: Size: 16
+// CHECK: Name: .plt
+// CHECK-NEXT: Type: SHT_PROGBITS
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: SHF_EXECINSTR
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x11020
+// CHECK-NEXT: Offset: 0x1020
+// CHECK-NEXT: Size: 32
+// CHECK: Name: .got
+// CHECK-NEXT: Type: SHT_PROGBITS
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: SHF_WRITE
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x12000
+// CHECK-NEXT: Offset: 0x2000
+// CHECK-NEXT: Size: 8
+// CHECK: Relocations [
+// CHECK-NEXT: Section (1) .rel.dyn {
+// CHECK-NEXT: 0x12000 R_ARM_IRELATIVE
+// CHECK-NEXT: 0x12004 R_ARM_IRELATIVE
+// CHECK-NEXT: }
+// CHECK-NEXT: ]
+// CHECK: Symbol {
+// CHECK: Name: __rel_iplt_end (6)
+// CHECK-NEXT: Value: 0x10104
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Local
+// CHECK-NEXT: Type: None
+// CHECK-NEXT: Other [
+// CHECK-NEXT: STV_HIDDEN
+// CHECK-NEXT: ]
+// CHECK-NEXT: Section: .rel.dyn
+// CHECK-NEXT: }
+// CHECK-NEXT: Symbol {
+// CHECK-NEXT: Name: __rel_iplt_start
+// CHECK-NEXT: Value: 0x100F4
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Local
+// CHECK-NEXT: Type: None
+// CHECK-NEXT: Other [
+// CHECK-NEXT: STV_HIDDEN
+// CHECK-NEXT: ]
+// CHECK-NEXT: Section: .rel.dyn
+// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
-// CHECK-NEXT: Name: _start (6)
+// CHECK-NEXT: Name: _start
// CHECK-NEXT: Value: 0x11008
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global
@@ -97,35 +109,29 @@ _start:
// CHECK-NEXT: Section: .text
// CHECK-NEXT: }
-// DISASM: Disassembly of section .text:
+// DISASM: Disassembly of section .text:
// DISASM-NEXT: foo:
-// DISASM-NEXT: 11000: 1e ff 2f e1 bx lr
-// DISASM: bar:
-// DISASM-NEXT: 11004: 1e ff 2f e1 bx lr
-// DISASM: _start:
-// DISASM-NEXT: 11008: 09 00 00 eb bl #36
-// DISASM-NEXT: 1100c: 0c 00 00 eb bl #48
-// DISASM-NEXT: 11010: d4 00 00 e3 movw r0, #212
-// DISASM-NEXT: 11014: 01 00 40 e3 movt r0, #1
-// r0 = 212 + 1 * 65536 = 100D4 = __rel_iplt_start
-// DISASM-NEXT: 11018: e4 00 00 e3 movw r0, #228
-// DISASM-NEXT: 1101c: 01 00 40 e3 movt r0, #1
-// r1 = 228 + 1 * 65536 = 100E4 = __rel_iplt_end
+// DISASM-NEXT: 11000: 1e ff 2f e1 bx lr
+// DISASM: bar:
+// DISASM-NEXT: 11004: 1e ff 2f e1 bx lr
+// DISASM: _start:
+// DISASM-NEXT: 11008: 04 00 00 eb bl #16
+// DISASM-NEXT: 1100c: 07 00 00 eb bl #28
+// 1 * 65536 + 244 = 0x100f4 __rel_iplt_start
+// DISASM-NEXT: 11010: f4 00 00 e3 movw r0, #244
+// DISASM-NEXT: 11014: 01 00 40 e3 movt r0, #1
+// 1 * 65536 + 260 = 0x10104 __rel_iplt_end
+// DISASM-NEXT: 11018: 04 01 00 e3 movw r0, #260
+// DISASM-NEXT: 1101c: 01 00 40 e3 movt r0, #1
// DISASM-NEXT: Disassembly of section .plt:
// DISASM-NEXT: .plt:
-// DISASM-NEXT: 11020: 04 e0 2d e5 str lr, [sp, #-4]!
-// DISASM-NEXT: 11024: 04 e0 9f e5 ldr lr, [pc, #4]
-// DISASM-NEXT: 11028: 0e e0 8f e0 add lr, pc, lr
-// DISASM-NEXT: 1102c: 08 f0 be e5 ldr pc, [lr, #8]!
-// 0x0fd0 + 0x11028 + 0x8 = 0x12000
-// DISASM-NEXT: 11030: d0 0f 00 00
-// DISASM-NEXT: 11034: 04 c0 9f e5 ldr r12, [pc, #4]
-// DISASM-NEXT: 11038: 0f c0 8c e0 add r12, r12, pc
-// DISASM-NEXT: 1103c: 00 f0 9c e5 ldr pc, [r12]
-// 0x0fcc + 0x11038 + 0x8 = 0x1200C
-// DISASM-NEXT: 11040: cc 0f 00 00
-// DISASM-NEXT: 11044: 04 c0 9f e5 ldr r12, [pc, #4]
-// DISASM-NEXT: 11048: 0f c0 8c e0 add r12, r12, pc
-// DISASM-NEXT: 1104c: 00 f0 9c e5 ldr pc, [r12]
-// 0x0fc0 + 0x11048 + 0x8 = 0x12010
-// DISASM-NEXT: 11050: c0 0f 00 00
+// DISASM-NEXT: 11020: 04 c0 9f e5 ldr r12, [pc, #4]
+// DISASM-NEXT: 11024: 0f c0 8c e0 add r12, r12, pc
+// 11024 + 8 + fd4 = 0x12000
+// DISASM-NEXT: 11028: 00 f0 9c e5 ldr pc, [r12]
+// DISASM-NEXT: 1102c: d4 0f 00 00
+// DISASM-NEXT: 11030: 04 c0 9f e5 ldr r12, [pc, #4]
+// DISASM-NEXT: 11034: 0f c0 8c e0 add r12, r12, pc
+// 11034 + 8 + fc8 = 0x12004
+// DISASM-NEXT: 11038: 00 f0 9c e5 ldr pc, [r12]
+// DISASM-NEXT: 1103c: c8 0f 00 00
diff --git a/test/ELF/arm-got-relative.s b/test/ELF/arm-got-relative.s
index 22ccb16a2a58..db76711fe68e 100644
--- a/test/ELF/arm-got-relative.s
+++ b/test/ELF/arm-got-relative.s
@@ -30,7 +30,7 @@ function:
// CHECK: Dynamic Relocations {
// CHECK-NEXT: 0x204C R_ARM_GLOB_DAT function 0x0
-// CHECK: Name: _GLOBAL_OFFSET_TABLE_ (16)
+// CHECK: Name: _GLOBAL_OFFSET_TABLE_
// CHECK-NEXT: Value: 0x0
// CHECK-NEXT: Size:
// CHECK-NEXT: Binding: Local
diff --git a/test/ELF/arm-pie-relative.s b/test/ELF/arm-pie-relative.s
new file mode 100644
index 000000000000..223fcca08a2c
--- /dev/null
+++ b/test/ELF/arm-pie-relative.s
@@ -0,0 +1,25 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t --pie -o %t2
+// RUN: llvm-readobj -r %t2 | FileCheck %s
+// RUN: llvm-objdump -s %t2 | FileCheck %s --check-prefix=GOT
+// REQUIRES: arm
+
+// Test that a R_ARM_GOT_BREL relocation with PIE results in a R_ARM_RELATIVE
+// dynamic relocation
+ .syntax unified
+ .text
+ .global _start
+_start:
+ .word sym(GOT)
+
+ .data
+ .global sym
+sym:
+ .word 0
+
+// CHECK: Relocations [
+// CHECK-NEXT: Section (4) .rel.dyn {
+// CHECK-NEXT: 0x2058 R_ARM_RELATIVE
+
+// GOT: Contents of section .got:
+// GOT-NEXT: 2058 00300000
diff --git a/test/ELF/arm-static-defines.s b/test/ELF/arm-static-defines.s
new file mode 100644
index 000000000000..0012841fb32c
--- /dev/null
+++ b/test/ELF/arm-static-defines.s
@@ -0,0 +1,44 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t --static -o %t2 2>&1
+// RUN: llvm-readobj --symbols %t2 | FileCheck %s
+// REQUIRES: arm
+
+// Check that on ARM we don't get a multiply defined symbol for __tls_get_addr
+// and undefined symbols for references to __exidx_start and __exidx_end
+ .syntax unified
+.section .text
+ .global __tls_get_addr
+__tls_get_addr:
+ bx lr
+
+ .global _start
+ .global __exidx_start
+ .global __exidx_end
+_start:
+ .fnstart
+ bx lr
+ .word __exidx_start
+ .word __exidx_end
+ .cantunwind
+ .fnend
+
+// CHECK: Name: __exidx_end
+// CHECK-NEXT: Value: 0x100E4
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Local
+// CHECK-NEXT: Type: None
+// CHECK-NEXT: Other [
+// CHECK-NEXT: STV_HIDDEN
+// CHECK-NEXT: ]
+// CHECK-NEXT: Section: .ARM.exidx
+// CHECK: Name: __exidx_start
+// CHECK-NEXT: Value: 0x100D4
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Local
+// CHECK-NEXT: Type: None
+// CHECK-NEXT: Other [
+// CHECK-NEXT: STV_HIDDEN
+// CHECK-NEXT: ]
+// CHECK-NEXT: Section: .ARM.exidx
+// CHECK: Symbol {
+// CHECK-NEXT: Name: __tls_get_addr
diff --git a/test/ELF/arm-target1.s b/test/ELF/arm-target1.s
new file mode 100644
index 000000000000..b62d6d663a85
--- /dev/null
+++ b/test/ELF/arm-target1.s
@@ -0,0 +1,32 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
+// RUN: llvm-readobj -r %t.o | FileCheck %s --check-prefix=RELOC
+// RUN: ld.lld -shared %t.o -o %t2.so --target1-rel
+// RUN: llvm-objdump -t -d %t2.so | FileCheck %s \
+// RUN: --check-prefix=RELATIVE
+// RUN: not ld.lld -shared %t.o -o %t3.so 2>&1 | FileCheck %s \
+// RUN: --check-prefix=ABS
+
+// RUN: ld.lld -shared %t.o -o %t2.so --target1-abs --target1-rel
+// RUN: llvm-objdump -t -d %t2.so | FileCheck %s \
+// RUN: --check-prefix=RELATIVE
+// RUN: not ld.lld -shared %t.o -o %t3.so --target1-rel --target1-abs 2>&1 \
+// RUN: | FileCheck %s --check-prefix=ABS
+
+// RELOC: Relocations [
+// RELOC: .rel.text {
+// RELOC: 0x0 R_ARM_TARGET1 patatino 0x0
+// RELOC: }
+// RELOC: ]
+
+.text
+ .word patatino(target1)
+ patatino:
+
+// RELATIVE: Disassembly of section .text:
+// RELATIVE: $d.0:
+// RELATIVE: 1000: 04 00 00 00 .word 0x00000004
+// RELATIVE: SYMBOL TABLE:
+// RELATIVE: 00001004 .text 00000000 patatino
+
+// ABS: {{.*}}.o:(.text+0x0): can't create dynamic relocation R_ARM_TARGET1 against symbol 'patatino' defined in {{.*}}.o
diff --git a/test/ELF/arm-target2.s b/test/ELF/arm-target2.s
new file mode 100644
index 000000000000..a678f7e08fdf
--- /dev/null
+++ b/test/ELF/arm-target2.s
@@ -0,0 +1,60 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
+// RUN: ld.lld %t.o -o %t 2>&1
+// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t | FileCheck %s
+// RUN: ld.lld %t.o --target2=got-rel -o %t2 2>&1
+// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s
+// RUN: ld.lld %t.o --target2=abs -o %t3 2>&1
+// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t3 | FileCheck -check-prefix=CHECK-ABS %s
+// RUN: ld.lld %t.o --target2=rel -o %t4 2>&1
+// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t4 | FileCheck -check-prefix=CHECK-REL %s
+// REQUIRES: arm
+
+// The R_ARM_TARGET2 is present in .ARM.extab sections. It can be handled as
+// either R_ARM_ABS32, R_ARM_REL32 or R_ARM_GOT_PREL. For ARM linux the default
+// is R_ARM_GOT_PREL. The other two options are primarily used for bare-metal,
+// they can be selected with the --target2=abs or --target2=rel option.
+ .syntax unified
+ .text
+ .globl _start
+ .align 2
+_start:
+ .type function, %function
+ .fnstart
+ bx lr
+ .personality __gxx_personality_v0
+ .handlerdata
+ .word _ZTIi(TARGET2)
+ .text
+ .fnend
+ .global __gxx_personality_v0
+ .type function, %function
+__gxx_personality_v0:
+ bx lr
+
+ .rodata
+_ZTIi: .word 0
+
+// CHECK: Contents of section .ARM.extab:
+// 1011c + 1ee4 = 12000 = .got
+// CHECK-NEXT: 10114 f00e0000 b0b0b000 e41e0000
+
+// CHECK-ABS: Contents of section .ARM.extab:
+// 100f0 = .rodata
+// CHECK-ABS-NEXT: 100d4 300f0000 b0b0b000 f0000100
+
+// CHECK-REL: Contents of section .ARM.extab:
+// 100dc + c = 100e8 = .rodata
+// CHECK-REL-NEXT: 100d4 300f0000 b0b0b000 14000000
+
+// CHECK: Contents of section .rodata:
+// CHECK-NEXT: 10130 00000000
+
+// CHECK-ABS: Contents of section .rodata:
+// CHECK-ABS-NEXT: 100f0 00000000
+
+// CHECK-REL: Contents of section .rodata:
+// CHECK-REL-NEXT: 100f0 00000000
+
+// CHECK: Contents of section .got:
+// 10130 = _ZTIi
+// CHECK-NEXT: 12000 30010100
diff --git a/test/ELF/arm-thumb-blx.s b/test/ELF/arm-thumb-blx.s
index b581d1dd3acd..d79bef1835e3 100644
--- a/test/ELF/arm-thumb-blx.s
+++ b/test/ELF/arm-thumb-blx.s
@@ -8,7 +8,7 @@
// RUN: .R_ARM_CALL24_callee4 : { *(.R_ARM_CALL24_callee_thumb_high) } } " > %t.script
// RUN: ld.lld --script %t.script %t %ttarget -o %t2 2>&1
// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-THUMB %s
-// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-ARM %s
+// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-ARM %s
// REQUIRES: arm
// Test BLX instruction is chosen for Thumb BL/BLX instruction and ARM callee
// 2 byte nops are used to test the pc-rounding behaviour. As a BLX from a
diff --git a/test/ELF/arm-thumb-branch.s b/test/ELF/arm-thumb-branch.s
index 94be9ecb6e53..4dc0280b22f6 100644
--- a/test/ELF/arm-thumb-branch.s
+++ b/test/ELF/arm-thumb-branch.s
@@ -1,6 +1,7 @@
// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %S/Inputs/far-arm-thumb-abs.s -o %tfar
// RUN: echo "SECTIONS { \
+// RUN: . = 0xb4; \
// RUN: .callee1 : { *(.callee_low) } \
// RUN: .caller : { *(.text) } \
// RUN: .callee2 : { *(.callee_high) } } " > %t.script
diff --git a/test/ELF/arm-thumb-interwork-thunk-range.s b/test/ELF/arm-thumb-interwork-thunk-range.s
new file mode 100644
index 000000000000..db674f4d5f7c
--- /dev/null
+++ b/test/ELF/arm-thumb-interwork-thunk-range.s
@@ -0,0 +1,15 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
+// RUN: ld.lld %t.o -o %t -image-base=0x80000000
+
+// Test that when the thunk is at a high address we don't get confused with it
+// being out of range.
+
+.thumb
+.global _start
+_start:
+b.w foo
+
+.arm
+.weak foo
+foo:
diff --git a/test/ELF/arm-thumb-interwork-thunk.s b/test/ELF/arm-thumb-interwork-thunk.s
index 6173df3c066b..3fd36b910cd1 100644
--- a/test/ELF/arm-thumb-interwork-thunk.s
+++ b/test/ELF/arm-thumb-interwork-thunk.s
@@ -1,12 +1,14 @@
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
// RUN: echo "SECTIONS { \
+// RUN: . = SIZEOF_HEADERS; \
// RUN: .R_ARM_JUMP24_callee_1 : { *(.R_ARM_JUMP24_callee_low) } \
// RUN: .R_ARM_THM_JUMP_callee_1 : { *(.R_ARM_THM_JUMP_callee_low)} \
// RUN: .text : { *(.text) } \
// RUN: .arm_caller : { *(.arm_caller) } \
// RUN: .thumb_caller : { *(.thumb_caller) } \
// RUN: .R_ARM_JUMP24_callee_2 : { *(.R_ARM_JUMP24_callee_high) } \
-// RUN: .R_ARM_THM_JUMP_callee_2 : { *(.R_ARM_THM_JUMP_callee_high) } } " > %t.script
+// RUN: .R_ARM_THM_JUMP_callee_2 : { *(.R_ARM_THM_JUMP_callee_high) } \
+// RUN: .got.plt 0x1894 : { } } " > %t.script
// RUN: ld.lld --script %t.script %t -o %t2 2>&1
// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-THUMB -check-prefix=CHECK-ABS-THUMB %s
// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-ARM -check-prefix=CHECK-ABS-ARM %s
diff --git a/test/ELF/arm-thumb-narrow-branch-check.s b/test/ELF/arm-thumb-narrow-branch-check.s
index b601b6d5dc26..82a7164f6df6 100644
--- a/test/ELF/arm-thumb-narrow-branch-check.s
+++ b/test/ELF/arm-thumb-narrow-branch-check.s
@@ -1,5 +1,6 @@
// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
// RUN: echo "SECTIONS { \
+// RUN: . = SIZEOF_HEADERS; \
// RUN: .R_ARM_PC11_1 : { *(.R_ARM_PC11_1) } \
// RUN: .caller : { *(.caller) } \
// RUN: .R_ARM_PC11_2 : { *(.R_ARM_PC11_2) } \
diff --git a/test/ELF/arm-thumb-no-undefined-thunk.s b/test/ELF/arm-thumb-no-undefined-thunk.s
new file mode 100644
index 000000000000..f668c8c278de
--- /dev/null
+++ b/test/ELF/arm-thumb-no-undefined-thunk.s
@@ -0,0 +1,24 @@
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1
+// RUN: llvm-objdump -triple=thumbv7a-none-linux-gnueabi -d %t2 | FileCheck %s
+// REQUIRES: arm
+
+// Check that no thunks are created for an undefined weak symbol
+ .syntax unified
+
+.weak target
+
+.section .text.thumb, "ax", %progbits
+ .thumb
+ .global
+_start:
+ bl target
+ b target
+ b.w target
+
+// CHECK: Disassembly of section .text:
+// CHECK-NEXT: _start:
+// 69636 = 0x11004 = next instruction
+// CHECK: 11000: 11 f0 02 f8 bl #69636
+// CHECK-NEXT: 11004: 11 f0 04 b8 b.w #69640
+// CHECK-NEXT: 11008: 11 f0 06 b8 b.w #69644
diff --git a/test/ELF/arm-thumb-undefined-weak.s b/test/ELF/arm-thumb-undefined-weak.s
new file mode 100644
index 000000000000..087f8e71cb21
--- /dev/null
+++ b/test/ELF/arm-thumb-undefined-weak.s
@@ -0,0 +1,38 @@
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1
+// RUN: llvm-objdump -triple=thumbv7a-none-linux-gnueabi -d %t2 | FileCheck %s
+// REQUIRES: arm
+
+// Check that the ARM ABI rules for undefined weak symbols are applied.
+// Branch instructions are resolved to the next instruction. Relative
+// relocations are resolved to the place.
+
+ .syntax unified
+
+ .weak target
+
+ .text
+ .global _start
+_start:
+// R_ARM_THM_JUMP19
+ beq.w target
+// R_ARM_THM_JUMP24
+ b.w target
+// R_ARM_THM_CALL
+ bl target
+// R_ARM_THM_CALL with exchange
+ blx target
+// R_ARM_THM_MOVT_PREL
+ movt r0, :upper16:target - .
+// R_ARM_THM_MOVW_PREL_NC
+ movw r0, :lower16:target - .
+
+// CHECK: Disassembly of section .text:
+// 69636 = 0x11004
+// CHECK: 11000: 11 f0 02 80 beq.w #69636
+// CHECK-NEXT: 11004: 11 f0 04 b8 b.w #69640
+// CHECK-NEXT: 11008: 11 f0 06 f8 bl #69644
+// blx is transformed into bl so we don't change state
+// CHECK-NEXT: 1100c: 11 f0 08 f8 bl #69648
+// CHECK-NEXT: 11010: c0 f2 00 00 movt r0, #0
+// CHECK-NEXT: 11014: 40 f2 00 00 movw r0, #0
diff --git a/test/ELF/arm-tls-gd32.s b/test/ELF/arm-tls-gd32.s
new file mode 100644
index 000000000000..206b65d05af9
--- /dev/null
+++ b/test/ELF/arm-tls-gd32.s
@@ -0,0 +1,106 @@
+// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=armv7a-linux-gnueabi
+// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: llvm-readobj -s -dyn-relocations %t.so | FileCheck --check-prefix=SEC %s
+// RUN: llvm-objdump -d -triple=armv7a-linux-gnueabi %t.so | FileCheck %s
+// REQUIRES: arm
+
+// Test the handling of the global-dynamic TLS model. Dynamic Loader finds
+// module index R_ARM_TLS_DTPMOD32 and the offset within the module
+// R_ARM_TLS_DTPOFF32. One of the variables is hidden which permits relaxation
+// to local dynamic
+
+ .text
+ .syntax unified
+ .globl func
+ .p2align 2
+ .type func,%function
+func:
+.L0:
+ nop
+.L1:
+ nop
+.L2:
+ nop
+
+ .p2align 2
+// Generate R_ARM_TLS_GD32 relocations
+// Allocates a pair of GOT entries dynamically relocated by R_ARM_TLS_DTPMOD32
+// and R_ARM_TLS_DTPOFF32 respectively. The literal contains the offset of the
+// first GOT entry from the place
+.Lt0: .word x(TLSGD) + (. - .L0 - 8)
+.Lt1: .word y(TLSGD) + (. - .L1 - 8)
+.Lt2: .word z(TLSGD) + (. - .L2 - 8)
+
+// __thread int x = 10
+// __thread int y;
+// __thread int z __attribute((visibility("hidden")))
+
+ .hidden z
+ .globl z
+ .globl y
+ .globl x
+
+ .section .tbss,"awT",%nobits
+ .p2align 2
+.TLSSTART:
+ .type z, %object
+z:
+ .space 4
+ .type y, %object
+y:
+ .space 4
+ .section .tdata,"awT",%progbits
+ .p2align 2
+ .type x, %object
+x:
+ .word 10
+
+// SEC: Name: .tdata
+// SEC-NEXT: Type: SHT_PROGBITS
+// SEC-NEXT: Flags [
+// SEC-NEXT: SHF_ALLOC
+// SEC-NEXT: SHF_TLS
+// SEC-NEXT: SHF_WRITE
+// SEC-NEXT: ]
+// SEC-NEXT: Address: 0x2000
+// SEC: Size: 4
+// SEC: Name: .tbss
+// SEC-NEXT: Type: SHT_NOBITS
+// SEC-NEXT: Flags [
+// SEC-NEXT: SHF_ALLOC
+// SEC-NEXT: SHF_TLS
+// SEC-NEXT: SHF_WRITE
+// SEC-NEXT: ]
+// SEC-NEXT: Address: 0x2004
+// SEC: Size: 8
+
+// SEC: Name: .got
+// SEC-NEXT: Type: SHT_PROGBITS
+// SEC-NEXT: Flags [
+// SEC-NEXT: SHF_ALLOC
+// SEC-NEXT: SHF_WRITE
+// SEC-NEXT: ]
+// SEC-NEXT: Address: 0x204C
+// SEC: Size: 24
+
+// SEC: Dynamic Relocations {
+// SEC-NEXT: 0x205C R_ARM_TLS_DTPMOD32 -
+// SEC-NEXT: 0x204C R_ARM_TLS_DTPMOD32 x
+// SEC-NEXT: 0x2050 R_ARM_TLS_DTPOFF32 x
+// SEC-NEXT: 0x2054 R_ARM_TLS_DTPMOD32 y
+// SEC-NEXT: 0x2058 R_ARM_TLS_DTPOFF32 y
+
+
+// CHECK: Disassembly
+// CHECK-NEXT: func:
+// CHECK-NEXT: 1000: 00 f0 20 e3 nop
+// CHECK-NEXT: 1004: 00 f0 20 e3 nop
+// CHECK-NEXT: 1008: 00 f0 20 e3 nop
+
+// (0x204c - 0x100c) + (0x100c - 0x1000 - 8) = 0x1044
+// CHECK: 100c: 44 10 00 00
+// (0x2054 - 0x1010) + (0x1010 - 0x1004 - 8) = 0x1048
+// CHECK-NEXT: 1010: 48 10 00 00
+// (0x205c - 0x1014) + (0x1014 - 0x1008 - 8) = 0x104c
+// CHECK-NEXT: 1014: 4c 10 00 00
+
diff --git a/test/ELF/arm-tls-ie32.s b/test/ELF/arm-tls-ie32.s
new file mode 100644
index 000000000000..48120fa682da
--- /dev/null
+++ b/test/ELF/arm-tls-ie32.s
@@ -0,0 +1,96 @@
+// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=armv7a-linux-gnueabi
+// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: llvm-readobj -s -dyn-relocations %t.so | FileCheck --check-prefix=SEC %s
+// RUN: llvm-objdump -d -triple=armv7a-linux-gnueabi %t.so | FileCheck %s
+// REQUIRES: arm
+
+// Test the handling of the initial-exec TLS model. Relative location within
+// static TLS is a run-time constant computed by dynamic loader as a result
+// of the R_ARM_TLS_TPOFF32 relocation.
+
+ .syntax unified
+ .arm
+ .globl func
+ .type func,%function
+ .p2align 2
+func:
+.L0:
+ nop
+.L1:
+ nop
+.L2:
+ nop
+
+ .p2align 2
+// Generate R_ARM_TLS_IE32 static relocations
+// Allocates a GOT entry dynamically relocated by R_ARM_TLS_TPOFF32
+// literal contains the offset of the GOT entry from the place
+.Lt0: .word x(gottpoff) + (. - .L0 - 8)
+.Lt1: .word y(gottpoff) + (. - .L1 - 8)
+.Lt2: .word .TLSSTART(gottpoff) + (. - .L2 - 8)
+
+// __thread int x = 10
+// __thread int y;
+// __thread int z __attribute((visibility("hidden")))
+ .hidden z
+ .globl z
+ .globl y
+ .globl x
+
+ .section .tbss,"awT",%nobits
+ .p2align 2
+.TLSSTART:
+ .type z, %object
+z:
+ .space 4
+ .type y, %object
+y:
+ .space 4
+ .section .tdata,"awT",%progbits
+ .p2align 2
+ .type x, %object
+x:
+ .word 10
+
+// SEC: Name: .tdata
+// SEC-NEXT: Type: SHT_PROGBITS
+// SEC-NEXT: Flags [
+// SEC-NEXT: SHF_ALLOC
+// SEC-NEXT: SHF_TLS
+// SEC-NEXT: SHF_WRITE
+// SEC: Size: 4
+// SEC: Name: .tbss
+// SEC-NEXT: Type: SHT_NOBITS
+// SEC-NEXT: Flags [
+// SEC-NEXT: SHF_ALLOC
+// SEC-NEXT: SHF_TLS
+// SEC-NEXT: SHF_WRITE
+// SEC: Size: 8
+
+// SEC: Name: .got
+// SEC-NEXT: Type: SHT_PROGBITS
+// SEC-NEXT: Flags [
+// SEC-NEXT: SHF_ALLOC
+// SEC-NEXT: SHF_WRITE
+// SEC-NEXT: ]
+// SEC-NEXT: Address: 0x204C
+// SEC: Size: 12
+
+
+// SEC: Dynamic Relocations {
+// SEC: 0x2054 R_ARM_TLS_TPOFF32
+// SEC: 0x204C R_ARM_TLS_TPOFF32 x
+// SEC: 0x2050 R_ARM_TLS_TPOFF32 y
+
+// CHECK: Disassembly of section .text:
+// CHECK-NEXT: func:
+// CHECK-NEXT: 1000: 00 f0 20 e3 nop
+// CHECK-NEXT: 1004: 00 f0 20 e3 nop
+// CHECK-NEXT: 1008: 00 f0 20 e3 nop
+
+// (0x204c - 0x100c) + (0x100c - 0x1000 - 8) = 0x1044
+// CHECK: 100c: 44 10 00 00
+// (0x2050 - 0x1010) + (0x1010 - 0x1004 - 8) = 0x1044
+// CHECK-NEXT: 1010: 44 10 00 00
+// (0x2054 - 0x1014) + (0x1014 - 0x1008 - 8) = 0x1044
+// CHECK-NEXT: 1014: 44 10 00 00
diff --git a/test/ELF/arm-tls-ldm32.s b/test/ELF/arm-tls-ldm32.s
new file mode 100644
index 000000000000..47e879102707
--- /dev/null
+++ b/test/ELF/arm-tls-ldm32.s
@@ -0,0 +1,73 @@
+// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=armv7a-linux-gnueabi
+// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: llvm-readobj -s -dyn-relocations %t.so | FileCheck --check-prefix=SEC %s
+// RUN: llvm-objdump -d -triple=armv7a-linux-gnueabi %t.so | FileCheck %s
+// REQUIRES: arm
+
+// Test the handling of the local-dynamic TLS model. Dynamic loader finds
+// module index R_ARM_TLS_DTPMOD32. The offset in the next GOT slot is 0
+// The R_ARM_TLS_LDO is the offset of the variable within the TLS block.
+ .global __tls_get_addr
+ .text
+ .p2align 2
+ .global _start
+ .syntax unified
+ .arm
+ .type _start, %function
+_start:
+.L0:
+ nop
+
+ .word x(tlsldm) + (. - .L0 - 8)
+ .word x(tlsldo)
+ .word y(tlsldo)
+
+ .section .tbss,"awT",%nobits
+ .p2align 2
+ .type y, %object
+y:
+ .space 4
+ .section .tdata,"awT",%progbits
+ .p2align 2
+ .type x, %object
+x:
+ .word 10
+
+// SEC: Name: .tdata
+// SEC-NEXT: Type: SHT_PROGBITS
+// SEC-NEXT: Flags [
+// SEC-NEXT: SHF_ALLOC
+// SEC-NEXT: SHF_TLS
+// SEC-NEXT: SHF_WRITE
+// SEC-NEXT: ]
+// SEC-NEXT: Address: 0x2000
+// SEC: Size: 4
+// SEC: Name: .tbss
+// SEC-NEXT: Type: SHT_NOBITS (0x8)
+// SEC-NEXT: Flags [
+// SEC-NEXT: SHF_ALLOC
+// SEC-NEXT: SHF_TLS
+// SEC-NEXT: SHF_WRITE
+// SEC-NEXT: ]
+// SEC-NEXT: Address: 0x2004
+// SEC: Size: 4
+
+// SEC: Dynamic Relocations {
+// SEC-NEXT: 0x204C R_ARM_TLS_DTPMOD32 - 0x0
+
+// CHECK: Disassembly of section .text:
+// CHECK-NEXT: _start:
+// CHECK-NEXT: 1000: 00 f0 20 e3 nop
+
+// (0x204c - 0x1004) + (0x1004 - 0x1000 - 8) = 0x1044
+// CHECK: 1004: 44 10 00 00
+// CHECK-NEXT: 1008: 00 00 00 00
+// CHECK-NEXT: 100c: 04 00 00 00
+
+// CHECK-EXE: Disassembly of section .text:
+// CHECK-NEXT-EXE: _start:
+// CHECK-NEXT-EXE: 11000: 00 f0 20 e3 nop
+
+// CHECK-EXE: 11004: fc 0f 00 00
+// CHECK-EXE: 11008: 00 00 00 00
+// CHECK-EXE: 1100c: 04 00 00 00
diff --git a/test/ELF/arm-tls-le32.s b/test/ELF/arm-tls-le32.s
new file mode 100644
index 000000000000..4d42a06b0fff
--- /dev/null
+++ b/test/ELF/arm-tls-le32.s
@@ -0,0 +1,77 @@
+// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=armv7a-linux-gnueabi
+// RUN: ld.lld %t.o -o %t
+// RUN: llvm-readobj -s -dyn-relocations %t | FileCheck --check-prefix=SEC %s
+// RUN: llvm-objdump -d -triple=armv7a-linux-gnueabi %t | FileCheck %s
+// REQUIRES: arm
+
+// Test the handling of the local exec TLS model. TLS can be resolved
+// statically for an application. The code sequences assume a thread pointer
+// in r9
+
+ .text
+ .syntax unified
+ .globl _start
+ .p2align 2
+ .type _start,%function
+_start:
+ .p2align 2
+// Generate R_ARM_TLS_LE32 relocations. These resolve statically to the offset
+// of the variable from the thread pointer
+.Lt0: .word x(TPOFF)
+.Lt1: .word y(TPOFF)
+.Lt2: .word z(TPOFF)
+
+// __thread int x = 10
+// __thread int y;
+// __thread int z __attribute((visibility("hidden")))
+
+ .hidden z
+ .globl z
+ .globl y
+ .globl x
+
+ .section .tbss,"awT",%nobits
+ .p2align 2
+.TLSSTART:
+ .type z, %object
+z:
+ .space 4
+ .type y, %object
+y:
+ .space 4
+ .section .tdata,"awT",%progbits
+ .p2align 2
+ .type x, %object
+x:
+ .word 10
+
+// SEC: Name: .tdata
+// SEC-NEXT: Type: SHT_PROGBITS
+// SEC-NEXT: Flags [
+// SEC-NEXT: SHF_ALLOC
+// SEC-NEXT: SHF_TLS
+// SEC-NEXT: SHF_WRITE
+// SEC-NEXT: ]
+// SEC-NEXT: Address: 0x12000
+// SEC: Size: 4
+// SEC: Name: .tbss
+// SEC-NEXT: Type: SHT_NOBITS
+// SEC-NEXT: Flags [
+// SEC-NEXT: SHF_ALLOC
+// SEC-NEXT: SHF_TLS
+// SEC-NEXT: SHF_WRITE
+// SEC-NEXT: ]
+// SEC-NEXT: Address: 0x12004
+// SEC: Size: 8
+
+// SEC: Dynamic Relocations {
+// SEC-NEXT: }
+
+// CHECK: Disassembly of section .text:
+// CHECK-NEXT: _start:
+// offset of x from Thread pointer = (TcbSize + 0x0 = 0x8)
+// CHECK-NEXT: 11000: 08 00 00 00
+// offset of z from Thread pointer = (TcbSize + 0x8 = 0x10)
+// CHECK-NEXT: 11004: 10 00 00 00
+// offset of y from Thread pointer = (TcbSize + 0x4 = 0xc)
+// CHECK-NEXT: 11008: 0c 00 00 00
diff --git a/test/ELF/arm-tls-norelax-gd-ie.s b/test/ELF/arm-tls-norelax-gd-ie.s
new file mode 100644
index 000000000000..3bd3db08ccf4
--- /dev/null
+++ b/test/ELF/arm-tls-norelax-gd-ie.s
@@ -0,0 +1,30 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %p/Inputs/arm-tls-get-addr.s -o %t1
+// RUN: ld.lld %t1 --shared -o %t1.so
+// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=armv7a-linux-gnueabi
+// RUN: ld.lld %t1.so %t.o -o %t
+// RUN: llvm-readobj -s -dyn-relocations %t | FileCheck %s
+// REQUIRES: arm
+
+// This tls global-dynamic sequence is with respect to a preemptible symbol but
+// is in an application so a relaxation to Initial Exec would normally be
+// possible. This would result in an assertion failure on ARM as the
+// relaxation functions can't be implemented on ARM. Check that the sequence
+// is handled as global dynamic
+
+ .text
+ .syntax unified
+ .globl func
+ .p2align 2
+ .type func,%function
+func:
+.L0:
+ .globl __tls_get_addr
+ bl __tls_get_addr
+ bx lr
+ .p2align 2
+ .Lt0: .word y(TLSGD) + (. - .L0 - 8)
+
+// CHECK: Dynamic Relocations {
+// CHECK-NEXT: 0x12078 R_ARM_TLS_DTPMOD32 y
+// CHECK-NEXT: 0x1207C R_ARM_TLS_DTPOFF32 y
+// CHECK-NEXT: 0x1300C R_ARM_JUMP_SLOT __tls_get_addr
diff --git a/test/ELF/arm-tls-norelax-gd-le.s b/test/ELF/arm-tls-norelax-gd-le.s
new file mode 100644
index 000000000000..67952b1f8565
--- /dev/null
+++ b/test/ELF/arm-tls-norelax-gd-le.s
@@ -0,0 +1,37 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %p/Inputs/arm-tls-get-addr.s -o %t1
+// RUN: ld.lld %t1 --shared -o %t1.so
+// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=armv7a-linux-gnueabi
+// RUN: ld.lld %t1.so %t.o -o %t
+// RUN: llvm-objdump -s %t | FileCheck %s
+// REQUIRES: arm
+
+// This tls global-dynamic sequence is with respect to a non-preemptible
+// symbol in an application so a relaxation to Local Exec would normally be
+// possible. This would result in an assertion failure on ARM as the
+// relaxation functions can't be implemented on ARM. Check that the sequence
+// is handled as global dynamic
+
+ .text
+ .syntax unified
+ .globl func
+ .p2align 2
+ .type func,%function
+func:
+.L0:
+ .globl __tls_get_addr
+ bl __tls_get_addr
+ bx lr
+ .p2align 2
+ .Lt0: .word x(TLSGD) + (. - .L0 - 8)
+
+ .globl x
+.section .tbss,"awT",%nobits
+ .p2align 2
+x:
+ .space 4
+ .type x, %object
+
+// CHECK: Contents of section .got:
+// Module index is always 1 for executable
+// CHECK-NEXT: 12060 01000000 00000000
+
diff --git a/test/ELF/arm-tls-norelax-ie-le.s b/test/ELF/arm-tls-norelax-ie-le.s
new file mode 100644
index 000000000000..ecb68293829a
--- /dev/null
+++ b/test/ELF/arm-tls-norelax-ie-le.s
@@ -0,0 +1,41 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %p/Inputs/arm-tls-get-addr.s -o %t1
+// RUN: ld.lld %t1 --shared -o %t1.so
+// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=armv7a-linux-gnueabi
+// RUN: ld.lld %t1.so %t.o -o %t
+// RUN: llvm-objdump -s -triple=armv7a-linux-gnueabi %t | FileCheck %s
+// REQUIRES: arm
+
+// This tls Initial Exec sequence is with respect to a non-preemptible symbol
+// so a relaxation would normally be possible. This would result in an assertion
+// failure on ARM as the relaxation functions can't be implemented on ARM.
+// Check that the sequence is handled as initial exec
+ .text
+ .syntax unified
+ .globl func
+ .p2align 2
+ .type func,%function
+func:
+.L0:
+ .globl __tls_get_addr
+ bl __tls_get_addr
+.L1:
+ bx lr
+ .p2align 2
+ .Lt0: .word x1(gottpoff) + (. - .L0 - 8)
+ .Lt1: .word x2(gottpoff) + (. - .L1 - 8)
+
+ .globl x1
+ .section .trw,"awT",%progbits
+ .p2align 2
+x1:
+ .word 0x1
+ .globl x2
+ .section .tbss,"awT",%nobits
+ .type x1, %object
+x2:
+ .space 4
+ .type x2, %object
+
+// CHECK: Contents of section .got
+// x1 at offset 8 from TP, x2 at offset c from TP. Offsets include TCB size of 8
+// CHECK-NEXT: 12064 08000000 0c000000
diff --git a/test/ELF/arm-tls-norelax-ld-le.s b/test/ELF/arm-tls-norelax-ld-le.s
new file mode 100644
index 000000000000..380610ef394b
--- /dev/null
+++ b/test/ELF/arm-tls-norelax-ld-le.s
@@ -0,0 +1,35 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %p/Inputs/arm-tls-get-addr.s -o %t1
+// RUN: ld.lld %t1 --shared -o %t1.so
+// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=armv7a-linux-gnueabi
+// RUN: ld.lld %t1.so %t.o -o %t
+// RUN: llvm-objdump -s %t | FileCheck %s
+// REQUIRES: arm
+
+ .global __tls_get_addr
+ .text
+ .p2align 2
+ .global _start
+ .syntax unified
+ .arm
+ .type _start, %function
+_start:
+.L0:
+ bl __tls_get_addr
+
+ .word x(tlsldm) + (. - .L0 - 8)
+ .word x(tlsldo)
+ .word y(tlsldo)
+
+ .section .tbss,"awT",%nobits
+ .p2align 2
+ .type y, %object
+y:
+ .space 4
+ .section .tdata,"awT",%progbits
+ .p2align 2
+ .type x, %object
+x:
+ .word 10
+
+// CHECK: Contents of section .got:
+// CHECK-NEXT: 12064 01000000 00000000
diff --git a/test/ELF/arm-undefined-weak.s b/test/ELF/arm-undefined-weak.s
new file mode 100644
index 000000000000..df67b467e263
--- /dev/null
+++ b/test/ELF/arm-undefined-weak.s
@@ -0,0 +1,39 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1
+// RUN: llvm-objdump -triple=armv7a-none-linux-gnueabi -d %t2 | FileCheck %s
+// REQUIRES: arm
+
+// Check that the ARM ABI rules for undefined weak symbols are applied.
+// Branch instructions are resolved to the next instruction. Undefined
+// Symbols in relative are resolved to the place so S - P + A = A.
+
+ .syntax unified
+
+ .weak target
+
+ .text
+ .global _start
+_start:
+// R_ARM_JUMP24
+ b target
+// R_ARM_CALL
+ bl target
+// R_ARM_CALL with exchange
+ blx target
+// R_ARM_MOVT_PREL
+ movt r0, :upper16:target - .
+// R_ARM_MOVW_PREL_NC
+ movw r0, :lower16:target - .
+// R_ARM_REL32
+ .word target - .
+
+// CHECK: Disassembly of section .text:
+// 69636 = 0x11004
+// CHECK: 11000: 01 44 00 ea b #69636
+// CHECK-NEXT: 11004: 02 44 00 eb bl #69640
+// blx is transformed into bl so we don't change state
+// CHECK-NEXT: 11008: 03 44 00 eb bl #69644
+// CHECK-NEXT: 1100c: 00 00 40 e3 movt r0, #0
+// CHECK-NEXT: 11010: 00 00 00 e3 movw r0, #0
+// CHECK: 11014: 00 00 00 00 .word 0x00000000
+
diff --git a/test/ELF/arm-use-r-output.s b/test/ELF/arm-use-r-output.s
new file mode 100644
index 000000000000..918362466d3a
--- /dev/null
+++ b/test/ELF/arm-use-r-output.s
@@ -0,0 +1,13 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
+// RUN: ld.lld -r %t.o -o %t2.o
+// RUN: ld.lld -shared %t2.o -o %t.so
+
+// We used to crash using the output of -r because of the relative order of
+// SHF_LINK_ORDER sections.
+
+// That can be fixed by changing -r or making the regular link more flexible,
+// so this is an end to end test.
+
+ .fnstart
+ .fnend
diff --git a/test/ELF/auxiliary.s b/test/ELF/auxiliary.s
new file mode 100644
index 000000000000..236d0a421d4f
--- /dev/null
+++ b/test/ELF/auxiliary.s
@@ -0,0 +1,12 @@
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -shared -f aaa --auxiliary bbb -o %t
+# RUN: llvm-readobj --dynamic-table %t | FileCheck %s
+
+# CHECK: DynamicSection [
+# CHECK-NEXT: Tag Type Name/Value
+# CHECK-NEXT: 0x000000007FFFFFFD AUXILIARY Auxiliary library: [aaa]
+# CHECK-NEXT: 0x000000007FFFFFFD AUXILIARY Auxiliary library: [bbb]
+
+# RUN: not ld.lld %t.o -f aaa --auxiliary bbb -o %t 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR %s
+# ERR: -f may not be used without -shared
diff --git a/test/ELF/avoid-empty-program-headers.s b/test/ELF/avoid-empty-program-headers.s
index f7315677e1a3..271d1a303850 100644
--- a/test/ELF/avoid-empty-program-headers.s
+++ b/test/ELF/avoid-empty-program-headers.s
@@ -16,8 +16,8 @@ _start:
// CHECK-NEXT: ProgramHeader {
// CHECK-NEXT: Type: PT_PHDR (0x6)
// CHECK-NEXT: Offset: 0x40
-// CHECK-NEXT: VirtualAddress: 0x10040
-// CHECK-NEXT: PhysicalAddress: 0x10040
+// CHECK-NEXT: VirtualAddress: 0x200040
+// CHECK-NEXT: PhysicalAddress: 0x200040
// CHECK-NEXT: FileSize: 280
// CHECK-NEXT: MemSize: 280
// CHECK-NEXT: Flags [ (0x4)
@@ -28,8 +28,8 @@ _start:
// CHECK-NEXT: ProgramHeader {
// CHECK-NEXT: Type: PT_LOAD (0x1)
// CHECK-NEXT: Offset: 0x0
-// CHECK-NEXT: VirtualAddress: 0x10000
-// CHECK-NEXT: PhysicalAddress: 0x10000
+// CHECK-NEXT: VirtualAddress: 0x200000
+// CHECK-NEXT: PhysicalAddress: 0x200000
// CHECK-NEXT: FileSize: 344
// CHECK-NEXT: MemSize: 344
// CHECK-NEXT: Flags [ (0x4)
@@ -40,8 +40,8 @@ _start:
// CHECK-NEXT: ProgramHeader {
// CHECK-NEXT: Type: PT_LOAD (0x1)
// CHECK-NEXT: Offset: 0x1000
-// CHECK-NEXT: VirtualAddress: 0x11000
-// CHECK-NEXT: PhysicalAddress: 0x11000
+// CHECK-NEXT: VirtualAddress: 0x201000
+// CHECK-NEXT: PhysicalAddress: 0x201000
// CHECK-NEXT: FileSize: 1
// CHECK-NEXT: MemSize: 1
// CHECK-NEXT: Flags [ (0x5)
@@ -53,8 +53,8 @@ _start:
// CHECK-NEXT: ProgramHeader {
// CHECK-NEXT: Type: PT_TLS (0x7)
// CHECK-NEXT: Offset: 0x1001
-// CHECK-NEXT: VirtualAddress: 0x11001
-// CHECK-NEXT: PhysicalAddress: 0x11001
+// CHECK-NEXT: VirtualAddress: 0x201001
+// CHECK-NEXT: PhysicalAddress: 0x201001
// CHECK-NEXT: FileSize: 0
// CHECK-NEXT: MemSize: 4
// CHECK-NEXT: Flags [ (0x4)
diff --git a/test/ELF/bad-archive.s b/test/ELF/bad-archive.s
new file mode 100644
index 000000000000..39c8e160f95d
--- /dev/null
+++ b/test/ELF/bad-archive.s
@@ -0,0 +1,11 @@
+// REQUIRES: x86
+
+// Check bad archive error reporting with --whole-archive
+// and without it.
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+// RUN: not ld.lld %t.o %p/Inputs/bad-archive.a -o %t 2>&1 | FileCheck %s
+// RUN: not ld.lld %t.o --whole-archive %p/Inputs/bad-archive.a -o %t 2>&1 | FileCheck %s
+// CHECK: bad-archive.a: failed to parse archive
+
+.globl _start
+_start:
diff --git a/test/ELF/basic-aarch64.s b/test/ELF/basic-aarch64.s
index 84105f0acdb7..b68a2f9942a7 100644
--- a/test/ELF/basic-aarch64.s
+++ b/test/ELF/basic-aarch64.s
@@ -26,15 +26,15 @@ _start:
# CHECK-NEXT: Version: 1
# CHECK-NEXT: Entry: [[ENTRY:0x[0-9A-F]+]]
# CHECK-NEXT: ProgramHeaderOffset: 0x40
-# CHECK-NEXT: SectionHeaderOffset: 0x1088
+# CHECK-NEXT: SectionHeaderOffset: 0x10098
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: HeaderSize: 64
# CHECK-NEXT: ProgramHeaderEntrySize: 56
# CHECK-NEXT: ProgramHeaderCount: 4
# CHECK-NEXT: SectionHeaderEntrySize: 64
-# CHECK-NEXT: SectionHeaderCount: 5
-# CHECK-NEXT: StringTableSectionIndex: 3
+# CHECK-NEXT: SectionHeaderCount: 6
+# CHECK-NEXT: StringTableSectionIndex: 4
# CHECK-NEXT: }
# CHECK-NEXT: Sections [
# CHECK-NEXT: Section {
@@ -59,8 +59,8 @@ _start:
# CHECK-NEXT: SHF_ALLOC (0x2)
# CHECK-NEXT: SHF_EXECINSTR (0x4)
# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x11000
-# CHECK-NEXT: Offset: 0x1000
+# CHECK-NEXT: Address: 0x20000
+# CHECK-NEXT: Offset: 0x10000
# CHECK-NEXT: Size: 12
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
@@ -69,40 +69,56 @@ _start:
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 2
+# CHECK-NEXT: Name: .comment
+# CHECK-NEXT: Type: SHT_PROGBITS (0x1)
+# CHECK-NEXT: Flags [ (0x30)
+# CHECK-NEXT: SHF_MERGE (0x10)
+# CHECK-NEXT: SHF_STRINGS (0x20)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x0
+# CHECK-NEXT: Offset: 0x1000C
+# CHECK-NEXT: Size: 8
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 1
+# CHECK-NEXT: EntrySize: 1
+# CHECK-NEXT: }
+# CHECK-NEXT: Section {
+# CHECK-NEXT: Index: 3
# CHECK-NEXT: Name: .symtab
# CHECK-NEXT: Type: SHT_SYMTAB (0x2)
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x1010
+# CHECK-NEXT: Offset: 0x10018
# CHECK-NEXT: Size: 72
-# CHECK-NEXT: Link: 4
+# CHECK-NEXT: Link: 5
# CHECK-NEXT: Info: 2
# CHECK-NEXT: AddressAlignment: 8
# CHECK-NEXT: EntrySize: 24
# CHECK-NEXT: }
# CHECK-NEXT: Section {
-# CHECK-NEXT: Index: 3
+# CHECK-NEXT: Index: 4
# CHECK-NEXT: Name: .shstrtab
# CHECK-NEXT: Type: SHT_STRTAB (0x3)
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x1058
-# CHECK-NEXT: Size: 33
+# CHECK-NEXT: Offset: 0x10060
+# CHECK-NEXT: Size: 42
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
# CHECK-NEXT: AddressAlignment: 1
# CHECK-NEXT: EntrySize: 0
# CHECK-NEXT: }
# CHECK-NEXT: Section {
-# CHECK-NEXT: Index: 4
-# CHECK-NEXT: Name: .strtab (25)
+# CHECK-NEXT: Index: 5
+# CHECK-NEXT: Name: .strtab
# CHECK-NEXT: Type: SHT_STRTAB (0x3)
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x1079
+# CHECK-NEXT: Offset: 0x1008A
# CHECK-NEXT: Size: 13
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
@@ -122,7 +138,7 @@ _start:
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
# CHECK-NEXT: Name: $x.0
-# CHECK-NEXT: Value: 0x11000
+# CHECK-NEXT: Value: 0x20000
# CHECK-NEXT: Size: 0
# CHECK-NEXT: Binding: Local (0x0)
# CHECK-NEXT: Type: None (0x0)
@@ -162,20 +178,20 @@ _start:
# CHECK-NEXT: Flags [
# CHECK-NEXT: PF_R
# CHECK-NEXT: ]
-# CHECK-NEXT: Alignment: 4096
+# CHECK-NEXT: Alignment: 65536
# CHECK-NEXT: }
# CHECK-NEXT: ProgramHeader {
# CHECK-NEXT: Type: PT_LOAD (0x1)
# CHECK-NEXT: Offset: 0x1000
-# CHECK-NEXT: VirtualAddress: 0x11000
-# CHECK-NEXT: PhysicalAddress: 0x11000
+# CHECK-NEXT: VirtualAddress: 0x20000
+# CHECK-NEXT: PhysicalAddress: 0x20000
# CHECK-NEXT: FileSize: 12
# CHECK-NEXT: MemSize: 12
# CHECK-NEXT: Flags [ (0x5)
# CHECK-NEXT: PF_R (0x4)
# CHECK-NEXT: PF_X (0x1)
# CHECK-NEXT: ]
-# CHECK-NEXT: Alignment: 4096
+# CHECK-NEXT: Alignment: 65536
# CHECK-NEXT: }
# CHECK-NEXT: ProgramHeader {
# CHECK-NEXT: Type: PT_GNU_STACK
diff --git a/test/ELF/basic-mips.s b/test/ELF/basic-mips.s
index c598c7b5f2f7..dc640edae9f0 100644
--- a/test/ELF/basic-mips.s
+++ b/test/ELF/basic-mips.s
@@ -27,18 +27,18 @@ __start:
# CHECK-NEXT: Version: 1
# CHECK-NEXT: Entry: 0x20000
# CHECK-NEXT: ProgramHeaderOffset: 0x34
-# CHECK-NEXT: SectionHeaderOffset: 0x30088
+# CHECK-NEXT: SectionHeaderOffset: 0x30098
# CHECK-NEXT: Flags [
# CHECK-NEXT: EF_MIPS_ABI_O32
-# CHECK-NEXT: EF_MIPS_ARCH_32R2
+# CHECK-NEXT: EF_MIPS_ARCH_32
# CHECK-NEXT: EF_MIPS_CPIC
# CHECK-NEXT: ]
# CHECK-NEXT: HeaderSize: 52
# CHECK-NEXT: ProgramHeaderEntrySize: 32
# CHECK-NEXT: ProgramHeaderCount: 6
# CHECK-NEXT: SectionHeaderEntrySize: 40
-# CHECK-NEXT: SectionHeaderCount: 10
-# CHECK-NEXT: StringTableSectionIndex: 8
+# CHECK-NEXT: SectionHeaderCount: 11
+# CHECK-NEXT: StringTableSectionIndex: 9
# CHECK-NEXT: }
# CHECK-NEXT: Sections [
# CHECK-NEXT: Section {
@@ -57,23 +57,23 @@ __start:
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 1
-# CHECK-NEXT: Name: .reginfo
-# CHECK-NEXT: Type: SHT_MIPS_REGINFO (0x70000006)
+# CHECK-NEXT: Name: .MIPS.abiflags
+# CHECK-NEXT: Type: SHT_MIPS_ABIFLAGS (0x7000002A)
# CHECK-NEXT: Flags [ (0x2)
# CHECK-NEXT: SHF_ALLOC (0x2)
# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x100F4
-# CHECK-NEXT: Offset: 0xF4
+# CHECK-NEXT: Address: 0x100F8
+# CHECK-NEXT: Offset: 0xF8
# CHECK-NEXT: Size: 24
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
-# CHECK-NEXT: AddressAlignment: 4
+# CHECK-NEXT: AddressAlignment: 8
# CHECK-NEXT: EntrySize: 24
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 2
-# CHECK-NEXT: Name: .MIPS.abiflags
-# CHECK-NEXT: Type: SHT_MIPS_ABIFLAGS (0x7000002A)
+# CHECK-NEXT: Name: .reginfo
+# CHECK-NEXT: Type: SHT_MIPS_REGINFO (0x70000006)
# CHECK-NEXT: Flags [ (0x2)
# CHECK-NEXT: SHF_ALLOC (0x2)
# CHECK-NEXT: ]
@@ -82,12 +82,12 @@ __start:
# CHECK-NEXT: Size: 24
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
-# CHECK-NEXT: AddressAlignment: 8
-# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: AddressAlignment: 4
+# CHECK-NEXT: EntrySize: 24
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 3
-# CHECK-NEXT: Name: .text (25)
+# CHECK-NEXT: Name: .text
# CHECK-NEXT: Type: SHT_PROGBITS (0x1)
# CHECK-NEXT: Flags [ (0x6)
# CHECK-NEXT: SHF_ALLOC (0x2)
@@ -103,7 +103,7 @@ __start:
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 4
-# CHECK-NEXT: Name: .got (31)
+# CHECK-NEXT: Name: .got
# CHECK-NEXT: Type: SHT_PROGBITS (0x1)
# CHECK-NEXT: Flags [ (0x10000003)
# CHECK-NEXT: SHF_ALLOC (0x2)
@@ -152,40 +152,56 @@ __start:
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 7
+# CHECK-NEXT: Name: .comment
+# CHECK-NEXT: Type: SHT_PROGBITS (0x1)
+# CHECK-NEXT: Flags [ (0x30)
+# CHECK-NEXT: SHF_MERGE (0x10)
+# CHECK-NEXT: SHF_STRINGS (0x20)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x0
+# CHECK-NEXT: Offset: 0x30000
+# CHECK-NEXT: Size: 8
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 1
+# CHECK-NEXT: EntrySize: 1
+# CHECK-NEXT: }
+# CHECK-NEXT: Section {
+# CHECK-NEXT: Index: 8
# CHECK-NEXT: Name: .symtab
# CHECK-NEXT: Type: SHT_SYMTAB (0x2)
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x30000
+# CHECK-NEXT: Offset: 0x30008
# CHECK-NEXT: Size: 48
-# CHECK-NEXT: Link: 9
+# CHECK-NEXT: Link: 10
# CHECK-NEXT: Info: 1
# CHECK-NEXT: AddressAlignment: 4
# CHECK-NEXT: EntrySize: 16
# CHECK-NEXT: }
# CHECK-NEXT: Section {
-# CHECK-NEXT: Index: 8
+# CHECK-NEXT: Index: 9
# CHECK-NEXT: Name: .shstrtab
# CHECK-NEXT: Type: SHT_STRTAB (0x3)
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x30030
-# CHECK-NEXT: Size: 73
+# CHECK-NEXT: Offset: 0x30038
+# CHECK-NEXT: Size: 82
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
# CHECK-NEXT: AddressAlignment: 1
# CHECK-NEXT: EntrySize: 0
# CHECK-NEXT: }
# CHECK-NEXT: Section {
-# CHECK-NEXT: Index: 9
+# CHECK-NEXT: Index: 10
# CHECK-NEXT: Name: .strtab
# CHECK-NEXT: Type: SHT_STRTAB (0x3)
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x30079
+# CHECK-NEXT: Offset: 0x3008A
# CHECK-NEXT: Size: 13
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
@@ -212,7 +228,7 @@ __start:
# CHECK-NEXT: Other [ (0x2)
# CHECK-NEXT: STV_HIDDEN (0x2)
# CHECK-NEXT: ]
-# CHECK-NEXT: Section: .got
+# CHECK-NEXT: Section: Absolute
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
# CHECK-NEXT: Name: __start
diff --git a/test/ELF/basic-ppc.s b/test/ELF/basic-ppc.s
index 83ace9a4983d..c0f28bd48c41 100644
--- a/test/ELF/basic-ppc.s
+++ b/test/ELF/basic-ppc.s
@@ -26,17 +26,17 @@
// CHECK-NEXT: Type: SharedObject (0x3)
// CHECK-NEXT: Machine: EM_PPC (0x14)
// CHECK-NEXT: Version: 1
-// CHECK-NEXT: Entry: 0x0
+// CHECK-NEXT: Entry: 0x1000
// CHECK-NEXT: ProgramHeaderOffset: 0x34
-// CHECK-NEXT: SectionHeaderOffset: 0x209C
+// CHECK-NEXT: SectionHeaderOffset: 0x20AC
// CHECK-NEXT: Flags [ (0x0)
// CHECK-NEXT: ]
// CHECK-NEXT: HeaderSize: 52
// CHECK-NEXT: ProgramHeaderEntrySize: 32
// CHECK-NEXT: ProgramHeaderCount: 7
// CHECK-NEXT: SectionHeaderEntrySize: 40
-// CHECK-NEXT: SectionHeaderCount: 9
-// CHECK-NEXT: StringTableSectionIndex: 7
+// CHECK-NEXT: SectionHeaderCount: 10
+// CHECK-NEXT: StringTableSectionIndex: 8
// CHECK-NEXT: }
// CHECK-NEXT: Sections [
// CHECK-NEXT: Section {
@@ -57,7 +57,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Section {
// CHECK-NEXT: Index: 1
-// CHECK-NEXT: Name: .dynsym (1)
+// CHECK-NEXT: Name: .dynsym
// CHECK-NEXT: Type: SHT_DYNSYM (0xB)
// CHECK-NEXT: Flags [ (0x2)
// CHECK-NEXT: SHF_ALLOC (0x2)
@@ -75,7 +75,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Section {
// CHECK-NEXT: Index: 2
-// CHECK-NEXT: Name: .hash (9)
+// CHECK-NEXT: Name: .hash
// CHECK-NEXT: Type: SHT_HASH (0x5)
// CHECK-NEXT: Flags [ (0x2)
// CHECK-NEXT: SHF_ALLOC (0x2)
@@ -93,7 +93,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Section {
// CHECK-NEXT: Index: 3
-// CHECK-NEXT: Name: .dynstr (15)
+// CHECK-NEXT: Name: .dynstr
// CHECK-NEXT: Type: SHT_STRTAB (0x3)
// CHECK-NEXT: Flags [ (0x2)
// CHECK-NEXT: SHF_ALLOC (0x2)
@@ -111,7 +111,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Section {
// CHECK-NEXT: Index: 4
-// CHECK-NEXT: Name: .text (23)
+// CHECK-NEXT: Name: .text
// CHECK-NEXT: Type: SHT_PROGBITS (0x1)
// CHECK-NEXT: Flags [ (0x6)
// CHECK-NEXT: SHF_ALLOC (0x2)
@@ -130,7 +130,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Section {
// CHECK-NEXT: Index: 5
-// CHECK-NEXT: Name: .dynamic (29)
+// CHECK-NEXT: Name: .dynamic
// CHECK-NEXT: Type: SHT_DYNAMIC (0x6)
// CHECK-NEXT: Flags [ (0x3)
// CHECK-NEXT: SHF_ALLOC (0x2)
@@ -151,14 +151,33 @@
// CHECK-NEXT: }
// CHECK-NEXT: Section {
// CHECK-NEXT: Index: 6
-// CHECK-NEXT: Name: .symtab (38)
+// CHECK-NEXT: Name: .comment
+// CHECK-NEXT: Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT: Flags [ (0x30)
+// CHECK-NEXT: SHF_MERGE (0x10)
+// CHECK-NEXT: SHF_STRINGS (0x20)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x0
+// CHECK-NEXT: Offset: 0x2030
+// CHECK-NEXT: Size: 8
+// CHECK-NEXT: Link: 0
+// CHECK-NEXT: Info: 0
+// CHECK-NEXT: AddressAlignment: 1
+// CHECK-NEXT: EntrySize: 1
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: 0000: 4C4C4420 312E3000 |LLD 1.0.|
+// CHECK-NEXT: )
+// CHECK-NEXT: }
+// CHECK-NEXT: Section {
+// CHECK-NEXT: Index: 7
+// CHECK-NEXT: Name: .symtab
// CHECK-NEXT: Type: SHT_SYMTAB (0x2)
// CHECK-NEXT: Flags [ (0x0)
// CHECK-NEXT: ]
// CHECK-NEXT: Address: 0x0
-// CHECK-NEXT: Offset: 0x2030
+// CHECK-NEXT: Offset: 0x2038
// CHECK-NEXT: Size: 32
-// CHECK-NEXT: Link: 8
+// CHECK-NEXT: Link: 9
// CHECK-NEXT: Info: 1
// CHECK-NEXT: AddressAlignment: 4
// CHECK-NEXT: EntrySize: 16
@@ -168,14 +187,14 @@
// CHECK-NEXT: )
// CHECK-NEXT: }
// CHECK-NEXT: Section {
-// CHECK-NEXT: Index: 7
-// CHECK-NEXT: Name: .shstrtab (46)
+// CHECK-NEXT: Index: 8
+// CHECK-NEXT: Name: .shstrtab
// CHECK-NEXT: Type: SHT_STRTAB (0x3)
// CHECK-NEXT: Flags [ (0x0)
// CHECK-NEXT: ]
// CHECK-NEXT: Address: 0x0
-// CHECK-NEXT: Offset: 0x2050
-// CHECK-NEXT: Size: 64
+// CHECK-NEXT: Offset: 0x2058
+// CHECK-NEXT: Size: 73
// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 1
@@ -183,18 +202,19 @@
// CHECK-NEXT: SectionData (
// CHECK-NEXT: 0000: 002E6479 6E73796D 002E6861 7368002E |..dynsym..hash..|
// CHECK-NEXT: 0010: 64796E73 7472002E 74657874 002E6479 |dynstr..text..dy|
-// CHECK-NEXT: 0020: 6E616D69 63002E73 796D7461 62002E73 |namic..symtab..s|
-// CHECK-NEXT: 0030: 68737472 74616200 2E737472 74616200 |hstrtab..strtab.|
+// CHECK-NEXT: 0020: 6E616D69 63002E63 6F6D6D65 6E74002E |namic..comment..|
+// CHECK-NEXT: 0030: 73796D74 6162002E 73687374 72746162 |symtab..shstrtab|
+// CHECK-NEXT: 0040: 002E7374 72746162 00 |..strtab.|
// CHECK-NEXT: )
// CHECK-NEXT: }
// CHECK-NEXT: Section {
-// CHECK-NEXT: Index: 8
-// CHECK-NEXT: Name: .strtab (56)
+// CHECK-NEXT: Index: 9
+// CHECK-NEXT: Name: .strtab
// CHECK-NEXT: Type: SHT_STRTAB (0x3)
// CHECK-NEXT: Flags [ (0x0)
// CHECK-NEXT: ]
// CHECK-NEXT: Address: 0x0
-// CHECK-NEXT: Offset: 0x2090
+// CHECK-NEXT: Offset: 0x20A1
// CHECK-NEXT: Size: 1
// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
diff --git a/test/ELF/basic.s b/test/ELF/basic.s
index 28449331d3fa..e284f9bd40d2 100644
--- a/test/ELF/basic.s
+++ b/test/ELF/basic.s
@@ -27,15 +27,15 @@ _start:
# CHECK-NEXT: Version: 1
# CHECK-NEXT: Entry: [[ENTRY:0x[0-9A-F]+]]
# CHECK-NEXT: ProgramHeaderOffset: 0x40
-# CHECK-NEXT: SectionHeaderOffset: 0x1070
+# CHECK-NEXT: SectionHeaderOffset: 0x1080
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: HeaderSize: 64
# CHECK-NEXT: ProgramHeaderEntrySize: 56
# CHECK-NEXT: ProgramHeaderCount: 4
# CHECK-NEXT: SectionHeaderEntrySize: 64
-# CHECK-NEXT: SectionHeaderCount: 5
-# CHECK-NEXT: StringTableSectionIndex: 3
+# CHECK-NEXT: SectionHeaderCount: 6
+# CHECK-NEXT: StringTableSectionIndex: 4
# CHECK-NEXT: }
# CHECK-NEXT: Sections [
# CHECK-NEXT: Section {
@@ -60,7 +60,7 @@ _start:
# CHECK-NEXT: SHF_ALLOC (0x2)
# CHECK-NEXT: SHF_EXECINSTR (0x4)
# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x11000
+# CHECK-NEXT: Address: 0x201000
# CHECK-NEXT: Offset: 0x1000
# CHECK-NEXT: Size: 16
# CHECK-NEXT: Link: 0
@@ -70,40 +70,56 @@ _start:
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 2
+# CHECK-NEXT: Name: .comment
+# CHECK-NEXT: Type: SHT_PROGBITS (0x1)
+# CHECK-NEXT: Flags [ (0x30)
+# CHECK-NEXT: SHF_MERGE (0x10)
+# CHECK-NEXT: SHF_STRINGS (0x20)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x0
+# CHECK-NEXT: Offset: 0x1010
+# CHECK-NEXT: Size: 8
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 1
+# CHECK-NEXT: EntrySize: 1
+# CHECK-NEXT: }
+# CHECK-NEXT: Section {
+# CHECK-NEXT: Index: 3
# CHECK-NEXT: Name: .symtab
# CHECK-NEXT: Type: SHT_SYMTAB (0x2)
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x1010
+# CHECK-NEXT: Offset: 0x1018
# CHECK-NEXT: Size: 48
-# CHECK-NEXT: Link: 4
+# CHECK-NEXT: Link: 5
# CHECK-NEXT: Info: 1
# CHECK-NEXT: AddressAlignment: 8
# CHECK-NEXT: EntrySize: 24
# CHECK-NEXT: }
# CHECK-NEXT: Section {
-# CHECK-NEXT: Index: 3
+# CHECK-NEXT: Index: 4
# CHECK-NEXT: Name: .shstrtab
# CHECK-NEXT: Type: SHT_STRTAB (0x3)
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x1040
-# CHECK-NEXT: Size: 33
+# CHECK-NEXT: Offset: 0x1048
+# CHECK-NEXT: Size: 42
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
# CHECK-NEXT: AddressAlignment: 1
# CHECK-NEXT: EntrySize: 0
# CHECK-NEXT: }
# CHECK-NEXT: Section {
-# CHECK-NEXT: Index: 4
-# CHECK-NEXT: Name: .strtab (25)
+# CHECK-NEXT: Index: 5
+# CHECK-NEXT: Name: .strtab
# CHECK-NEXT: Type: SHT_STRTAB (0x3)
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x1061
+# CHECK-NEXT: Offset: 0x1072
# CHECK-NEXT: Size: 8
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
@@ -135,8 +151,8 @@ _start:
# CHECK-NEXT: ProgramHeader {
# CHECK-NEXT: Type: PT_PHDR (0x6)
# CHECK-NEXT: Offset: 0x40
-# CHECK-NEXT: VirtualAddress: 0x10040
-# CHECK-NEXT: PhysicalAddress: 0x10040
+# CHECK-NEXT: VirtualAddress: 0x200040
+# CHECK-NEXT: PhysicalAddress: 0x200040
# CHECK-NEXT: FileSize: 224
# CHECK-NEXT: MemSize: 224
# CHECK-NEXT: Flags [ (0x4)
@@ -147,8 +163,8 @@ _start:
# CHECK-NEXT: ProgramHeader {
# CHECK-NEXT: Type: PT_LOAD (0x1)
# CHECK-NEXT: Offset: 0x0
-# CHECK-NEXT: VirtualAddress: 0x10000
-# CHECK-NEXT: PhysicalAddress: 0x10000
+# CHECK-NEXT: VirtualAddress: 0x200000
+# CHECK-NEXT: PhysicalAddress: 0x200000
# CHECK-NEXT: FileSize: 288
# CHECK-NEXT: MemSize: 288
# CHECK-NEXT: Flags [
@@ -159,8 +175,8 @@ _start:
# CHECK-NEXT: ProgramHeader {
# CHECK-NEXT: Type: PT_LOAD (0x1)
# CHECK-NEXT: Offset: 0x1000
-# CHECK-NEXT: VirtualAddress: 0x11000
-# CHECK-NEXT: PhysicalAddress: 0x11000
+# CHECK-NEXT: VirtualAddress: 0x201000
+# CHECK-NEXT: PhysicalAddress: 0x201000
# CHECK-NEXT: FileSize: 16
# CHECK-NEXT: MemSize: 16
# CHECK-NEXT: Flags [ (0x5)
@@ -207,24 +223,28 @@ _start:
# RUN: not ld.lld -o %t2 2>&1 | \
# RUN: FileCheck --check-prefix=NO_INPUT %s
-# NO_INPUT: no input files.
+# NO_INPUT: ld.lld{{.*}}: no input files
# RUN: not ld.lld %t.no.such.file -o %t2 2>&1 | \
# RUN: FileCheck --check-prefix=CANNOT_OPEN %s
# CANNOT_OPEN: cannot open {{.*}}.no.such.file: {{[Nn]}}o such file or directory
# RUN: not ld.lld %t -o 2>&1 | FileCheck --check-prefix=NO_O_VAL %s
-# NO_O_VAL: missing arg value for "-o", expected 1 argument.
+# NO_O_VAL: -o: missing argument
# RUN: not ld.lld --foo 2>&1 | FileCheck --check-prefix=UNKNOWN %s
# UNKNOWN: unknown argument: --foo
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
# RUN: not ld.lld %t %t -o %t2 2>&1 | FileCheck --check-prefix=DUP %s
-# DUP: duplicate symbol: _start in {{.*}} and {{.*}}
+# DUP: {{.*}}:(.text+0x0): duplicate symbol '_start'
+# DUP: {{.*}}:(.text+0x0): previous definition was here
+
+# RUN: not ld.lld %t -o %t -m wrong_emul_fbsd 2>&1 | FileCheck --check-prefix=UNKNOWN_EMUL %s
+# UNKNOWN_EMUL: unknown emulation: wrong_emul_fbsd
-# RUN: not ld.lld %t -o %t -m wrong_emul 2>&1 | FileCheck --check-prefix=UNKNOWN_EMUL %s
-# UNKNOWN_EMUL: unknown emulation: wrong_emul
+# RUN: not ld.lld %t --lto-partitions=0 2>&1 | FileCheck --check-prefix=NOTHREADS %s
+# NOTHREADS: --lto-partitions: number of threads must be > 0
-# RUN: not ld.lld %t --lto-jobs=0 2>&1 | FileCheck --check-prefix=NOTHREADS %s
-# NOTHREADS: number of threads must be > 0
+# RUN: not ld.lld %t --thinlto-jobs=0 2>&1 | FileCheck --check-prefix=NOTHREADSTHIN %s
+# NOTHREADSTHIN: --thinlto-jobs: number of threads must be > 0
diff --git a/test/ELF/basic32.s b/test/ELF/basic32.s
index 44ebe7a46225..e68ed73e637d 100644
--- a/test/ELF/basic32.s
+++ b/test/ELF/basic32.s
@@ -25,15 +25,15 @@ _start:
# CHECK-NEXT: Version: 1
# CHECK-NEXT: Entry: 0x11000
# CHECK-NEXT: ProgramHeaderOffset: 0x34
-# CHECK-NEXT: SectionHeaderOffset: 0x1058
+# CHECK-NEXT: SectionHeaderOffset: 0x1068
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: HeaderSize: 52
# CHECK-NEXT: ProgramHeaderEntrySize: 32
# CHECK-NEXT: ProgramHeaderCount: 4
# CHECK-NEXT: SectionHeaderEntrySize: 40
-# CHECK-NEXT: SectionHeaderCount: 5
-# CHECK-NEXT: StringTableSectionIndex: 3
+# CHECK-NEXT: SectionHeaderCount: 6
+# CHECK-NEXT: StringTableSectionIndex: 4
# CHECK-NEXT: }
# CHECK-NEXT: Sections [
# CHECK-NEXT: Section {
@@ -68,40 +68,56 @@ _start:
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 2
+# CHECK-NEXT: Name: .comment
+# CHECK-NEXT: Type: SHT_PROGBITS (0x1)
+# CHECK-NEXT: Flags [ (0x30)
+# CHECK-NEXT: SHF_MERGE (0x10)
+# CHECK-NEXT: SHF_STRINGS (0x20)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x0
+# CHECK-NEXT: Offset: 0x100C
+# CHECK-NEXT: Size: 8
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 1
+# CHECK-NEXT: EntrySize: 1
+# CHECK-NEXT: }
+# CHECK-NEXT: Section {
+# CHECK-NEXT: Index: 3
# CHECK-NEXT: Name: .symtab
# CHECK-NEXT: Type: SHT_SYMTAB
# CHECK-NEXT: Flags [
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x100C
+# CHECK-NEXT: Offset: 0x1014
# CHECK-NEXT: Size: 32
-# CHECK-NEXT: Link: 4
+# CHECK-NEXT: Link: 5
# CHECK-NEXT: Info: 1
# CHECK-NEXT: AddressAlignment: 4
# CHECK-NEXT: EntrySize: 16
# CHECK-NEXT: }
# CHECK-NEXT: Section {
-# CHECK-NEXT: Index: 3
+# CHECK-NEXT: Index: 4
# CHECK-NEXT: Name: .shstrtab
# CHECK-NEXT: Type: SHT_STRTAB (0x3)
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x102C
-# CHECK-NEXT: Size: 33
+# CHECK-NEXT: Offset: 0x1034
+# CHECK-NEXT: Size: 42
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
# CHECK-NEXT: AddressAlignment: 1
# CHECK-NEXT: EntrySize: 0
# CHECK-NEXT: }
# CHECK-NEXT: Section {
-# CHECK-NEXT: Index: 4
-# CHECK-NEXT: Name: .strtab (25)
+# CHECK-NEXT: Index: 5
+# CHECK-NEXT: Name: .strtab
# CHECK-NEXT: Type: SHT_STRTAB (0x3)
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x104D
+# CHECK-NEXT: Offset: 0x105E
# CHECK-NEXT: Size: 8
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
diff --git a/test/ELF/basic64be.s b/test/ELF/basic64be.s
index fb0bf7bfdb80..1ad982f04867 100644
--- a/test/ELF/basic64be.s
+++ b/test/ELF/basic64be.s
@@ -38,15 +38,15 @@ _start:
# CHECK-NEXT: Version: 1
# CHECK-NEXT: Entry: 0x10020040
# CHECK-NEXT: ProgramHeaderOffset: 0x40
-# CHECK-NEXT: SectionHeaderOffset: 0x200C8
+# CHECK-NEXT: SectionHeaderOffset: 0x200D8
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: HeaderSize: 64
# CHECK-NEXT: ProgramHeaderEntrySize: 56
# CHECK-NEXT: ProgramHeaderCount: 6
# CHECK-NEXT: SectionHeaderEntrySize: 64
-# CHECK-NEXT: SectionHeaderCount: 9
-# CHECK-NEXT: StringTableSectionIndex: 7
+# CHECK-NEXT: SectionHeaderCount: 10
+# CHECK-NEXT: StringTableSectionIndex: 8
# CHECK-NEXT: }
# CHECK-NEXT: Sections [
# CHECK-NEXT: Section {
@@ -67,7 +67,7 @@ _start:
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 1
-# CHECK-NEXT: Name: .text (1)
+# CHECK-NEXT: Name: .text
# CHECK-NEXT: Type: SHT_PROGBITS (0x1)
# CHECK-NEXT: Flags [ (0x6)
# CHECK-NEXT: SHF_ALLOC (0x2)
@@ -163,14 +163,33 @@ _start:
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 6
+# CHECK-NEXT: Name: .comment
+# CHECK-NEXT: Type: SHT_PROGBITS (0x1)
+# CHECK-NEXT: Flags [ (0x30)
+# CHECK-NEXT: SHF_MERGE (0x10)
+# CHECK-NEXT: SHF_STRINGS (0x20)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x0
+# CHECK-NEXT: Offset: 0x20058
+# CHECK-NEXT: Size: 8
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 1
+# CHECK-NEXT: EntrySize: 1
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 4C4C4420 312E3000 |LLD 1.0.|
+# CHECK-NEXT: )
+# CHECK-NEXT: }
+# CHECK-NEXT: Section {
+# CHECK-NEXT: Index: 7
# CHECK-NEXT: Name: .symtab
# CHECK-NEXT: Type: SHT_SYMTAB (0x2)
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x20058
+# CHECK-NEXT: Offset: 0x20060
# CHECK-NEXT: Size: 48
-# CHECK-NEXT: Link: 8
+# CHECK-NEXT: Link: 9
# CHECK-NEXT: Info: 1
# CHECK-NEXT: AddressAlignment: 8
# CHECK-NEXT: EntrySize: 24
@@ -178,14 +197,14 @@ _start:
# CHECK: )
# CHECK-NEXT: }
# CHECK-NEXT: Section {
-# CHECK-NEXT: Index: 7
+# CHECK-NEXT: Index: 8
# CHECK-NEXT: Name: .shstrtab
# CHECK-NEXT: Type: SHT_STRTAB
# CHECK-NEXT: Flags [
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x20088
-# CHECK-NEXT: Size: 54
+# CHECK-NEXT: Offset: 0x20090
+# CHECK-NEXT: Size: 63
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
# CHECK-NEXT: AddressAlignment: 1
@@ -194,13 +213,13 @@ _start:
# CHECK: )
# CHECK-NEXT: }
# CHECK-NEXT: Section {
-# CHECK-NEXT: Index: 8
+# CHECK-NEXT: Index: 9
# CHECK-NEXT: Name: .strtab
# CHECK-NEXT: Type: SHT_STRTAB
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x200BE
+# CHECK-NEXT: Offset: 0x200CF
# CHECK-NEXT: Size: 8
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
diff --git a/test/ELF/bss.s b/test/ELF/bss.s
index d0b43ec5a262..abd7f2e564d7 100644
--- a/test/ELF/bss.s
+++ b/test/ELF/bss.s
@@ -11,14 +11,24 @@
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x11000
-// CHECK-NEXT: Offset: 0x1000
+// CHECK-NEXT: Address:
+// CHECK-NEXT: Offset: 0x[[OFFSET:.*]]
// CHECK-NEXT: Size: 4
// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment:
// CHECK-NEXT: EntrySize: 0
// CHECK-NEXT: }
+// CHECK-NEXT: Section {
+// CHECK-NEXT: Index:
+// CHECK-NEXT: Name:
+// CHECK-NEXT: Type:
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_MERGE
+// CHECK-NEXT: SHF_STRINGS
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address:
+// CHECK-NEXT: Offset: 0x[[OFFSET]]
.global _start
_start:
diff --git a/test/ELF/bsymbolic-undef.s b/test/ELF/bsymbolic-undef.s
index 6590bbcb50b7..19bb3162fd95 100644
--- a/test/ELF/bsymbolic-undef.s
+++ b/test/ELF/bsymbolic-undef.s
@@ -4,7 +4,7 @@
# CHECK: DynamicSymbols [
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: @ (0)
+# CHECK-NEXT: Name: @
# CHECK-NEXT: Value: 0x0
# CHECK-NEXT: Size: 0
# CHECK-NEXT: Binding: Local (0x0)
@@ -13,7 +13,7 @@
# CHECK-NEXT: Section: Undefined (0x0)
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: undef@ (1)
+# CHECK-NEXT: Name: undef@
# CHECK-NEXT: Value: 0x0
# CHECK-NEXT: Size: 0
# CHECK-NEXT: Binding: Global (0x1)
diff --git a/test/ELF/bsymbolic.s b/test/ELF/bsymbolic.s
index d713588d5952..5a089d55492d 100644
--- a/test/ELF/bsymbolic.s
+++ b/test/ELF/bsymbolic.s
@@ -25,7 +25,7 @@ nop
.globl do
.type do,@function
-do:
+do:
callq foo@PLT
callq bar@PLT
diff --git a/test/ELF/build-id.s b/test/ELF/build-id.s
index f1eac42812b4..51758d9eb24a 100644
--- a/test/ELF/build-id.s
+++ b/test/ELF/build-id.s
@@ -1,16 +1,36 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: ld.lld --build-id %t -o %t2
+
+# RUN: ld.lld --build-id %t -o %t2 -threads
+# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=DEFAULT %s
+# RUN: ld.lld --build-id %t -o %t2 -no-threads
# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=DEFAULT %s
-# RUN: ld.lld --build-id=md5 %t -o %t2
+
+# RUN: ld.lld --build-id=md5 %t -o %t2 -threads
+# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=MD5 %s
+# RUN: ld.lld --build-id=md5 %t -o %t2 -no-threads
# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=MD5 %s
-# RUN: ld.lld --build-id=sha1 %t -o %t2
+
+# RUN: ld.lld --build-id=sha1 %t -o %t2 -threads
+# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=SHA1 %s
+# RUN: ld.lld --build-id=sha1 %t -o %t2 -no-threads
+# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=SHA1 %s
+
+# RUN: ld.lld --build-id=tree %t -o %t2 -threads
# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=SHA1 %s
+# RUN: ld.lld --build-id=tree %t -o %t2 -no-threads
+# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=SHA1 %s
+
+# RUN: ld.lld --build-id=uuid %t -o %t2
+# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=UUID %s
+
# RUN: ld.lld --build-id=0x12345678 %t -o %t2
# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=HEX %s
+
# RUN: ld.lld %t -o %t2
# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=NONE %s
+
# RUN: ld.lld --build-id=md5 --build-id=none %t -o %t2
# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=NONE %s
@@ -21,15 +41,21 @@ _start:
.section .note.test, "a", @note
.quad 42
+# DEFAULT: Contents of section .note.test:
# DEFAULT: Contents of section .note.gnu.build-id:
# DEFAULT-NEXT: 04000000 08000000 03000000 474e5500 ............GNU.
-# DEFAULT: Contents of section .note.test:
+# DEFAULT-NEXT: d08dafb4 e6294b62
# MD5: Contents of section .note.gnu.build-id:
# MD5-NEXT: 04000000 10000000 03000000 474e5500 ............GNU.
+# MD5-NEXT: 37
# SHA1: Contents of section .note.gnu.build-id:
# SHA1-NEXT: 04000000 14000000 03000000 474e5500 ............GNU.
+# SHA1-NEXT: 7a4f4eaf 69ceb948 4a7d6e51 2225e87c
+
+# UUID: Contents of section .note.gnu.build-id:
+# UUID-NEXT: 04000000 10000000 03000000 474e5500 ............GNU.
# HEX: Contents of section .note.gnu.build-id:
# HEX-NEXT: 04000000 04000000 03000000 474e5500 ............GNU.
diff --git a/test/ELF/color-diagnostics.test b/test/ELF/color-diagnostics.test
new file mode 100644
index 000000000000..074bba29c54a
--- /dev/null
+++ b/test/ELF/color-diagnostics.test
@@ -0,0 +1,18 @@
+# Windows command prompt doesn't support ANSI escape sequences.
+# REQUIRES: shell
+
+# RUN: not ld.lld -xyz -color-diagnostics /nosuchfile 2>&1 \
+# RUN: | FileCheck -check-prefix=COLOR %s
+# RUN: not ld.lld -xyz -color-diagnostics=always /nosuchfile 2>&1 \
+# RUN: | FileCheck -check-prefix=COLOR %s
+
+# COLOR: {{ld.lld: .\[0;1;31merror: .\[0munknown argument: -xyz}}
+# COLOR: {{ld.lld: .\[0;1;31merror: .\[0mcannot open /nosuchfile}}
+
+# RUN: not ld.lld /nosuchfile 2>&1 | FileCheck -check-prefix=NOCOLOR %s
+# RUN: not ld.lld -color-diagnostics=never /nosuchfile 2>&1 \
+# RUN: | FileCheck -check-prefix=NOCOLOR %s
+# RUN: not ld.lld -color-diagnostics=always -no-color-diagnostics \
+# RUN: /nosuchfile 2>&1 | FileCheck -check-prefix=NOCOLOR %s
+
+# NOCOLOR: ld.lld: error: cannot open /nosuchfile
diff --git a/test/ELF/comdat.s b/test/ELF/comdat.s
index d422ee8fba33..5b190b177ee0 100644
--- a/test/ELF/comdat.s
+++ b/test/ELF/comdat.s
@@ -5,6 +5,15 @@
// RUN: llvm-readobj -s -t %t | FileCheck --check-prefix=READ %s
// REQUIRES: x86
+// Check that we don't crash with --gc-section and that we print a list of
+// reclaimed sections on stderr.
+// RUN: ld.lld --gc-sections --print-gc-sections -shared %t.o %t.o %t2.o -o %t \
+// RUN: 2>&1 | FileCheck --check-prefix=GC %s
+// GC: removing unused section from '.text' in file
+// GC: removing unused section from '.text3' in file
+// GC: removing unused section from '.text' in file
+// GC: removing unused section from '.text' in file
+
.section .text2,"axG",@progbits,foo,comdat,unique,0
foo:
nop
diff --git a/test/ELF/comment-gc.s b/test/ELF/comment-gc.s
new file mode 100644
index 000000000000..8018ff89bbf9
--- /dev/null
+++ b/test/ELF/comment-gc.s
@@ -0,0 +1,15 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/comment-gc.s -o %t2.o
+# RUN: ld.lld %t.o %t2.o -o %t1 --gc-sections -shared
+# RUN: llvm-objdump -s %t1 | FileCheck %s
+
+# CHECK: Contents of section .comment:
+# CHECK-NEXT: 0000 00666f6f 00626172 004c4c44 20312e30 .foo.bar.LLD 1.0
+# CHECK-NEXT: 0010 00 .
+
+.ident "foo"
+
+.globl _start
+_start:
+ nop
diff --git a/test/ELF/common.s b/test/ELF/common.s
index f16f948fe64e..c8011a0a5088 100644
--- a/test/ELF/common.s
+++ b/test/ELF/common.s
@@ -10,7 +10,7 @@
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x11000
+// CHECK-NEXT: Address: 0x201000
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 22
// CHECK-NEXT: Link: 0
@@ -18,7 +18,7 @@
// CHECK-NEXT: AddressAlignment: 16
// CHECK: Name: sym1
-// CHECK-NEXT: Value: 0x11004
+// CHECK-NEXT: Value: 0x201004
// CHECK-NEXT: Size: 8
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: Object
@@ -26,7 +26,7 @@
// CHECK-NEXT: Section: .bss
// CHECK: Name: sym2
-// CHECK-NEXT: Value: 0x1100C
+// CHECK-NEXT: Value: 0x20100C
// CHECK-NEXT: Size: 8
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: Object
@@ -34,7 +34,7 @@
// CHECK-NEXT: Section: .bss
// CHECK: Name: sym3
-// CHECK-NEXT: Value: 0x11014
+// CHECK-NEXT: Value: 0x201014
// CHECK-NEXT: Size: 2
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: Object
@@ -42,7 +42,7 @@
// CHECK-NEXT: Section: .bss
// CHECK: Name: sym4
-// CHECK-NEXT: Value: 0x11000
+// CHECK-NEXT: Value: 0x201000
// CHECK-NEXT: Size: 4
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: Object
diff --git a/test/ELF/compressed-debug-input.s b/test/ELF/compressed-debug-input.s
index 7339833dbb7f..5104a102ba30 100644
--- a/test/ELF/compressed-debug-input.s
+++ b/test/ELF/compressed-debug-input.s
@@ -1,52 +1,73 @@
# REQUIRES: zlib
# RUN: llvm-mc -compress-debug-sections=zlib -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: llvm-readobj -sections %t | FileCheck -check-prefix=COMPRESSED %s
+# RUN: llvm-readobj -sections %t | FileCheck -check-prefix=ZLIB %s
+# ZLIB: Section {
+# ZLIB: Index: 2
+# ZLIB: Name: .debug_str
+# ZLIB-NEXT: Type: SHT_PROGBITS
+# ZLIB-NEXT: Flags [
+# ZLIB-NEXT: SHF_COMPRESSED (0x800)
+# ZLIB-NEXT: SHF_MERGE (0x10)
+# ZLIB-NEXT: SHF_STRINGS (0x20)
+# ZLIB-NEXT: ]
+# ZLIB-NEXT: Address:
+# ZLIB-NEXT: Offset:
+# ZLIB-NEXT: Size:
+# ZLIB-NEXT: Link:
+# ZLIB-NEXT: Info:
+# ZLIB-NEXT: AddressAlignment: 1
+# ZLIB-NEXT: EntrySize: 1
+# ZLIB-NEXT: }
-# COMPRESSED: Section {
-# COMPRESSED: Index: 2
-# COMPRESSED: Name: .debug_str
-# COMPRESSED-NEXT: Type: SHT_PROGBITS
-# COMPRESSED-NEXT: Flags [
-# COMPRESSED-NEXT: SHF_COMPRESSED (0x800)
-# COMPRESSED-NEXT: SHF_MERGE (0x10)
-# COMPRESSED-NEXT: SHF_STRINGS (0x20)
-# COMPRESSED-NEXT: ]
-# COMPRESSED-NEXT: Address:
-# COMPRESSED-NEXT: Offset:
-# COMPRESSED-NEXT: Size: 66
-# COMPRESSED-NEXT: Link:
-# COMPRESSED-NEXT: Info:
-# COMPRESSED-NEXT: AddressAlignment: 1
-# COMPRESSED-NEXT: EntrySize: 1
-# COMPRESSED-NEXT: }
+# RUN: llvm-mc -compress-debug-sections=zlib-gnu -filetype=obj -triple=x86_64-unknown-linux %s -o %t2
+# RUN: llvm-readobj -sections %t2 | FileCheck -check-prefix=GNU %s
+# GNU: Section {
+# GNU: Index: 2
+# GNU: Name: .zdebug_str
+# GNU-NEXT: Type: SHT_PROGBITS
+# GNU-NEXT: Flags [
+# GNU-NEXT: SHF_MERGE (0x10)
+# GNU-NEXT: SHF_STRINGS (0x20)
+# GNU-NEXT: ]
+# GNU-NEXT: Address:
+# GNU-NEXT: Offset:
+# GNU-NEXT: Size:
+# GNU-NEXT: Link:
+# GNU-NEXT: Info:
+# GNU-NEXT: AddressAlignment: 1
+# GNU-NEXT: EntrySize: 1
+# GNU-NEXT: }
# RUN: ld.lld %t -o %t.so -shared
-# RUN: llvm-readobj -sections -section-data %t.so | FileCheck -check-prefix=UNCOMPRESSED %s
+# RUN: llvm-readobj -sections -section-data %t.so | FileCheck -check-prefix=DATA %s
-# UNCOMPRESSED: Section {
-# UNCOMPRESSED: Index: 6
-# UNCOMPRESSED: Name: .debug_str
-# UNCOMPRESSED-NEXT: Type: SHT_PROGBITS
-# UNCOMPRESSED-NEXT: Flags [
-# UNCOMPRESSED-NEXT: SHF_MERGE (0x10)
-# UNCOMPRESSED-NEXT: SHF_STRINGS (0x20)
-# UNCOMPRESSED-NEXT: ]
-# UNCOMPRESSED-NEXT: Address: 0x0
-# UNCOMPRESSED-NEXT: Offset: 0x1060
-# UNCOMPRESSED-NEXT: Size: 69
-# UNCOMPRESSED-NEXT: Link: 0
-# UNCOMPRESSED-NEXT: Info: 0
-# UNCOMPRESSED-NEXT: AddressAlignment: 1
-# UNCOMPRESSED-NEXT: EntrySize: 1
-# UNCOMPRESSED-NEXT: SectionData (
-# UNCOMPRESSED-NEXT: 0000: 73686F72 7420756E 7369676E 65642069 |short unsigned i|
-# UNCOMPRESSED-NEXT: 0010: 6E740075 6E736967 6E656420 696E7400 |nt.unsigned int.|
-# UNCOMPRESSED-NEXT: 0020: 6C6F6E67 20756E73 69676E65 6420696E |long unsigned in|
-# UNCOMPRESSED-NEXT: 0030: 74006368 61720075 6E736967 6E656420 |t.char.unsigned |
-# UNCOMPRESSED-NEXT: 0040: 63686172 00 |char.|
-# UNCOMPRESSED-NEXT: )
-# UNCOMPRESSED-NEXT: }
+# RUN: ld.lld %t2 -o %t2.so -shared
+# RUN: llvm-readobj -sections -section-data %t2.so | FileCheck -check-prefix=DATA %s
+
+# DATA: Section {
+# DATA: Index: 6
+# DATA: Name: .debug_str
+# DATA-NEXT: Type: SHT_PROGBITS
+# DATA-NEXT: Flags [
+# DATA-NEXT: SHF_MERGE (0x10)
+# DATA-NEXT: SHF_STRINGS (0x20)
+# DATA-NEXT: ]
+# DATA-NEXT: Address: 0x0
+# DATA-NEXT: Offset: 0x1060
+# DATA-NEXT: Size: 69
+# DATA-NEXT: Link: 0
+# DATA-NEXT: Info: 0
+# DATA-NEXT: AddressAlignment: 1
+# DATA-NEXT: EntrySize: 1
+# DATA-NEXT: SectionData (
+# DATA-NEXT: 0000: 73686F72 7420756E 7369676E 65642069 |short unsigned i|
+# DATA-NEXT: 0010: 6E740075 6E736967 6E656420 696E7400 |nt.unsigned int.|
+# DATA-NEXT: 0020: 6C6F6E67 20756E73 69676E65 6420696E |long unsigned in|
+# DATA-NEXT: 0030: 74006368 61720075 6E736967 6E656420 |t.char.unsigned |
+# DATA-NEXT: 0040: 63686172 00 |char.|
+# DATA-NEXT: )
+# DATA-NEXT: }
.section .debug_str,"MS",@progbits,1
.LASF2:
diff --git a/test/ELF/conflict.s b/test/ELF/conflict.s
index 7598eea3711c..b9bb50515c5c 100644
--- a/test/ELF/conflict.s
+++ b/test/ELF/conflict.s
@@ -1,22 +1,38 @@
-# REQUIRES: x86, shell
+# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
# RUN: not ld.lld %t1.o %t1.o -o %t2 2>&1 | FileCheck -check-prefix=DEMANGLE %s
-# DEMANGLE: duplicate symbol: mul(double, double) in
-# DEMANGLE: duplicate symbol: foo in
+# DEMANGLE: {{.*}}:(.text+0x0): duplicate symbol 'mul(double, double)'
+# DEMANGLE-NEXT: {{.*}}:(.text+0x0): previous definition was here
+# DEMANGLE-NEXT: {{.*}}:(.text+0x0): duplicate symbol 'foo'
+# DEMANGLE-NEXT: {{.*}}:(.text+0x0): previous definition was here
# RUN: not ld.lld %t1.o %t1.o -o %t2 --no-demangle 2>&1 | \
# RUN: FileCheck -check-prefix=NO_DEMANGLE %s
-# NO_DEMANGLE: duplicate symbol: _Z3muldd in
-# NO_DEMANGLE: duplicate symbol: foo in
+# NO_DEMANGLE: {{.*}}:(.text+0x0): duplicate symbol '_Z3muldd'
+# NO_DEMANGLE-NEXT: {{.*}}:(.text+0x0): previous definition was here
+# NO_DEMANGLE-NEXT: {{.*}}:(.text+0x0): duplicate symbol 'foo'
+# NO_DEMANGLE-NEXT: {{.*}}:(.text+0x0): previous definition was here
+
+# RUN: not ld.lld %t1.o %t1.o -o %t2 --demangle --no-demangle 2>&1 | \
+# RUN: FileCheck -check-prefix=NO_DEMANGLE %s
+# RUN: not ld.lld %t1.o %t1.o -o %t2 --no-demangle --demangle 2>&1 | \
+# RUN: FileCheck -check-prefix=DEMANGLE %s
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/conflict.s -o %t2.o
# RUN: llvm-ar rcs %t3.a %t2.o
# RUN: not ld.lld %t1.o %t3.a -u baz -o %t2 2>&1 | FileCheck -check-prefix=ARCHIVE %s
-# ARCHIVE: duplicate symbol: foo in {{.*}}1.o and {{.*}}3.a({{.*}}2.o)
+# ARCHIVE: {{.*}}3.a({{.*}}2.o):(.text+0x0): duplicate symbol 'foo'
+# ARCHIVE-NEXT: {{.*}}1.o:(.text+0x0): previous definition was here
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/conflict-debug.s -o %t-dbg.o
+# RUN: not ld.lld %t-dbg.o %t-dbg.o -o %t-dbg 2>&1 | FileCheck -check-prefix=DBGINFO %s
+
+# DBGINFO: conflict-debug.s:4: duplicate symbol 'zed'
+# DBGINFO-NEXT: conflict-debug.s:4: previous definition was here
.globl _Z3muldd, foo
_Z3muldd:
diff --git a/test/ELF/copy-errors.s b/test/ELF/copy-errors.s
index 9d8b72ddb43b..b0d56c9c6c64 100644
--- a/test/ELF/copy-errors.s
+++ b/test/ELF/copy-errors.s
@@ -9,7 +9,7 @@ _start:
call bar
-// CHECK: cannot preempt symbol
+// CHECK: {{.*}}.o:(.text+0x1): cannot preempt symbol 'bar' defined in {{.*}}.so
call zed
-// CHECK: symbol is missing type
+// CHECK: symbol 'zed' defined in {{.*}}.so is missing type
diff --git a/test/ELF/copy-in-shared.s b/test/ELF/copy-in-shared.s
index 4114d95fb101..b8e57ce9e703 100644
--- a/test/ELF/copy-in-shared.s
+++ b/test/ELF/copy-in-shared.s
@@ -7,4 +7,4 @@
.quad foo
-// CHECK: can't create dynamic relocation R_X86_64_64 against readonly segment
+// CHECK: {{.*}}.o:(.text+0x0): can't create dynamic relocation R_X86_64_64 against symbol 'foo' defined in {{.*}}.so
diff --git a/test/ELF/copy-rel-corrupted.s b/test/ELF/copy-rel-corrupted.s
index a3f72f71c1da..3cdad7cf8037 100644
--- a/test/ELF/copy-rel-corrupted.s
+++ b/test/ELF/copy-rel-corrupted.s
@@ -3,7 +3,7 @@
// RUN: ld.lld %t2.o -o %t2.so -shared
// RUN: not ld.lld %t.o %t2.so -o %t.exe 2>&1 | FileCheck %s
-// CHECK: cannot create a copy relocation for x
+// CHECK: error: cannot create a copy relocation for symbol x
.global _start
_start:
diff --git a/test/ELF/copy-rel-pie-error.s b/test/ELF/copy-rel-pie-error.s
index 1f427023baa5..7cb6700ef9f3 100644
--- a/test/ELF/copy-rel-pie-error.s
+++ b/test/ELF/copy-rel-pie-error.s
@@ -3,8 +3,8 @@
// RUN: ld.lld %t2.o -o %t2.so -shared
// RUN: not ld.lld %t.o %t2.so -o %t.exe -pie 2>&1 | FileCheck %s
-// CHECK: can't create dynamic relocation R_X86_64_64 against readonly segment
-// CHECK: can't create dynamic relocation R_X86_64_64 against readonly segment
+// CHECK: {{.*}}.o:(.text+0x0): can't create dynamic relocation R_X86_64_64 against symbol 'bar' defined in {{.*}}.so
+// CHECK: {{.*}}.o:(.text+0x8): can't create dynamic relocation R_X86_64_64 against symbol 'foo' defined in {{.*}}.so
.global _start
_start:
diff --git a/test/ELF/debug-gc.s b/test/ELF/debug-gc.s
new file mode 100644
index 000000000000..8bcfde16a7b7
--- /dev/null
+++ b/test/ELF/debug-gc.s
@@ -0,0 +1,30 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t1 --gc-sections
+# RUN: llvm-objdump -s %t1 | FileCheck %s
+
+# CHECK: Contents of section .debug_str:
+# CHECK-NEXT: 0000 41414100 42424200 43434300 AAA.BBB.CCC.
+# CHECK: Contents of section .foo:
+# CHECK-NEXT: 0000 2a000000
+# CHECK: Contents of section .debug_info:
+# CHECK-NEXT: 0000 00000000 04000000
+
+.globl _start
+_start:
+
+.section .debug_str,"MS",@progbits,1
+.Linfo_string0:
+ .asciz "AAA"
+.Linfo_string1:
+ .asciz "BBB"
+.Linfo_string2:
+ .asciz "CCC"
+
+.section .foo,"M",@progbits,4
+.p2align 2
+ .long 42
+
+.section .debug_info,"",@progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
diff --git a/test/ELF/defined-tls_get_addr.s b/test/ELF/defined-tls_get_addr.s
new file mode 100644
index 000000000000..509c293cca1d
--- /dev/null
+++ b/test/ELF/defined-tls_get_addr.s
@@ -0,0 +1,10 @@
+// RUN: llvm-mc %s -o %t.o -triple x86_64-pc-linux -filetype=obj
+// RUN: ld.lld %t.o -o %t
+
+// Don't error if __tls_get_addr is defined.
+
+.global _start
+.global __tls_get_addr
+_start:
+__tls_get_addr:
+nop
diff --git a/test/ELF/discard-locals.s b/test/ELF/discard-locals.s
index 073e891c3143..9deaccff11c7 100644
--- a/test/ELF/discard-locals.s
+++ b/test/ELF/discard-locals.s
@@ -40,7 +40,7 @@ _start:
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: _start
-// CHECK-NEXT: Value: 0x11000
+// CHECK-NEXT: Value:
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: None
diff --git a/test/ELF/discard-none.s b/test/ELF/discard-none.s
index e3a7a2032082..89e06fd7b7e4 100644
--- a/test/ELF/discard-none.s
+++ b/test/ELF/discard-none.s
@@ -36,7 +36,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: .Lmyothervar
-// CHECK-NEXT: Value: 0x1000
+// CHECK-NEXT: Value:
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -45,7 +45,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: .Lmyvar
-// CHECK-NEXT: Value: 0x1000
+// CHECK-NEXT: Value:
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
diff --git a/test/ELF/dont-export-hidden.s b/test/ELF/dont-export-hidden.s
index 8819a6e99fdb..8088c8d94d89 100644
--- a/test/ELF/dont-export-hidden.s
+++ b/test/ELF/dont-export-hidden.s
@@ -18,7 +18,7 @@ foo:
// CHECK: DynamicSymbols [
// CHECK-NEXT: Symbol {
-// CHECK-NEXT: Name: @ (0)
+// CHECK-NEXT: Name: @
// CHECK-NEXT: Value: 0x0
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
diff --git a/test/ELF/driver.test b/test/ELF/driver.test
index 95e2100b4abf..80d19cb4d6a8 100644
--- a/test/ELF/driver.test
+++ b/test/ELF/driver.test
@@ -19,6 +19,8 @@
# RUN: ld.lld --version 2>&1 | FileCheck -check-prefix=VERSION %s
# VERSION: LLD
+# RUN: not ld.lld -v 2>&1 | FileCheck -check-prefix=VERSION %s
+
## Attempt to link DSO with -r
# RUN: ld.lld -shared %t -o %t.so
# RUN: not ld.lld -r %t.so %t -o %tfail 2>&1 | FileCheck -check-prefix=ERR %s
@@ -44,6 +46,14 @@
# RUN: not ld.lld -shared -pie %t -o %tfail 2>&1 | FileCheck -check-prefix=ERR6 %s
# ERR6: -shared and -pie may not be used together
+## "--output=foo" is equivalent to "-o foo".
+# RUN: not ld.lld %t --output=/no/such/file 2>&1 | FileCheck -check-prefix=ERR7 %s
+# ERR7: failed to open /no/such/file
+
+## "-output=foo" is equivalent to "-o utput=foo".
+# RUN: not ld.lld %t -output=/no/such/file 2>&1 | FileCheck -check-prefix=ERR8 %s
+# ERR8: failed to open utput=/no/such/file
+
.globl _start
_start:
nop
diff --git a/test/ELF/duplicate-internal.s b/test/ELF/duplicate-internal.s
deleted file mode 100644
index d1ccf5322127..000000000000
--- a/test/ELF/duplicate-internal.s
+++ /dev/null
@@ -1,11 +0,0 @@
-# Should print an expected message in case of conflict with an internally generated symbol.
-
-# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
-# RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-
-# CHECK: duplicate symbol: _gp in {{.*}} and (internal)
-
-# REQUIRES: mips
-
- .globl _gp
-_gp = 0
diff --git a/test/ELF/dynamic-got-rela.s b/test/ELF/dynamic-got-rela.s
new file mode 100644
index 000000000000..0aeb6d477a26
--- /dev/null
+++ b/test/ELF/dynamic-got-rela.s
@@ -0,0 +1,34 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: llvm-readobj -r -s -l -section-data %t.so | FileCheck %s
+
+// CHECK: Name: .got
+// CHECK-NEXT: Type: SHT_PROGBITS
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: SHF_WRITE
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x[[GOT:.*]]
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size:
+// CHECK-NEXT: Link:
+// CHECK-NEXT: Info:
+// CHECK-NEXT: AddressAlignment:
+// CHECK-NEXT: EntrySize:
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: 0000: 00000000 00000000 |
+// CHECK-NEXT: )
+
+// CHECK: Relocations [
+// CHECK-NEXT: Section ({{.*}}) .rela.dyn {
+// CHECK-NEXT: 0x[[GOT]] R_X86_64_RELATIVE - 0x[[ADDEND:.*]]
+// CHECK-NEXT: }
+// CHECK-NEXT: ]
+
+// CHECK: Type: PT_DYNAMIC
+// CHECK-NEXT: Offset: 0x[[ADDEND]]
+// CHECK-NEXT: VirtualAddress: 0x[[ADDEND]]
+// CHECK-NEXT: PhysicalAddress: 0x[[ADDEND]]
+
+cmpq $0, _DYNAMIC@GOTPCREL(%rip)
diff --git a/test/ELF/dynamic-got.s b/test/ELF/dynamic-got.s
new file mode 100644
index 000000000000..c50c9022329a
--- /dev/null
+++ b/test/ELF/dynamic-got.s
@@ -0,0 +1,39 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=i386-pc-linux %s -o %t.o
+// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: llvm-readobj -s -l -section-data -r %t.so | FileCheck %s
+
+// CHECK: Name: .got
+// CHECK-NEXT: Type: SHT_PROGBITS
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: SHF_WRITE
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address:
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size:
+// CHECK-NEXT: Link:
+// CHECK-NEXT: Info:
+// CHECK-NEXT: AddressAlignment:
+// CHECK-NEXT: EntrySize:
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: 0000: 00200000 |
+// CHECK-NEXT: )
+
+// CHECK: Relocations [
+// CHECK-NEXT: Section ({{.*}}) .rel.dyn {
+// CHECK-NEXT: 0x2050 R_386_RELATIVE - 0x0
+// CHECK-NEXT: }
+// CHECK-NEXT: ]
+
+// CHECK: Type: PT_DYNAMIC
+// CHECK-NEXT: Offset: 0x2000
+// CHECK-NEXT: VirtualAddress: 0x2000
+// CHECK-NEXT: PhysicalAddress: 0x2000
+
+ calll .L0$pb
+.L0$pb:
+ popl %eax
+.Ltmp0:
+ addl $_GLOBAL_OFFSET_TABLE_+(.Ltmp0-.L0$pb), %eax
+ movl _DYNAMIC@GOT(%eax), %eax
diff --git a/test/ELF/dynamic-list-extern.s b/test/ELF/dynamic-list-extern.s
new file mode 100644
index 000000000000..dfeb31d4fc07
--- /dev/null
+++ b/test/ELF/dynamic-list-extern.s
@@ -0,0 +1,15 @@
+# REQUIRES: x86
+
+# Test that we can parse multiple externs.
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+
+# RUN: echo '{ \
+# RUN: extern "C" { \
+# RUN: foo; \
+# RUN: }; \
+# RUN: extern "C++" { \
+# RUN: bar; \
+# RUN: }; \
+# RUN: };' > %t.list
+# RUN: ld.lld --dynamic-list %t.list %t.o -shared -o %t.so
diff --git a/test/ELF/dynamic-list.s b/test/ELF/dynamic-list.s
index 5e257b31d1f3..e2472e716b3f 100644
--- a/test/ELF/dynamic-list.s
+++ b/test/ELF/dynamic-list.s
@@ -2,8 +2,6 @@
## implemented in Python, and the Cygwin implementations of the Unix utilities.
## Avoid running these tests on Windows for now by requiring a real shell.
-# REQUIRES: shell
-
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o
@@ -29,7 +27,7 @@
# CHECK: DynamicSymbols [
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: @ (0)
+# CHECK-NEXT: Name: @
# CHECK-NEXT: Value: 0x0
# CHECK-NEXT: Size: 0
# CHECK-NEXT: Binding: Local
@@ -38,15 +36,15 @@
# CHECK-NEXT: Section: Undefined
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: foo1@ (1)
-# CHECK-NEXT: Value: 0x11000
+# CHECK-NEXT: Name: foo1@
+# CHECK-NEXT: Value: 0x201000
# CHECK-NEXT: Size: 0
# CHECK-NEXT: Binding: Global (0x1)
# CHECK-NEXT: Type: None (0x0)
# CHECK-NEXT: Other: 0
# CHECK-NEXT: Section: .text (0x4)
# CHECK-NEXT: }
-# CHECK-NEXT: ]
+# CHECK-NEXT: ]
## Now export all the foo1, foo2, and foo31 symbols
@@ -56,7 +54,7 @@
# CHECK2: DynamicSymbols [
# CHECK2-NEXT: Symbol {
-# CHECK2-NEXT: Name: @ (0)
+# CHECK2-NEXT: Name: @
# CHECK2-NEXT: Value: 0x0
# CHECK2-NEXT: Size: 0
# CHECK2-NEXT: Binding: Local
@@ -65,8 +63,8 @@
# CHECK2-NEXT: Section: Undefined
# CHECK2-NEXT: }
# CHECK2-NEXT: Symbol {
-# CHECK2-NEXT: Name: foo1@ (1)
-# CHECK2-NEXT: Value: 0x11000
+# CHECK2-NEXT: Name: foo1@
+# CHECK2-NEXT: Value: 0x201000
# CHECK2-NEXT: Size: 0
# CHECK2-NEXT: Binding: Global (0x1)
# CHECK2-NEXT: Type: None (0x0)
@@ -74,8 +72,8 @@
# CHECK2-NEXT: Section: .text (0x4)
# CHECK2-NEXT: }
# CHECK2-NEXT: Symbol {
-# CHECK2-NEXT: Name: foo2@ (6)
-# CHECK2-NEXT: Value: 0x11001
+# CHECK2-NEXT: Name: foo2@
+# CHECK2-NEXT: Value: 0x201001
# CHECK2-NEXT: Size: 0
# CHECK2-NEXT: Binding: Global (0x1)
# CHECK2-NEXT: Type: None (0x0)
@@ -83,15 +81,15 @@
# CHECK2-NEXT: Section: .text (0x4)
# CHECK2-NEXT: }
# CHECK2-NEXT: Symbol {
-# CHECK2-NEXT: Name: foo31@ (11)
-# CHECK2-NEXT: Value: 0x11002
+# CHECK2-NEXT: Name: foo31@
+# CHECK2-NEXT: Value: 0x201002
# CHECK2-NEXT: Size: 0
# CHECK2-NEXT: Binding: Global (0x1)
# CHECK2-NEXT: Type: None (0x0)
# CHECK2-NEXT: Other: 0
# CHECK2-NEXT: Section: .text (0x4)
# CHECK2-NEXT: }
-# CHECK2-NEXT: ]
+# CHECK2-NEXT: ]
.globl foo1
foo1:
diff --git a/test/ELF/dynamic-reloc-in-ro.s b/test/ELF/dynamic-reloc-in-ro.s
index 2ef36f695fb6..682dbdd8984e 100644
--- a/test/ELF/dynamic-reloc-in-ro.s
+++ b/test/ELF/dynamic-reloc-in-ro.s
@@ -5,4 +5,4 @@
foo:
.quad foo
-// CHECK: can't create dynamic relocation R_X86_64_64 against readonly segment
+// CHECK: {{.*}}.o:(.text+0x0): can't create dynamic relocation R_X86_64_64 against local symbol in readonly segment defined in {{.*}}.o
diff --git a/test/ELF/dynamic-reloc-index.s b/test/ELF/dynamic-reloc-index.s
index 2d38c014a307..ba3c5791beb0 100644
--- a/test/ELF/dynamic-reloc-index.s
+++ b/test/ELF/dynamic-reloc-index.s
@@ -8,7 +8,7 @@
// CHECK: Relocations [
// CHECK-NEXT: Section ({{.*}}) .rela.plt {
-// CHECK-NEXT: 0x13018 R_X86_64_JUMP_SLOT bar 0x0
+// CHECK-NEXT: 0x203018 R_X86_64_JUMP_SLOT bar 0x0
// CHECK-NEXT: }
// CHECK-NEXT: ]
diff --git a/test/ELF/dynamic-reloc.s b/test/ELF/dynamic-reloc.s
index 5e23ba93d5a9..07894cffc90d 100644
--- a/test/ELF/dynamic-reloc.s
+++ b/test/ELF/dynamic-reloc.s
@@ -28,12 +28,12 @@
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_EXECINSTR
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x11000
+// CHECK-NEXT: Address: 0x201000
// CHECK: Relocations [
// CHECK-NEXT: Section ({{.*}}) .rela.plt {
// CHECK-NEXT: Relocation {
-// CHECK-NEXT: Offset: 0x13018
+// CHECK-NEXT: Offset: 0x203018
// CHECK-NEXT: Type: R_X86_64_JUMP_SLOT
// CHECK-NEXT: Symbol: bar
// CHECK-NEXT: Addend: 0x0
@@ -44,6 +44,7 @@
// CHECK: DynamicSection [
// CHECK-NEXT: Tag Type Name/Value
// CHECK-NEXT: 0x0000000000000001 NEEDED SharedLibrary ({{.*}}2.so)
+// CHECK-NEXT: 0x0000000000000015 DEBUG 0x0
// CHECK-NEXT: 0x0000000000000017 JMPREL
// CHECK-NEXT: 0x0000000000000002 PLTRELSZ 24 (bytes)
// CHECK-NEXT: 0x0000000000000003 PLTGOT
@@ -53,7 +54,6 @@
// CHECK-NEXT: 0x0000000000000005 STRTAB
// CHECK-NEXT: 0x000000000000000A STRSZ
// CHECK-NEXT: 0x0000000000000004 HASH
-// CHECK-NEXT: 0x0000000000000015 DEBUG 0x0
// CHECK-NEXT: 0x0000000000000000 NULL 0x0
// CHECK-NEXT: ]
diff --git a/test/ELF/dynamic.s b/test/ELF/dynamic.s
index 2efd2990c52c..0ed7abc146be 100644
--- a/test/ELF/dynamic.s
+++ b/test/ELF/dynamic.s
@@ -34,8 +34,8 @@
# CHECK-NEXT: Section: .dynamic
# CHECK-NEXT: }
-# RUN: ld.lld %t.o -o %t.o
-# RUN: llvm-readobj -sections -symbols %t.o | FileCheck -check-prefix=NODYN %s
+# RUN: ld.lld %t.o -o %t2
+# RUN: llvm-readobj -sections -symbols %t2 | FileCheck -check-prefix=NODYN %s
# NODYN: Symbols [
# NODYN-NOT: Name: _DYNAMIC
# NODYN: ]
diff --git a/test/ELF/edata-etext.s b/test/ELF/edata-etext.s
index e0538d690d7c..7723adf94e23 100644
--- a/test/ELF/edata-etext.s
+++ b/test/ELF/edata-etext.s
@@ -1,7 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
# RUN: ld.lld %t.o -o %t
-# RUN: llvm-readobj -sections -symbols %t | FileCheck %s
+# RUN: llvm-objdump -t -section-headers %t | FileCheck %s
## This checks that:
## 1) Address of _etext is the first location after the last read-only loadable segment.
@@ -10,101 +10,24 @@
## greater than the address of _etext, the address of _end is same as the address
## of _edata." (https://docs.oracle.com/cd/E53394_01/html/E54766/u-etext-3c.html).
## 3) Address of _end is different from _edata because of 2.
-# CHECK: Section {
-# CHECK: Index: 1
-# CHECK: Name: .text
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: SHF_EXECINSTR
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x11000
-# CHECK-NEXT: Offset: 0x1000
-# CHECK-NEXT: Size: 1
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize: 0
-# CHECK-NEXT: }
-# CHECK-NEXT: Section {
-# CHECK-NEXT: Index: 2
-# CHECK-NEXT: Name: .data
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: SHF_WRITE
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x12000
-# CHECK-NEXT: Offset: 0x2000
-# CHECK-NEXT: Size: 2
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize:
-# CHECK-NEXT: }
-# CHECK-NEXT: Section {
-# CHECK-NEXT: Index: 3
-# CHECK-NEXT: Name: .bss
-# CHECK-NEXT: Type: SHT_NOBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: SHF_WRITE
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x12004
-# CHECK-NEXT: Offset: 0x2002
-# CHECK-NEXT: Size: 6
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize:
-# CHECK-NEXT: }
-# CHECK: Symbols [
-# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name:
-# CHECK-NEXT: Value: 0x0
-# CHECK-NEXT: Size: 0
-# CHECK-NEXT: Binding: Local
-# CHECK-NEXT: Type: None
-# CHECK-NEXT: Other: 0
-# CHECK-NEXT: Section: Undefined
-# CHECK-NEXT: }
-# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: _start
-# CHECK-NEXT: Value: 0x11000
-# CHECK-NEXT: Size: 0
-# CHECK-NEXT: Binding: Global
-# CHECK-NEXT: Type: None
-# CHECK-NEXT: Other: 0
-# CHECK-NEXT: Section: .text
-# CHECK-NEXT: }
-# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: _edata
-# CHECK-NEXT: Value: 0x12002
-# CHECK-NEXT: Size: 0
-# CHECK-NEXT: Binding: Global
-# CHECK-NEXT: Type: None
-# CHECK-NEXT: Other: 0
-# CHECK-NEXT: Section: Absolute
-# CHECK-NEXT: }
-# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: _end
-# CHECK-NEXT: Value: 0x1200A
-# CHECK-NEXT: Size: 0
-# CHECK-NEXT: Binding: Global
-# CHECK-NEXT: Type: None
-# CHECK-NEXT: Other: 0
-# CHECK-NEXT: Section: Absolute
-# CHECK-NEXT: }
-# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: _etext
-# CHECK-NEXT: Value: 0x11001
-# CHECK-NEXT: Size: 0
-# CHECK-NEXT: Binding: Global
-# CHECK-NEXT: Type: None
-# CHECK-NEXT: Other: 0
-# CHECK-NEXT: Section: Absolute
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
+# CHECK: Sections:
+# CHECK-NEXT: Idx Name Size Address Type
+# CHECK-NEXT: 0 00000000 0000000000000000
+# CHECK-NEXT: 1 .text 00000001 0000000000201000 TEXT DATA
+# CHECK-NEXT: 2 .data 00000002 0000000000202000 DATA
+# CHECK-NEXT: 3 .bss 00000006 0000000000202004 BSS
+# CHECK: SYMBOL TABLE:
+# CHECK-NEXT: 0000000000000000 *UND* 00000000
+# CHECK-NEXT: 0000000000202002 *ABS* 00000000 _edata
+# CHECK-NEXT: 000000000020200a *ABS* 00000000 _end
+# CHECK-NEXT: 0000000000201001 *ABS* 00000000 _etext
+# CHECK-NEXT: 0000000000201000 .text 00000000 _start
+
+# RUN: ld.lld -r %t.o -o %t2
+# RUN: llvm-objdump -t %t2 | FileCheck %s --check-prefix=RELOCATABLE
+# RELOCATABLE: 0000000000000000 *UND* 00000000 _edata
+# RELOCATABLE-NEXT: 0000000000000000 *UND* 00000000 _end
+# RELOCATABLE-NEXT: 0000000000000000 *UND* 00000000 _etext
.global _start,_end,_etext,_edata
.text
diff --git a/test/ELF/eh-align-cie.s b/test/ELF/eh-align-cie.s
index e85cbd27d4fd..343dea5004f0 100644
--- a/test/ELF/eh-align-cie.s
+++ b/test/ELF/eh-align-cie.s
@@ -51,7 +51,7 @@ bar:
// CHECK-NEXT: EntrySize:
// CHECK-NEXT: SectionData (
// CHECK-NEXT: 0000: 1C000000 00000000 017A5052 00017810
-// CHECK-NEXT: 0010: 061B260E 00001B0C 07089001 00000000
-// CHECK-NEXT: 0020: 14000000 24000000 100E0000 00000000
+// CHECK-NEXT: 0010: 061BF60D 00001B0C 07089001 00000000
+// CHECK-NEXT: 0020: 14000000 24000000 E00D0000 00000000
// CHECK-NEXT: 0030: 00000000 00000000
// CHECK-NEXT: )
diff --git a/test/ELF/eh-frame-dyn-rel.s b/test/ELF/eh-frame-dyn-rel.s
index 62d56951b2cf..91baf597c723 100644
--- a/test/ELF/eh-frame-dyn-rel.s
+++ b/test/ELF/eh-frame-dyn-rel.s
@@ -7,4 +7,4 @@
.cfi_personality 0x8c, foo
.cfi_endproc
-// CHECK: can't create dynamic relocation R_X86_64_64 against readonly segment
+// CHECK: {{.*}}.o:(.eh_frame+0x12): can't create dynamic relocation R_X86_64_64 against symbol 'foo' defined in {{.*}}.o
diff --git a/test/ELF/eh-frame-gc2.s b/test/ELF/eh-frame-gc2.s
new file mode 100644
index 000000000000..9cf0d0832d5b
--- /dev/null
+++ b/test/ELF/eh-frame-gc2.s
@@ -0,0 +1,15 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o
+// RUN: ld.lld --gc-sections %t.o -o %t
+// RUN: llvm-readobj -s %t | FileCheck %s
+
+// Test that the we don't gc the personality function.
+// CHECK: Name: .foobar
+
+ .globl _start
+_start:
+ .cfi_startproc
+ .cfi_personality 3, foobar
+ .cfi_endproc
+ .section .foobar,"ax"
+foobar:
diff --git a/test/ELF/eh-frame-hdr-abs-fde.s b/test/ELF/eh-frame-hdr-abs-fde.s
index 37705d6ad818..dd0836a0ab72 100644
--- a/test/ELF/eh-frame-hdr-abs-fde.s
+++ b/test/ELF/eh-frame-hdr-abs-fde.s
@@ -9,11 +9,11 @@
# REQUIRES: mips
# CHECK: Contents of section .eh_frame_hdr:
-# CHECK-NEXT: 10178 011b033b ffffffcc 00000001 0000fe88
-# ^-- 0x20000 - 0x10178
+# CHECK-NEXT: 10148 011b033b 00000010 00000001 0000feb8
+# ^-- 0x20000 - 0x10148
# .text - .eh_frame_hdr
-# CHECK-NEXT: 10188 ffffffe8
-# CHECK-NEXT: Contents of section .text:
+# CHECK-NEXT: 10158 0000002c
+# CHECK: Contents of section .text:
# CHECK-NEXT: 20000 00000000
# CHECK: Augmentation: "zLR"
diff --git a/test/ELF/eh-frame-hdr-augmentation.s b/test/ELF/eh-frame-hdr-augmentation.s
index 91e6a9cc1ccb..618f5e1a9d7d 100644
--- a/test/ELF/eh-frame-hdr-augmentation.s
+++ b/test/ELF/eh-frame-hdr-augmentation.s
@@ -18,7 +18,7 @@
// CHECK-NEXT: DW_CFA_nop:
// CHECK-NEXT: DW_CFA_nop:
-// CHECK: 00000020 00000014 00000024 FDE cie=00000024 pc=00000dd8...00000dd8
+// CHECK: 00000020 00000014 00000024 FDE cie=00000024 pc=00000d98...00000d98
// CHECK-NEXT: DW_CFA_nop:
// CHECK-NEXT: DW_CFA_nop:
// CHECK-NEXT: DW_CFA_nop:
diff --git a/test/ELF/eh-frame-hdr-icf.s b/test/ELF/eh-frame-hdr-icf.s
index 2e7b335fb46d..1b42285f5d32 100644
--- a/test/ELF/eh-frame-hdr-icf.s
+++ b/test/ELF/eh-frame-hdr-icf.s
@@ -5,10 +5,10 @@
# RUN: llvm-objdump -s %t2 | FileCheck %s
# CHECK: Contents of section .eh_frame_hdr:
-# CHECK-NEXT: 101a0 011b033b b4ffffff 01000000 600e0000
-# ^ FDE count
-# CHECK-NEXT: 101b0 d0ffffff 00000000 00000000
-# ^ FDE for f2
+# CHECK-NEXT: 200158 011b033b 1c000000 01000000 a80e0000
+# ^ FDE count
+# CHECK-NEXT: 200168 38000000 00000000 00000000
+# ^ FDE for f2
.globl _start, f1, f2
_start:
diff --git a/test/ELF/eh-frame-hdr-no-out.s b/test/ELF/eh-frame-hdr-no-out.s
deleted file mode 100644
index edbafad07247..000000000000
--- a/test/ELF/eh-frame-hdr-no-out.s
+++ /dev/null
@@ -1,6 +0,0 @@
-// REQUIRES: x86
-// RUN: not ld.lld --eh-frame-hdr %p/Inputs/invalid-cie-version2.elf -o %t >& %t.log
-// RUN: FileCheck %s < %t.log
-
-// invalid-cie-version2.elf contains unsupported version of CIE = 2.
-// CHECK: FDE version 1 or 3 expected, but got 2
diff --git a/test/ELF/eh-frame-hdr.s b/test/ELF/eh-frame-hdr.s
index 16fb49ac8e9f..35c14a4b65dd 100644
--- a/test/ELF/eh-frame-hdr.s
+++ b/test/ELF/eh-frame-hdr.s
@@ -32,92 +32,91 @@ _start:
//HDRDISASM: Disassembly of section foo:
//HDRDISASM-NEXT: foo:
-//HDRDISASM-NEXT: 11000: 90 nop
+//HDRDISASM-NEXT: 201000: 90 nop
//HDRDISASM-NEXT: Disassembly of section bar:
//HDRDISASM-NEXT: bar:
-//HDRDISASM-NEXT: 11001: 90 nop
+//HDRDISASM-NEXT: 201001: 90 nop
//HDRDISASM-NEXT: Disassembly of section dah:
//HDRDISASM-NEXT: dah:
-//HDRDISASM-NEXT: 11002: 90 nop
+//HDRDISASM-NEXT: 201002: 90 nop
-// HDR: Sections [
-// HDR: Section {
-// HDR: Index: 1
-// HDR-NEXT: Name: .eh_frame
+// HDR: Section {
+// HDR: Index:
+// HDR: Name: .eh_frame_hdr
// HDR-NEXT: Type: SHT_PROGBITS
// HDR-NEXT: Flags [
// HDR-NEXT: SHF_ALLOC
// HDR-NEXT: ]
-// HDR-NEXT: Address: 0x10158
+// HDR-NEXT: Address: 0x200158
// HDR-NEXT: Offset: 0x158
-// HDR-NEXT: Size: 96
+// HDR-NEXT: Size: 36
// HDR-NEXT: Link: 0
// HDR-NEXT: Info: 0
-// HDR-NEXT: AddressAlignment: 8
+// HDR-NEXT: AddressAlignment: 1
// HDR-NEXT: EntrySize: 0
// HDR-NEXT: SectionData (
-// HDR-NEXT: 0000: 14000000 00000000 017A5200 01781001 |
-// HDR-NEXT: 0010: 1B0C0708 90010000 14000000 1C000000 |
-// HDR-NEXT: 0020: 880E0000 01000000 00000000 00000000 |
-// HDR-NEXT: 0030: 14000000 34000000 710E0000 01000000 |
-// HDR-NEXT: 0040: 00000000 00000000 14000000 4C000000 |
-// HDR-NEXT: 0050: 5A0E0000 01000000 00000000 00000000 |
-// CIE: 14000000 00000000 017A5200 01781001 1B0C0708 90010000
-// FDE(1): 14000000 1C000000 880E0000 01000000 00000000 00000000
-// address of data (starts with 0x880E0000) = 0x10158 + 0x0020 = 0x10178
-// The starting address to which this FDE applies = 0xE88 + 0x10178 = 0x11000
-// The number of bytes after the start address to which this FDE applies = 0x01000000 = 1
-// FDE(2): 14000000 34000000 710E0000 01000000 00000000 00000000
-// address of data (starts with 0x710E0000) = 0x10158 + 0x0038 = 0x10190
-// The starting address to which this FDE applies = 0xE71 + 0x10190 = 0x11001
-// The number of bytes after the start address to which this FDE applies = 0x01000000 = 1
-// FDE(3): 14000000 4C000000 5A0E0000 01000000 00000000 00000000
-// address of data (starts with 0x5A0E0000) = 0x10158 + 0x0050 = 0x101A8
-// The starting address to which this FDE applies = 0xE5A + 0x101A8 = 0x11002
-// The number of bytes after the start address to which this FDE applies = 0x01000000 = 1
+// HDR-NEXT: 0000: 011B033B 24000000 03000000 A80E0000
+// HDR-NEXT: 0010: 40000000 A90E0000 58000000 AA0E0000
+// HDR-NEXT: 0020: 70000000
// HDR-NEXT: )
+// Header (always 4 bytes): 0x011B033B
+// 24000000 = .eh_frame(0x200180) - .eh_frame_hdr(0x200158) - 4
+// 03000000 = 3 = the number of FDE pointers in the table.
+// Entry(1): A80E0000 40000000
+// 480E0000 = 0x201000 - .eh_frame_hdr(0x200158) = 0xEA8
+// 40000000 = address of FDE(1) - .eh_frame_hdr(0x200158) =
+// = .eh_frame(0x200180) + 24 - 0x200158 = 0x40
+// Entry(2): A90E0000 58000000
+// A90E0000 = 0x201001 - .eh_frame_hdr(0x200158) = 0xEA9
+// 58000000 = address of FDE(2) - .eh_frame_hdr(0x200158) =
+// = .eh_frame(0x200180) + 24 + 24 - 0x200158 = 0x58
+// Entry(3): AA0E0000 70000000
+// AA0E0000 = 0x201002 - .eh_frame_hdr(0x200158) = 0xEAA
+// 70000000 = address of FDE(3) - .eh_frame_hdr(0x200158) =
+// = .eh_frame(0x200180) + 24 + 24 + 24 - 0x200158 = 0x70
// HDR-NEXT: }
// HDR-NEXT: Section {
-// HDR-NEXT: Index: 2
-// HDR-NEXT: Name: .eh_frame_hdr
+// HDR-NEXT: Index:
+// HDR-NEXT: Name: .eh_frame
// HDR-NEXT: Type: SHT_PROGBITS
// HDR-NEXT: Flags [
// HDR-NEXT: SHF_ALLOC
// HDR-NEXT: ]
-// HDR-NEXT: Address: 0x101B8
-// HDR-NEXT: Offset: 0x1B8
-// HDR-NEXT: Size: 36
+// HDR-NEXT: Address: 0x200180
+// HDR-NEXT: Offset: 0x180
+// HDR-NEXT: Size: 96
// HDR-NEXT: Link: 0
// HDR-NEXT: Info: 0
-// HDR-NEXT: AddressAlignment: 1
+// HDR-NEXT: AddressAlignment: 8
// HDR-NEXT: EntrySize: 0
// HDR-NEXT: SectionData (
-// HDR-NEXT: 0000: 011B033B 9CFFFFFF 03000000 480E0000 |
-// HDR-NEXT: 0010: B8FFFFFF 490E0000 D0FFFFFF 4A0E0000 |
-// HDR-NEXT: 0020: E8FFFFFF |
-// Header (always 4 bytes): 0x011B033B
-// 9CFFFFFF = .eh_frame(0x10158) - .eh_frame_hdr(0x101B8) - 4
-// 03000000 = 3 = the number of FDE pointers in the table.
-// Entry(1): 480E0000 B8FFFFFF
-// 480E0000 = 0x11000 - .eh_frame_hdr(0x101B8) = 0xE48
-// B8FFFFFF = address of FDE(1) - .eh_frame_hdr(0x101B8) =
-// = .eh_frame(0x10158) + 24 - 0x101B8 = 0xFFFFFFB8
-// Entry(2): 490E0000 D0FFFFFF
-// 490E0000 = 0x11001 - .eh_frame_hdr(0x101B8) = 0xE49
-// D0FFFFFF = address of FDE(2) - .eh_frame_hdr(0x101B8) =
-// = .eh_frame(0x10158) + 24 + 24 - 0x101B8 = 0xFFFFFFD0
-// Entry(3): 4A0E0000 E8FFFFFF
-// 4A0E0000 = 0x11002 - .eh_frame_hdr(0x101B8) = 0xE4A
-// E8FFFFFF = address of FDE(2) - .eh_frame_hdr(0x101B8) =
-// = .eh_frame(0x10158) + 24 + 24 - 0x101B8 = 0xFFFFFFE8
+// HDR-NEXT: 0000: 14000000 00000000 017A5200 01781001
+// HDR-NEXT: 0010: 1B0C0708 90010000 14000000 1C000000
+// HDR-NEXT: 0020: 600E0000 01000000 00000000 00000000
+// HDR-NEXT: 0030: 14000000 34000000 490E0000 01000000
+// HDR-NEXT: 0040: 00000000 00000000 14000000 4C000000
+// HDR-NEXT: 0050: 320E0000 01000000 00000000 00000000
// HDR-NEXT: )
+// CIE: 14000000 00000000 017A5200 01781001 1B0C0708 90010000
+// FDE(1): 14000000 1C000000 600E0000 01000000 00000000 00000000
+// address of data (starts with 0x600E0000) = 0x200180 + 0x0020 = 0x2001A0
+// The starting address to which this FDE applies = 0xE60 + 0x2001A0 = 0x201000
+// The number of bytes after the start address to which this FDE applies = 0x01000000 = 1
+// FDE(2): 14000000 34000000 490E0000 01000000 00000000 00000000
+// address of data (starts with 0x490E0000) = 0x200180 + 0x0038 = 0x2001B8
+// The starting address to which this FDE applies = 0xE49 + 0x2001B8 = 0x201001
+// The number of bytes after the start address to which this FDE applies = 0x01000000 = 1
+// FDE(3): 14000000 4C000000 320E0000 01000000 00000000 00000000
+// address of data (starts with 0x320E0000) = 0x200180 + 0x0050 = 0x2001D0
+// The starting address to which this FDE applies = 0xE5A + 0x2001D0 = 0x201002
+// The number of bytes after the start address to which this FDE applies = 0x01000000 = 1
// HDR-NEXT: }
// HDR: ProgramHeaders [
// HDR: ProgramHeader {
// HDR: Type: PT_GNU_EH_FRAME
-// HDR-NEXT: Offset: 0x1B8
-// HDR-NEXT: VirtualAddress: 0x101B8
-// HDR-NEXT: PhysicalAddress: 0x101B8
+// HDR-NEXT: Offset: 0x158
+// HDR-NEXT: VirtualAddress: 0x200158
+// HDR-NEXT: PhysicalAddress: 0x200158
// HDR-NEXT: FileSize: 36
// HDR-NEXT: MemSize: 36
// HDR-NEXT: Flags [
diff --git a/test/ELF/eh-frame-marker.s b/test/ELF/eh-frame-marker.s
index a5de2a7c3d40..30bac460ae25 100644
--- a/test/ELF/eh-frame-marker.s
+++ b/test/ELF/eh-frame-marker.s
@@ -1,6 +1,19 @@
// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux
// RUN: ld.lld --eh-frame-hdr %t.o -o %t.so -shared
+// RUN: llvm-readobj -t -s %t.so | FileCheck %s
// We used to crash on this.
+
+// CHECK: Name: .eh_frame_hdr
+// CHECK: Name: .eh_frame
+// CHECK-NEXT: Type: SHT_PROGBITS
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: [[ADDR:.*]]
+
+// CHECK: Name: foo
+// CHECK-NEXT: Value: [[ADDR]]
+
.section .eh_frame
foo:
.long 0
diff --git a/test/ELF/eh-frame-merge.s b/test/ELF/eh-frame-merge.s
index 2610d4d4d2de..addbb3f857fe 100644
--- a/test/ELF/eh-frame-merge.s
+++ b/test/ELF/eh-frame-merge.s
@@ -35,10 +35,10 @@
// CHECK-NEXT: SectionData (
// CHECK-NEXT: 0000: 14000000 00000000 017A5200 01781001 |
// CHECK-NEXT: 0010: 1B0C0708 90010000 14000000 1C000000 |
-// CHECK-NEXT: 0020: 180E0000 01000000 00000000 00000000 |
-// CHECK-NEXT: 0030: 14000000 34000000 020E0000 02000000 |
+// CHECK-NEXT: 0020: E80D0000 01000000 00000000 00000000 |
+// CHECK-NEXT: 0030: 14000000 34000000 D20D0000 02000000 |
// CHECK-NEXT: 0040: 00000000 00000000 14000000 4C000000 |
-// CHECK-NEXT: 0050: E90D0000 01000000 00000000 00000000 |
+// CHECK-NEXT: 0050: B90D0000 01000000 00000000 00000000 |
// CHECK-NEXT: )
// CHECK: Name: foo
diff --git a/test/ELF/ehdr_start.s b/test/ELF/ehdr_start.s
new file mode 100644
index 000000000000..9172feca5e9c
--- /dev/null
+++ b/test/ELF/ehdr_start.s
@@ -0,0 +1,16 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t
+# RUN: llvm-objdump -t %t | FileCheck %s
+# CHECK: 0000000000200040 *ABS* 00000000 .hidden __ehdr_start
+
+.text
+.global _start, __ehdr_start
+_start:
+ .quad __ehdr_start
+
+# RUN: ld.lld -r %t.o -o %t.r
+# RUN: llvm-objdump -t %t.r | FileCheck %s --check-prefix=RELOCATABLE
+
+# RELOCATABLE: 0000000000000000 *UND* 00000000 __ehdr_start
diff --git a/test/ELF/ehframe-relocation.s b/test/ELF/ehframe-relocation.s
index 8d5e14919e92..0213b1bebf83 100644
--- a/test/ELF/ehframe-relocation.s
+++ b/test/ELF/ehframe-relocation.s
@@ -10,17 +10,17 @@
// CHECK-NEXT: Flags [
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x10120
+// CHECK-NEXT: Address: 0x200120
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 48
// CHECK-NOT: .eh_frame
-// 0x10120 = 65824
-// 0x10120 + 5 = 65829
+// 0x200120 = 2097440
+// 0x200120 + 5 = 2097445
// DISASM: Disassembly of section .text:
// DISASM-NEXT: _start:
-// DISASM-NEXT: 11000: {{.*}} movq 65824, %rax
-// DISASM-NEXT: 11008: {{.*}} movq 65829, %rax
+// DISASM-NEXT: 201000: {{.*}} movq 2097440, %rax
+// DISASM-NEXT: 201008: {{.*}} movq 2097445, %rax
.section .eh_frame,"ax",@unwind
diff --git a/test/ELF/empty-pt-load.s b/test/ELF/empty-pt-load.s
new file mode 100644
index 000000000000..4074b63cb80f
--- /dev/null
+++ b/test/ELF/empty-pt-load.s
@@ -0,0 +1,11 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: llvm-readobj -l --elf-output-style=GNU %t.so | FileCheck %s
+
+// Test that we don't create an empty executable PT_LOAD.
+
+// CHECK: PHDR {{.*}} R 0x8
+// CHECK-NEXT: LOAD {{.*}} R 0x1000
+// CHECK-NEXT: LOAD {{.*}} RW 0x1000
+// CHECK-NEXT: DYNAMIC {{.*}} RW 0x8
diff --git a/test/ELF/empty-ver.s b/test/ELF/empty-ver.s
index f200d2876e09..3412f3134737 100644
--- a/test/ELF/empty-ver.s
+++ b/test/ELF/empty-ver.s
@@ -1,7 +1,25 @@
// REQUIRES: x86
+// RUN: mkdir -p %t.dir
+// RUN: cd %t.dir
// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux
-// RUN: ld.lld %t.o -o %t.so -shared -version-script %p/Inputs/empty-ver.ver
-// RUN: llvm-readobj -version-info %t.so | FileCheck %s
+// RUN: ld.lld %t.o -o t.so -shared -version-script %p/Inputs/empty-ver.ver
+// RUN: llvm-readobj -s -section-data -version-info t.so | FileCheck %s
+
+// CHECK: Name: .dynstr
+// CHECK-NEXT: Type: SHT_STRTAB
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address:
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size: 14
+// CHECK-NEXT: Link: 0
+// CHECK-NEXT: Info: 0
+// CHECK-NEXT: AddressAlignment: 1
+// CHECK-NEXT: EntrySize: 0
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: 0000: 00666F6F 00742E73 6F007665 7200 |.foo.t.so.ver.|
+// CHECK-NEXT: )
// CHECK: Version symbols {
// CHECK-NEXT: Section Name:
diff --git a/test/ELF/emulation.s b/test/ELF/emulation.s
index 7cc764fc6c18..a54faeaa832a 100644
--- a/test/ELF/emulation.s
+++ b/test/ELF/emulation.s
@@ -1,3 +1,37 @@
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-freebsd %s -o %tx64
+# RUN: ld.lld -m elf_amd64_fbsd %tx64 -o %t2x64
+# RUN: llvm-readobj -file-headers %t2x64 | FileCheck --check-prefix=AMD64 %s
+# RUN: ld.lld %tx64 -o %t3x64
+# RUN: llvm-readobj -file-headers %t3x64 | FileCheck --check-prefix=AMD64 %s
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.sysv
+# RUN: ld.lld -m elf_amd64_fbsd %t.sysv -o %t.freebsd
+# RUN: llvm-readobj -file-headers %t.freebsd | FileCheck --check-prefix=AMD64 %s
+# AMD64: ElfHeader {
+# AMD64-NEXT: Ident {
+# AMD64-NEXT: Magic: (7F 45 4C 46)
+# AMD64-NEXT: Class: 64-bit (0x2)
+# AMD64-NEXT: DataEncoding: LittleEndian (0x1)
+# AMD64-NEXT: FileVersion: 1
+# AMD64-NEXT: OS/ABI: FreeBSD (0x9)
+# AMD64-NEXT: ABIVersion: 0
+# AMD64-NEXT: Unused: (00 00 00 00 00 00 00)
+# AMD64-NEXT: }
+# AMD64-NEXT: Type: Executable (0x2)
+# AMD64-NEXT: Machine: EM_X86_64 (0x3E)
+# AMD64-NEXT: Version: 1
+# AMD64-NEXT: Entry:
+# AMD64-NEXT: ProgramHeaderOffset: 0x40
+# AMD64-NEXT: SectionHeaderOffset:
+# AMD64-NEXT: Flags [ (0x0)
+# AMD64-NEXT: ]
+# AMD64-NEXT: HeaderSize: 64
+# AMD64-NEXT: ProgramHeaderEntrySize: 56
+# AMD64-NEXT: ProgramHeaderCount:
+# AMD64-NEXT: SectionHeaderEntrySize: 64
+# AMD64-NEXT: SectionHeaderCount:
+# AMD64-NEXT: StringTableSectionIndex:
+# AMD64-NEXT: }
+
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %tx64
# RUN: ld.lld -m elf_x86_64 %tx64 -o %t2x64
# RUN: llvm-readobj -file-headers %t2x64 | FileCheck --check-prefix=X86-64 %s
@@ -122,6 +156,37 @@
# X86FBSD-NEXT: StringTableSectionIndex:
# X86FBSD-NEXT: }
+# RUN: llvm-mc -filetype=obj -triple=i586-intel-elfiamcu %s -o %tiamcu
+# RUN: ld.lld -m elf_iamcu %tiamcu -o %t2iamcu
+# RUN: llvm-readobj -file-headers %t2iamcu | FileCheck --check-prefix=IAMCU %s
+# RUN: ld.lld %tiamcu -o %t3iamcu
+# RUN: llvm-readobj -file-headers %t3iamcu | FileCheck --check-prefix=IAMCU %s
+# IAMCU: ElfHeader {
+# IAMCU-NEXT: Ident {
+# IAMCU-NEXT: Magic: (7F 45 4C 46)
+# IAMCU-NEXT: Class: 32-bit (0x1)
+# IAMCU-NEXT: DataEncoding: LittleEndian (0x1)
+# IAMCU-NEXT: FileVersion: 1
+# IAMCU-NEXT: OS/ABI: SystemV (0x0)
+# IAMCU-NEXT: ABIVersion: 0
+# IAMCU-NEXT: Unused: (00 00 00 00 00 00 00)
+# IAMCU-NEXT: }
+# IAMCU-NEXT: Type: Executable (0x2)
+# IAMCU-NEXT: Machine: EM_IAMCU (0x6)
+# IAMCU-NEXT: Version: 1
+# IAMCU-NEXT: Entry:
+# IAMCU-NEXT: ProgramHeaderOffset: 0x34
+# IAMCU-NEXT: SectionHeaderOffset:
+# IAMCU-NEXT: Flags [ (0x0)
+# IAMCU-NEXT: ]
+# IAMCU-NEXT: HeaderSize: 52
+# IAMCU-NEXT: ProgramHeaderEntrySize: 32
+# IAMCU-NEXT: ProgramHeaderCount:
+# IAMCU-NEXT: SectionHeaderEntrySize: 40
+# IAMCU-NEXT: SectionHeaderCount:
+# IAMCU-NEXT: StringTableSectionIndex:
+# IAMCU-NEXT: }
+
# RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %s -o %tppc64
# RUN: ld.lld -m elf64ppc %tppc64 -o %t2ppc64
# RUN: llvm-readobj -file-headers %t2ppc64 | FileCheck --check-prefix=PPC64 %s
@@ -176,7 +241,7 @@
# MIPS-NEXT: SectionHeaderOffset:
# MIPS-NEXT: Flags [
# MIPS-NEXT: EF_MIPS_ABI_O32
-# MIPS-NEXT: EF_MIPS_ARCH_32R2
+# MIPS-NEXT: EF_MIPS_ARCH_32
# MIPS-NEXT: EF_MIPS_CPIC
# MIPS-NEXT: ]
@@ -205,7 +270,7 @@
# MIPSEL-NEXT: SectionHeaderOffset:
# MIPSEL-NEXT: Flags [
# MIPSEL-NEXT: EF_MIPS_ABI_O32
-# MIPSEL-NEXT: EF_MIPS_ARCH_32R2
+# MIPSEL-NEXT: EF_MIPS_ARCH_32
# MIPSEL-NEXT: EF_MIPS_CPIC
# MIPSEL-NEXT: ]
@@ -231,7 +296,7 @@
# MIPS64-NEXT: ProgramHeaderOffset: 0x40
# MIPS64-NEXT: SectionHeaderOffset:
# MIPS64-NEXT: Flags [
-# MIPS64-NEXT: EF_MIPS_ARCH_64R2
+# MIPS64-NEXT: EF_MIPS_ARCH_64
# MIPS64-NEXT: EF_MIPS_CPIC
# MIPS64-NEXT: EF_MIPS_PIC
# MIPS64-NEXT: ]
@@ -258,7 +323,7 @@
# MIPS64EL-NEXT: ProgramHeaderOffset: 0x40
# MIPS64EL-NEXT: SectionHeaderOffset:
# MIPS64EL-NEXT: Flags [
-# MIPS64EL-NEXT: EF_MIPS_ARCH_64R2
+# MIPS64EL-NEXT: EF_MIPS_ARCH_64
# MIPS64EL-NEXT: EF_MIPS_CPIC
# MIPS64EL-NEXT: EF_MIPS_PIC
# MIPS64EL-NEXT: ]
diff --git a/test/ELF/end-update.s b/test/ELF/end-update.s
index 8654ef1e92eb..afb137fd2d73 100644
--- a/test/ELF/end-update.s
+++ b/test/ELF/end-update.s
@@ -12,13 +12,13 @@
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x12000
+// CHECK-NEXT: Address: 0x202000
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 6
// CHECK: ]
// CHECK: Symbols [
// CHECK: Name: end
-// CHECK-NEXT: Value: 0x12006
+// CHECK-NEXT: Value: 0x202006
// CHECK: ]
.global _start,end
diff --git a/test/ELF/end.s b/test/ELF/end.s
index 689157f82070..f40c5db8b5ff 100644
--- a/test/ELF/end.s
+++ b/test/ELF/end.s
@@ -14,60 +14,18 @@
// DEFAULT-NEXT: SHF_ALLOC
// DEFAULT-NEXT: SHF_WRITE
// DEFAULT-NEXT: ]
-// DEFAULT-NEXT: Address: 0x12002
+// DEFAULT-NEXT: Address: 0x202002
// DEFAULT-NEXT: Offset:
// DEFAULT-NEXT: Size: 6
// DEFAULT: ]
// DEFAULT: Symbols [
// DEFAULT: Name: _end
-// DEFAULT-NEXT: Value: 0x12008
+// DEFAULT-NEXT: Value: 0x202008
// DEFAULT: ]
-// If there is no .bss section, "_end" should point to the end of the .data section.
-// RUN: echo "SECTIONS { \
-// RUN: /DISCARD/ : { *(.bss) } }" > %t.script
-// RUN: ld.lld %t.o --script %t.script -o %t
-// RUN: llvm-readobj -sections -symbols %t | FileCheck %s --check-prefix=NOBSS
-
-// NOBSS: Sections [
-// NOBSS: Name: .data
-// NOBSS-NEXT: Type:
-// NOBSS-NEXT: Flags [
-// NOBSS-NEXT: SHF_ALLOC
-// NOBSS-NEXT: SHF_WRITE
-// NOBSS-NEXT: ]
-// NOBSS-NEXT: Address: 0x159
-// NOBSS-NEXT: Offset:
-// NOBSS-NEXT: Size: 2
-// NOBSS: ]
-// NOBSS: Symbols [
-// NOBSS: Name: _end
-// NOBSS-NEXT: Value: 0x15B
-// NOBSS: ]
-
-// If the layout of the sections is changed, "_end" should point to the end of allocated address space.
-// RUN: echo "SECTIONS { \
-// RUN: .bss : { *(.bss) } \
-// RUN: .data : { *(.data) } \
-// RUN: .text : { *(.text) } }" > %t.script
-// RUN: ld.lld %t.o --script %t.script -o %t
-// RUN: llvm-readobj -sections -symbols %t | FileCheck %s --check-prefix=TEXTATEND
-
-// TEXTATEND: Sections [
-// TEXTATEND: Name: .text
-// TEXTATEND-NEXT: Type:
-// TEXTATEND-NEXT: Flags [
-// TEXTATEND-NEXT: SHF_ALLOC
-// TEXTATEND-NEXT: SHF_EXECINSTR
-// TEXTATEND-NEXT: ]
-// TEXTATEND-NEXT: Address: 0x160
-// TEXTATEND-NEXT: Offset:
-// TEXTATEND-NEXT: Size: 1
-// TEXTATEND: ]
-// TEXTATEND: Symbols [
-// TEXTATEND: Name: _end
-// TEXTATEND-NEXT: Value: 0x161
-// TEXTATEND: ]
+// RUN: ld.lld -r %t.o -o %t2
+// RUN: llvm-objdump -t %t2 | FileCheck %s --check-prefix=RELOCATABLE
+// RELOCATABLE: 0000000000000000 *UND* 00000000 _end
.global _start,_end
.text
diff --git a/test/ELF/entry.s b/test/ELF/entry.s
index 6175c7623a34..f288bcf6ae50 100644
--- a/test/ELF/entry.s
+++ b/test/ELF/entry.s
@@ -1,21 +1,53 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1
-# RUN: not ld.lld %t1 -o %t2
+
+# RUN: ld.lld -e foobar %t1 -o %t2 2>&1 | FileCheck -check-prefix=WARN1 %s
+# RUN: llvm-readobj -file-headers %t2 | FileCheck -check-prefix=TEXT %s
+
+# WARN1: warning: cannot find entry symbol foobar; defaulting to 0x201000
+# TEXT: Entry: 0x201000
+
+# RUN: ld.lld %t1 -o %t2 2>&1 | FileCheck -check-prefix=WARN2 %s
+# WARN2: warning: cannot find entry symbol _start; defaulting to 0x201000
+
+# RUN: ld.lld -shared -e foobar %t1 -o %t2 2>&1 | FileCheck -check-prefix=WARN3 %s
+# WARN3: warning: cannot find entry symbol foobar; defaulting to 0x1000
+
+# RUN: ld.lld -shared --fatal-warnings -e entry %t1 -o %t2
+# RUN: ld.lld -shared --fatal-warnings %t1 -o %t2
+
+# RUN: echo .data > %t.s
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux -n %t.s -o %t3
+# RUN: ld.lld %t3 -o %t4 2>&1 | FileCheck -check-prefix=WARN4 %s
+# RUN: llvm-readobj -file-headers %t4 | FileCheck -check-prefix=NOENTRY %s
+
+# WARN4: cannot find entry symbol _start; not setting start address
+# NOENTRY: Entry: 0x0
+
+# RUN: ld.lld -v -r %t1 -o %t2 2>&1 | FileCheck -check-prefix=WARN5 %s
+# WARN5-NOT: warning: cannot find entry symbol
+
# RUN: ld.lld %t1 -o %t2 -e entry
# RUN: llvm-readobj -file-headers %t2 | FileCheck -check-prefix=SYM %s
-# RUN: ld.lld %t1 -shared -o %t2 -e entry
+# SYM: Entry: 0x201008
+
+# RUN: ld.lld %t1 --fatal-warnings -shared -o %t2 -e entry
# RUN: llvm-readobj -file-headers %t2 | FileCheck -check-prefix=DSO %s
+# DSO: Entry: 0x1008
+
# RUN: ld.lld %t1 -o %t2 --entry=4096
# RUN: llvm-readobj -file-headers %t2 | FileCheck -check-prefix=DEC %s
+# DEC: Entry: 0x1000
+
# RUN: ld.lld %t1 -o %t2 --entry 0xcafe
# RUN: llvm-readobj -file-headers %t2 | FileCheck -check-prefix=HEX %s
+# HEX: Entry: 0xCAFE
+
# RUN: ld.lld %t1 -o %t2 -e 0777
# RUN: llvm-readobj -file-headers %t2 | FileCheck -check-prefix=OCT %s
-
-# SYM: Entry: 0x11000
-# DSO: Entry: 0x1000
-# DEC: Entry: 0x1000
-# HEX: Entry: 0xCAFE
# OCT: Entry: 0x1FF
.globl entry
+.text
+ .quad 0
entry:
+ ret
diff --git a/test/ELF/error-limit.test b/test/ELF/error-limit.test
new file mode 100644
index 000000000000..a82f2f2bfd72
--- /dev/null
+++ b/test/ELF/error-limit.test
@@ -0,0 +1,26 @@
+RUN: not ld.lld 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 \
+RUN: 21 22 2>&1 | FileCheck -check-prefix=DEFAULT %s
+
+DEFAULT: cannot open 01
+DEFAULT: cannot open 20
+DEFAULT-NEXT: too many errors emitted, stopping now (use -error-limit=0 to see all errors)
+DEFAULT-NOT: cannot open 21
+
+RUN: not ld.lld -error-limit=5 01 02 03 04 05 06 07 08 09 10 2>&1 \
+RUN: | FileCheck -check-prefix=LIMIT5 %s
+RUN: not ld.lld -error-limit 5 01 02 03 04 05 06 07 08 09 10 2>&1 \
+RUN: | FileCheck -check-prefix=LIMIT5 %s
+
+LIMIT5: cannot open 01
+LIMIT5: cannot open 05
+LIMIT5-NEXT: too many errors emitted, stopping now (use -error-limit=0 to see all errors)
+LIMIT5-NOT: cannot open 06
+
+RUN: not ld.lld -error-limit=0 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 \
+RUN: 16 17 18 19 20 21 22 2>&1 | FileCheck -check-prefix=UNLIMITED %s
+
+UNLIMITED: cannot open 01
+UNLIMITED: cannot open 20
+UNLIMITED: cannot open 21
+UNLIMITED: cannot open 22
+UNLIMITED-NOT: too many errors emitted, stopping now (use -error-limit=0 to see all errors)
diff --git a/test/ELF/exclude.s b/test/ELF/exclude.s
new file mode 100644
index 000000000000..8fe003747b14
--- /dev/null
+++ b/test/ELF/exclude.s
@@ -0,0 +1,19 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: ld.lld -o %t1 %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+# RUN: ld.lld -r -o %t1 %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck --check-prefix=RELOCATABLE %s
+
+# CHECK-NOT: .aaa
+# RELOCATABLE: .aaa
+
+.globl _start
+_start:
+ jmp _start
+
+.section .aaa,"ae"
+ .quad .bbb
+
+.section .bbb,"a"
+ .quad 0
diff --git a/test/ELF/format-binary.test b/test/ELF/format-binary.test
new file mode 100644
index 000000000000..586f5f963dfa
--- /dev/null
+++ b/test/ELF/format-binary.test
@@ -0,0 +1,56 @@
+# REQUIRES: x86
+
+# RUN: echo -n "Fluffle Puff" > %t.binary
+# RUN: ld.lld -m elf_x86_64 -r -b binary %t.binary -o %t.out
+# RUN: llvm-readobj %t.out -sections -section-data -symbols | FileCheck %s
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -b binary %t.binary -b default %t.o -shared -o %t.out
+
+# RUN: not ld.lld -b foo > %t.log 2>&1
+# RUN: FileCheck -check-prefix=ERR %s < %t.log
+# ERR: error: unknown -format value: foo (supported formats: elf, default, binary)
+
+# CHECK: Name: .data
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x0
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 12
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment:
+# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 466C7566 666C6520 50756666 |Fluffle Puff|
+# CHECK-NEXT: )
+# CHECK-NEXT: }
+
+# CHECK: Name: _binary_{{[a-zA-Z0-9_]+}}test_ELF_Output_format_binary_test_tmp_binary_start
+# CHECK-NEXT: Value: 0x0
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .data
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: _binary_{{[a-zA-Z0-9_]+}}test_ELF_Output_format_binary_test_tmp_binary_end
+# CHECK-NEXT: Value: 0xC
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .data
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: _binary_{{[a-zA-Z0-9_]+}}test_ELF_Output_format_binary_test_tmp_binary_size
+# CHECK-NEXT: Value: 0xC
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Absolute
+# CHECK-NEXT: }
diff --git a/test/ELF/gc-debuginfo-tls.s b/test/ELF/gc-debuginfo-tls.s
new file mode 100644
index 000000000000..9884a578cca9
--- /dev/null
+++ b/test/ELF/gc-debuginfo-tls.s
@@ -0,0 +1,23 @@
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o --gc-sections -shared -o %t1
+# RUN: ld.lld %t.o -shared -o %t2
+# RUN: llvm-readobj -symbols %t1 | FileCheck %s --check-prefix=GC
+# RUN: llvm-readobj -symbols %t2 | FileCheck %s --check-prefix=NOGC
+
+# NOGC: Symbol {
+# NOGC: Name: .tbss
+# NOGC: Value: 0x1000
+# NOGC: Size: 0
+# NOGC: Binding: Local
+# NOGC: Type: TLS
+# NOGC: Other: 0
+# NOGC: Section: .tbss
+# NOGC: }
+
+# GC-NOT: tbss
+
+.section .tbss,"awT",@nobits
+patatino:
+ .long 0
+ .section .noalloc,""
+ .quad patatino
diff --git a/test/ELF/gc-sections-alloc.s b/test/ELF/gc-sections-alloc.s
new file mode 100644
index 000000000000..bffd1f9cdcab
--- /dev/null
+++ b/test/ELF/gc-sections-alloc.s
@@ -0,0 +1,31 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: ld.lld %t -o %t2 --gc-sections -shared
+# RUN: llvm-readobj -sections -section-data %t2 | FileCheck %s
+
+# Non alloca section .bar should not keep section .foo alive.
+
+# CHECK-NOT: Name: .foo
+
+# CHECK: Name: .bar
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Link:
+# CHECK-NEXT: Info:
+# CHECK-NEXT: AddressAlignment:
+# CHECK-NEXT: EntrySize:
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 00000000 00000000 |
+# CHECK-NEXT: )
+
+
+.section .foo,"a"
+.byte 0
+
+.section .bar
+.quad .foo
diff --git a/test/ELF/gc-sections-implicit-addend.s b/test/ELF/gc-sections-implicit-addend.s
new file mode 100644
index 000000000000..1270affbcc0e
--- /dev/null
+++ b/test/ELF/gc-sections-implicit-addend.s
@@ -0,0 +1,26 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t --gc-sections
+# RUN: llvm-readobj -s %t | FileCheck %s
+# RUN: llvm-objdump -d %t | FileCheck --check-prefix=DISASM %s
+
+# CHECK: Name: .foo
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_MERGE
+# CHECK-NEXT: SHF_STRINGS
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x100B4
+
+# 0x100B4 == 65716
+# DISASM: leal 65716, %eax
+
+ .section .foo,"aMS",@progbits,1
+ .byte 0
+
+ .text
+ .global _start
+_start:
+ leal .foo, %eax
diff --git a/test/ELF/gc-sections-keep-shared-start.s b/test/ELF/gc-sections-keep-shared-start.s
new file mode 100644
index 000000000000..23417ca01dc6
--- /dev/null
+++ b/test/ELF/gc-sections-keep-shared-start.s
@@ -0,0 +1,30 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: ld.lld -shared --gc-sections -o %t1 %t
+# RUN: llvm-readobj --elf-output-style=GNU --file-headers --symbols %t1
+# | FileCheck %s
+# CHECK: Entry point address: 0x1000
+# CHECK: 0000000000001000 0 FUNC LOCAL HIDDEN 4 _start
+# CHECK: 0000000000001006 0 FUNC LOCAL HIDDEN 4 internal
+# CHECK: 0000000000001005 0 FUNC GLOBAL DEFAULT 4 foobar
+
+.section .text.start,"ax"
+.globl _start
+.type _start,%function
+.hidden _start
+_start:
+ jmp internal
+
+.section .text.foobar,"ax"
+.globl foobar
+.type foobar,%function
+foobar:
+ ret
+
+.section .text.internal,"ax"
+.globl internal
+.hidden internal
+.type internal,%function
+internal:
+ ret
diff --git a/test/ELF/gc-sections-lsda.s b/test/ELF/gc-sections-lsda.s
index b5bed8f0c816..1fc75c8e85b7 100644
--- a/test/ELF/gc-sections-lsda.s
+++ b/test/ELF/gc-sections-lsda.s
@@ -9,7 +9,7 @@
// RUN: llvm-readobj -s %t | FileCheck %s
// CHECK: Name: .abc
-// CHECK: Name: .foo (38)
+// CHECK: Name: .foo
.cfi_startproc
.cfi_lsda 0x1b,zed
diff --git a/test/ELF/gc-sections-non-alloc-to-merge.s b/test/ELF/gc-sections-non-alloc-to-merge.s
new file mode 100644
index 000000000000..37b0328d8455
--- /dev/null
+++ b/test/ELF/gc-sections-non-alloc-to-merge.s
@@ -0,0 +1,27 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t --gc-sections
+# RUN: llvm-readobj -s --elf-output-style=GNU %t | FileCheck %s
+
+# CHECK: .merge1 PROGBITS {{[0-9a-z]*}} {{[0-9a-z]*}} 000004
+
+ .global _start
+_start:
+ .quad .Lfoo
+
+ .section .merge1,"aM",@progbits,4
+ .p2align 2
+.Lfoo:
+ .long 1
+.Lbar:
+ .long 2
+
+ .section .merge2,"aM",@progbits,4
+ .p2align 2
+.Lzed:
+ .long 1
+
+ .section bar
+ .quad .Lbar
+ .quad .Lzed
diff --git a/test/ELF/gc-sections-shared.s b/test/ELF/gc-sections-shared.s
index d52eae2177b2..f1ac9cd345d4 100644
--- a/test/ELF/gc-sections-shared.s
+++ b/test/ELF/gc-sections-shared.s
@@ -8,13 +8,47 @@
# This test the property that we have a needed line for every undefined.
# It would also be OK to drop bar2 and the need for the .so
+# CHECK: DynamicSymbols [
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name:
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Local
+# CHECK-NEXT: Type:
+# CHECK-NEXT: Other:
+# CHECK-NEXT: Section: Undefined (0x0)
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: bar
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type:
+# CHECK-NEXT: Other:
+# CHECK-NEXT: Section: .text
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: bar2
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type:
+# CHECK-NEXT: Other:
+# CHECK-NEXT: Section: Undefined
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: foo
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type:
+# CHECK-NEXT: Other:
+# CHECK-NEXT: Section: .text
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
-# CHECK: Name: bar
-# CHECK: Name: bar2
-# CHECK: Name: foo
# CHECK: NEEDED SharedLibrary ({{.*}}.so)
-
.section .text.foo, "ax"
.globl foo
foo:
diff --git a/test/ELF/gc-sections-synthetic.s b/test/ELF/gc-sections-synthetic.s
new file mode 100644
index 000000000000..e47cc02cdde7
--- /dev/null
+++ b/test/ELF/gc-sections-synthetic.s
@@ -0,0 +1,16 @@
+# REQUIRES: x86
+
+# Linker-synthesized sections shouldn't be gc'ed.
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t1
+# RUN: ld.lld %t1 -shared -o %t.so
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t2
+# RUN: ld.lld %t2 %t.so -build-id -dynamic-linker /foo/bar -o %t.out
+# RUN: llvm-readobj -sections %t.out | FileCheck %s
+
+# CHECK: Name: .interp
+# CHECK: Name: .note.gnu.build-id
+
+.globl _start
+_start:
+ ret
diff --git a/test/ELF/gc-sections.s b/test/ELF/gc-sections.s
index 93f7dc67023f..f100153ec358 100644
--- a/test/ELF/gc-sections.s
+++ b/test/ELF/gc-sections.s
@@ -14,13 +14,13 @@
# NOGC: Name: .dtors
# NOGC: Name: .init
# NOGC: Name: .fini
+# NOGC: Name: .debug_pubtypes
+# NOGC: Name: .comment
# NOGC: Name: a
# NOGC: Name: b
# NOGC: Name: c
# NOGC: Name: x
# NOGC: Name: y
-# NOGC: Name: __preinit_array_start
-# NOGC: Name: __preinit_array_end
# NOGC: Name: d
# GC1: Name: .eh_frame
@@ -29,13 +29,13 @@
# GC1: Name: .dtors
# GC1: Name: .init
# GC1: Name: .fini
+# GC1: Name: .debug_pubtypes
+# GC1: Name: .comment
# GC1: Name: a
# GC1: Name: b
# GC1: Name: c
# GC1-NOT: Name: x
# GC1-NOT: Name: y
-# GC1: Name: __preinit_array_start
-# GC1: Name: __preinit_array_end
# GC1-NOT: Name: d
# GC2: Name: .eh_frame
@@ -44,13 +44,13 @@
# GC2: Name: .dtors
# GC2: Name: .init
# GC2: Name: .fini
+# GC2: Name: .debug_pubtypes
+# GC2: Name: .comment
# GC2: Name: a
# GC2: Name: b
# GC2: Name: c
# GC2-NOT: Name: x
# GC2-NOT: Name: y
-# GC2: Name: __preinit_array_start
-# GC2: Name: __preinit_array_end
# GC2: Name: d
.globl _start, d
@@ -100,3 +100,9 @@ y:
.section .eh_frame,"a",@unwind
.quad 0
+
+.section .debug_pubtypes,"",@progbits
+ .quad 0
+
+.section .comment,"MS",@progbits,8
+ .quad 0
diff --git a/test/ELF/gdb-index.s b/test/ELF/gdb-index.s
new file mode 100644
index 000000000000..4ad5c13d3158
--- /dev/null
+++ b/test/ELF/gdb-index.s
@@ -0,0 +1,49 @@
+## gdb-index-a.elf and gdb-index-b.elf are a test.o and test2.o renamed,
+## were generated in this way:
+## test.cpp:
+## int main() { return 0; }
+## test2.cpp:
+## int main2() { return 0; }
+## Compiled with:
+## gcc -gsplit-dwarf -c test.cpp test2.cpp
+## gcc version 5.3.1 20160413
+## Info about gdb-index: https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html
+
+# REQUIRES: x86
+# RUN: ld.lld --gdb-index -e main %p/Inputs/gdb-index-a.elf %p/Inputs/gdb-index-b.elf -o %t
+# RUN: llvm-dwarfdump -debug-dump=gdb_index %t | FileCheck %s
+# RUN: llvm-objdump -d %t | FileCheck %s --check-prefix=DISASM
+
+# DISASM: Disassembly of section .text:
+# DISASM: main:
+# DISASM-CHECK: 11000: 55 pushq %rbp
+# DISASM-CHECK: 11001: 48 89 e5 movq %rsp, %rbp
+# DISASM-CHECK: 11004: b8 00 00 00 00 movl $0, %eax
+# DISASM-CHECK: 11009: 5d popq %rbp
+# DISASM-CHECK: 1100a: c3 retq
+# DISASM: _Z5main2v:
+# DISASM-CHECK: 1100b: 55 pushq %rbp
+# DISASM-CHECK: 1100c: 48 89 e5 movq %rsp, %rbp
+# DISASM-CHECK: 1100f: b8 00 00 00 00 movl $0, %eax
+# DISASM-CHECK: 11014: 5d popq %rbp
+# DISASM-CHECK: 11015: c3 retq
+
+# CHECK: .gnu_index contents:
+# CHECK-NEXT: Version = 7
+# CHECK: CU list offset = 0x18, has 2 entries:
+# CHECK-NEXT: 0: Offset = 0x0, Length = 0x34
+# CHECK-NEXT: 1: Offset = 0x34, Length = 0x34
+# CHECK: Address area offset = 0x38, has 2 entries:
+# CHECK-NEXT: Low address = 0x201000, High address = 0x20100b, CU index = 0
+# CHECK-NEXT: Low address = 0x20100b, High address = 0x201016, CU index = 1
+# CHECK: Symbol table offset = 0x60, size = 1024, filled slots:
+# CHECK-NEXT: 489: Name offset = 0x1d, CU vector offset = 0x0
+# CHECK-NEXT: String name: main, CU vector index: 0
+# CHECK-NEXT: 754: Name offset = 0x22, CU vector offset = 0x8
+# CHECK-NEXT: String name: int, CU vector index: 1
+# CHECK-NEXT: 956: Name offset = 0x26, CU vector offset = 0x14
+# CHECK-NEXT: String name: main2, CU vector index: 2
+# CHECK: Constant pool offset = 0x2060, has 3 CU vectors:
+# CHECK-NEXT: 0(0x0): 0x30000000
+# CHECK-NEXT: 1(0x8): 0x90000000 0x90000001
+# CHECK-NEXT: 2(0x14): 0x30000001
diff --git a/test/ELF/gnu-ifunc-gotpcrel.s b/test/ELF/gnu-ifunc-gotpcrel.s
index 08aa0af37a1b..2a5814a98bc2 100644
--- a/test/ELF/gnu-ifunc-gotpcrel.s
+++ b/test/ELF/gnu-ifunc-gotpcrel.s
@@ -6,7 +6,7 @@
# RUN: llvm-readobj -dyn-relocations %t | FileCheck %s
# CHECK: Dynamic Relocations {
-# CHECK-NEXT: 0x120B0 R_X86_64_GLOB_DAT foo 0x0
+# CHECK-NEXT: 0x2020B0 R_X86_64_GLOB_DAT foo 0x0
# CHECK-NEXT: }
.globl _start
diff --git a/test/ELF/gnu-ifunc-i386.s b/test/ELF/gnu-ifunc-i386.s
index bc2d0f9610b3..21f1313a9b05 100644
--- a/test/ELF/gnu-ifunc-i386.s
+++ b/test/ELF/gnu-ifunc-i386.s
@@ -15,15 +15,15 @@
// CHECK-NEXT: Address: [[RELA:.*]]
// CHECK-NEXT: Offset: 0xD4
// CHECK-NEXT: Size: 16
-// CHECK-NEXT: Link: 5
+// CHECK-NEXT: Link: 6
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 4
// CHECK-NEXT: EntrySize: 8
// CHECK-NEXT: }
// CHECK: Relocations [
// CHECK-NEXT: Section ({{.*}}) .rel.plt {
-// CHECK-NEXT: 0x1200C R_386_IRELATIVE
-// CHECK-NEXT: 0x12010 R_386_IRELATIVE
+// CHECK-NEXT: 0x12000 R_386_IRELATIVE
+// CHECK-NEXT: 0x12004 R_386_IRELATIVE
// CHECK-NEXT: }
// CHECK-NEXT: ]
@@ -88,30 +88,24 @@
// CHECK-NEXT: }
// CHECK-NEXT:]
-// DISASM: Disassembly of section .text:
+// DISASM: Disassembly of section .text:
// DISASM-NEXT: foo:
-// DISASM-NEXT: 11000: c3 retl
-// DISASM: bar:
-// DISASM-NEXT: 11001: c3 retl
+// DISASM-NEXT: 11000: c3 retl
+// DISASM: bar:
+// DISASM-NEXT: 11001: c3 retl
// DISASM: _start:
-// DISASM-NEXT: 11002: e8 29 00 00 00 calll 41
-// DISASM-NEXT: 11007: e8 34 00 00 00 calll 52
+// DISASM-NEXT: 11002: e8 19 00 00 00 calll 25
+// DISASM-NEXT: 11007: e8 24 00 00 00 calll 36
// DISASM-NEXT: 1100c: ba d4 00 01 00 movl $65748, %edx
// DISASM-NEXT: 11011: ba e4 00 01 00 movl $65764, %edx
// DISASM-NEXT: Disassembly of section .plt:
// DISASM-NEXT: .plt:
-// DISASM-NEXT: 11020: ff 35 04 20 01 00 pushl 73732
-// DISASM-NEXT: 11026: ff 25 08 20 01 00 jmpl *73736
-// DISASM-NEXT: 1102c: 90 nop
-// DISASM-NEXT: 1102d: 90 nop
-// DISASM-NEXT: 1102e: 90 nop
-// DISASM-NEXT: 1102f: 90 nop
-// DISASM-NEXT: 11030: ff 25 0c 20 01 00 jmpl *73740
-// DISASM-NEXT: 11036: 68 00 00 00 00 pushl $0
-// DISASM-NEXT: 1103b: e9 e0 ff ff ff jmp -32 <.plt>
-// DISASM-NEXT: 11040: ff 25 10 20 01 00 jmpl *73744
-// DISASM-NEXT: 11046: 68 08 00 00 00 pushl $8
-// DISASM-NEXT: 1104b: e9 d0 ff ff ff jmp -48 <.plt>
+// DISASM-NEXT: 11020: ff 25 00 20 01 00 jmpl *73728
+// DISASM-NEXT: 11026: 68 10 00 00 00 pushl $16
+// DISASM-NEXT: 1102b: e9 e0 ff ff ff jmp -32 <_start+0xE>
+// DISASM-NEXT: 11030: ff 25 04 20 01 00 jmpl *73732
+// DISASM-NEXT: 11036: 68 18 00 00 00 pushl $24
+// DISASM-NEXT: 1103b: e9 d0 ff ff ff jmp -48 <_start+0xE>
.text
.type foo STT_GNU_IFUNC
diff --git a/test/ELF/gnu-ifunc-plt-i386.s b/test/ELF/gnu-ifunc-plt-i386.s
new file mode 100644
index 000000000000..75ebc8dd76ef
--- /dev/null
+++ b/test/ELF/gnu-ifunc-plt-i386.s
@@ -0,0 +1,76 @@
+// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %S/Inputs/shared2-x86-64.s -o %t1.o
+// RUN: ld.lld %t1.o --shared -o %t.so
+// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o
+// RUN: ld.lld %t.so %t.o -o %tout
+// RUN: llvm-objdump -d %tout | FileCheck %s --check-prefix=DISASM
+// RUN: llvm-objdump -s %tout | FileCheck %s --check-prefix=GOTPLT
+// RUN: llvm-readobj -r -dynamic-table %tout | FileCheck %s
+// REQUIRES: x86
+
+// Check that the IRELATIVE relocations are after the JUMP_SLOT in the plt
+// CHECK: Relocations [
+// CHECK-NEXT: Section (4) .rel.plt {
+// CHECK-NEXT: 0x1300C R_386_JUMP_SLOT bar2
+// CHECK-NEXT: 0x13010 R_386_JUMP_SLOT zed2
+// CHECK-NEXT: 0x13014 R_386_IRELATIVE
+// CHECK-NEXT: 0x13018 R_386_IRELATIVE
+
+// Check that IRELATIVE .got.plt entries point to ifunc resolver and not
+// back to the plt entry + 6.
+// GOTPLT: Contents of section .got.plt:
+// GOTPLT: 13000 00200100 00000000 00000000 36100100
+// GOTPLT-NEXT: 13010 46100100 00100100 01100100
+
+// Check that the PLTRELSZ tag includes the IRELATIVE relocations
+// CHECK: DynamicSection [
+// CHECK: 0x00000002 PLTRELSZ 32 (bytes)
+
+// Check that a PLT header is written and the ifunc entries appear last
+// DISASM: Disassembly of section .text:
+// DISASM-NEXT: foo:
+// DISASM-NEXT: 11000: c3 retl
+// DISASM: bar:
+// DISASM-NEXT: 11001: c3 retl
+// DISASM: _start:
+// DISASM-NEXT: 11002: e8 49 00 00 00 calll 73
+// DISASM-NEXT: 11007: e8 54 00 00 00 calll 84
+// DISASM-NEXT: 1100c: e8 1f 00 00 00 calll 31
+// DISASM-NEXT: 11011: e8 2a 00 00 00 calll 42
+// DISASM-NEXT: Disassembly of section .plt:
+// DISASM-NEXT: .plt:
+// DISASM-NEXT: 11020: ff 35 04 30 01 00 pushl 77828
+// DISASM-NEXT: 11026: ff 25 08 30 01 00 jmpl *77832
+// DISASM-NEXT: 1102c: 90 nop
+// DISASM-NEXT: 1102d: 90 nop
+// DISASM-NEXT: 1102e: 90 nop
+// DISASM-NEXT: 1102f: 90 nop
+// DISASM-NEXT: 11030: ff 25 0c 30 01 00 jmpl *77836
+// DISASM-NEXT: 11036: 68 00 00 00 00 pushl $0
+// DISASM-NEXT: 1103b: e9 e0 ff ff ff jmp -32 <.plt>
+// DISASM-NEXT: 11040: ff 25 10 30 01 00 jmpl *77840
+// DISASM-NEXT: 11046: 68 08 00 00 00 pushl $8
+// DISASM-NEXT: 1104b: e9 d0 ff ff ff jmp -48 <.plt>
+// DISASM-NEXT: 11050: ff 25 14 30 01 00 jmpl *77844
+// DISASM-NEXT: 11056: 68 30 00 00 00 pushl $48
+// DISASM-NEXT: 1105b: e9 e0 ff ff ff jmp -32 <.plt+0x20>
+// DISASM-NEXT: 11060: ff 25 18 30 01 00 jmpl *77848
+// DISASM-NEXT: 11066: 68 38 00 00 00 pushl $56
+// DISASM-NEXT: 1106b: e9 d0 ff ff ff jmp -48 <.plt+0x20>
+
+.text
+.type foo STT_GNU_IFUNC
+.globl foo
+foo:
+ ret
+
+.type bar STT_GNU_IFUNC
+.globl bar
+bar:
+ ret
+
+.globl _start
+_start:
+ call foo
+ call bar
+ call bar2
+ call zed2
diff --git a/test/ELF/gnu-ifunc-plt.s b/test/ELF/gnu-ifunc-plt.s
new file mode 100644
index 000000000000..c7bd6b67b115
--- /dev/null
+++ b/test/ELF/gnu-ifunc-plt.s
@@ -0,0 +1,74 @@
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/shared2-x86-64.s -o %t1.o
+// RUN: ld.lld %t1.o --shared -o %t.so
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+// RUN: ld.lld %t.so %t.o -o %tout
+// RUN: llvm-objdump -d %tout | FileCheck %s --check-prefix=DISASM
+// RUN: llvm-objdump -s %tout | FileCheck %s --check-prefix=GOTPLT
+// RUN: llvm-readobj -r -dynamic-table %tout | FileCheck %s
+// REQUIRES: x86
+
+// Check that the IRELATIVE relocations are after the JUMP_SLOT in the plt
+// CHECK: Relocations [
+// CHECK-NEXT: Section (4) .rela.plt {
+// CHECK-NEXT: 0x203018 R_X86_64_JUMP_SLOT bar2 0x0
+// CHECK-NEXT: 0x203020 R_X86_64_JUMP_SLOT zed2 0x0
+// CHECK-NEXT: 0x203028 R_X86_64_IRELATIVE - 0x201000
+// CHECK-NEXT: 0x203030 R_X86_64_IRELATIVE - 0x201001
+
+// Check that .got.plt entries point back to PLT header
+// GOTPLT: Contents of section .got.plt:
+// GOTPLT-NEXT: 203000 00202000 00000000 00000000 00000000 . .............
+// GOTPLT-NEXT: 203010 00000000 00000000 36102000 00000000 ........6. .....
+// GOTPLT-NEXT: 203020 46102000 00000000 56102000 00000000 F. .....V. .....
+// GOTPLT-NEXT: 203030 66102000 00000000
+
+// Check that the PLTRELSZ tag includes the IRELATIVE relocations
+// CHECK: DynamicSection [
+// CHECK: 0x0000000000000002 PLTRELSZ 96 (bytes)
+
+// Check that a PLT header is written and the ifunc entries appear last
+// DISASM: Disassembly of section .text:
+// DISASM-NEXT: foo:
+// DISASM-NEXT: 201000: c3 retq
+// DISASM: bar:
+// DISASM-NEXT: 201001: c3 retq
+// DISASM: _start:
+// DISASM-NEXT: 201002: e8 49 00 00 00 callq 73
+// DISASM-NEXT: 201007: e8 54 00 00 00 callq 84
+// DISASM-NEXT: 20100c: e8 1f 00 00 00 callq 31
+// DISASM-NEXT: 201011: e8 2a 00 00 00 callq 42
+// DISASM-NEXT: Disassembly of section .plt:
+// DISASM-NEXT: .plt:
+// DISASM-NEXT: 201020: ff 35 e2 1f 00 00 pushq 8162(%rip)
+// DISASM-NEXT: 201026: ff 25 e4 1f 00 00 jmpq *8164(%rip)
+// DISASM-NEXT: 20102c: 0f 1f 40 00 nopl (%rax)
+// DISASM-NEXT: 201030: ff 25 e2 1f 00 00 jmpq *8162(%rip)
+// DISASM-NEXT: 201036: 68 00 00 00 00 pushq $0
+// DISASM-NEXT: 20103b: e9 e0 ff ff ff jmp -32 <.plt>
+// DISASM-NEXT: 201040: ff 25 da 1f 00 00 jmpq *8154(%rip)
+// DISASM-NEXT: 201046: 68 01 00 00 00 pushq $1
+// DISASM-NEXT: 20104b: e9 d0 ff ff ff jmp -48 <.plt>
+// DISASM-NEXT: 201050: ff 25 d2 1f 00 00 jmpq *8146(%rip)
+// DISASM-NEXT: 201056: 68 00 00 00 00 pushq $0
+// DISASM-NEXT: 20105b: e9 e0 ff ff ff jmp -32 <.plt+0x20>
+// DISASM-NEXT: 201060: ff 25 ca 1f 00 00 jmpq *8138(%rip)
+// DISASM-NEXT: 201066: 68 01 00 00 00 pushq $1
+// DISASM-NEXT: 20106b: e9 d0 ff ff ff jmp -48 <.plt+0x20>
+
+.text
+.type foo STT_GNU_IFUNC
+.globl foo
+foo:
+ ret
+
+.type bar STT_GNU_IFUNC
+.globl bar
+bar:
+ ret
+
+.globl _start
+_start:
+ call foo
+ call bar
+ call bar2
+ call zed2
diff --git a/test/ELF/gnu-ifunc-shared.s b/test/ELF/gnu-ifunc-shared.s
new file mode 100644
index 000000000000..30142b6dfe59
--- /dev/null
+++ b/test/ELF/gnu-ifunc-shared.s
@@ -0,0 +1,66 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+// RUN: ld.lld --shared -o %t.so %t.o
+// RUN: llvm-objdump -d %t.so | FileCheck %s --check-prefix=DISASM
+// RUN: llvm-readobj -r %t.so | FileCheck %s
+
+// Check that an IRELATIVE relocation is used for a non-preemptible ifunc
+// handler and a JUMP_SLOT is used for a preemptible ifunc
+// DISASM: Disassembly of section .text:
+// DISASM-NEXT: fct:
+// DISASM-NEXT: 1000: c3 retq
+// DISASM: fct2:
+// DISASM-NEXT: 1001: c3 retq
+// DISASM: f1:
+// DISASM-NEXT: 1002: e8 49 00 00 00 callq 73
+// DISASM-NEXT: 1007: e8 24 00 00 00 callq 36
+// DISASM-NEXT: 100c: e8 2f 00 00 00 callq 47
+// DISASM-NEXT: 1011: c3 retq
+// DISASM: f2:
+// DISASM-NEXT: 1012: c3 retq
+// DISASM-NEXT: Disassembly of section .plt:
+// DISASM-NEXT: .plt:
+// DISASM-NEXT: 1020: ff 35 e2 1f 00 00 pushq 8162(%rip)
+// DISASM-NEXT: 1026: ff 25 e4 1f 00 00 jmpq *8164(%rip)
+// DISASM-NEXT: 102c: 0f 1f 40 00 nopl (%rax)
+// DISASM-NEXT: 1030: ff 25 e2 1f 00 00 jmpq *8162(%rip)
+// DISASM-NEXT: 1036: 68 00 00 00 00 pushq $0
+// DISASM-NEXT: 103b: e9 e0 ff ff ff jmp -32 <.plt>
+// DISASM-NEXT: 1040: ff 25 da 1f 00 00 jmpq *8154(%rip)
+// DISASM-NEXT: 1046: 68 01 00 00 00 pushq $1
+// DISASM-NEXT: 104b: e9 d0 ff ff ff jmp -48 <.plt>
+// DISASM-NEXT: 1050: ff 25 d2 1f 00 00 jmpq *8146(%rip)
+// DISASM-NEXT: 1056: 68 00 00 00 00 pushq $0
+// DISASM-NEXT: 105b: e9 e0 ff ff ff jmp -32 <.plt+0x20>
+
+// CHECK: Relocations [
+// CHECK-NEXT: Section (4) .rela.plt {
+// CHECK-NEXT: 0x3018 R_X86_64_JUMP_SLOT fct2 0x0
+// CHECK-NEXT: 0x3020 R_X86_64_JUMP_SLOT f2 0x0
+// CHECK-NEXT: 0x3028 R_X86_64_IRELATIVE - 0x1000
+
+ // Hidden expect IRELATIVE
+ .globl fct
+ .hidden fct
+ .type fct, STT_GNU_IFUNC
+fct:
+ ret
+
+ // Not hidden expect JUMP_SLOT
+ .globl fct2
+ .type fct2, STT_GNU_IFUNC
+fct2:
+ ret
+
+ .globl f1
+ .type f1, @function
+f1:
+ call fct
+ call fct2
+ call f2@PLT
+ ret
+
+ .globl f2
+ .type f2, @function
+f2:
+ ret
diff --git a/test/ELF/gnu-ifunc.s b/test/ELF/gnu-ifunc.s
index 5336c89a563b..f86f0300baaf 100644
--- a/test/ELF/gnu-ifunc.s
+++ b/test/ELF/gnu-ifunc.s
@@ -15,15 +15,15 @@
// CHECK-NEXT: Address: [[RELA:.*]]
// CHECK-NEXT: Offset: 0x158
// CHECK-NEXT: Size: 48
-// CHECK-NEXT: Link: 5
+// CHECK-NEXT: Link: 6
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 8
// CHECK-NEXT: EntrySize: 24
// CHECK-NEXT: }
// CHECK: Relocations [
// CHECK-NEXT: Section ({{.*}}) .rela.plt {
-// CHECK-NEXT: 0x12018 R_X86_64_IRELATIVE
-// CHECK-NEXT: 0x12020 R_X86_64_IRELATIVE
+// CHECK-NEXT: 0x202000 R_X86_64_IRELATIVE
+// CHECK-NEXT: 0x202008 R_X86_64_IRELATIVE
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK: Symbols [
@@ -38,7 +38,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: __rela_iplt_end
-// CHECK-NEXT: Value: 0x10188
+// CHECK-NEXT: Value: 0x200188
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -60,7 +60,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: _start
-// CHECK-NEXT: Value: 0x11002
+// CHECK-NEXT: Value: 0x201002
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: None
@@ -69,7 +69,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: bar
-// CHECK-NEXT: Value: 0x11001
+// CHECK-NEXT: Value: 0x201001
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: GNU_IFunc
@@ -78,7 +78,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: foo
-// CHECK-NEXT: Value: 0x11000
+// CHECK-NEXT: Value: 0x201000
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: GNU_IFunc
@@ -87,28 +87,25 @@
// CHECK-NEXT: }
// CHECK-NEXT: ]
-// DISASM: Disassembly of section .text:
+// DISASM: Disassembly of section .text:
// DISASM-NEXT: foo:
-// DISASM-NEXT: 11000: c3 retq
+// DISASM-NEXT: 201000: {{.*}} retq
// DISASM: bar:
-// DISASM-NEXT: 11001: c3 retq
+// DISASM-NEXT: 201001: {{.*}} retq
// DISASM: _start:
-// DISASM-NEXT: 11002: e8 29 00 00 00 callq 41
-// DISASM-NEXT: 11007: e8 34 00 00 00 callq 52
-// DISASM-NEXT: 1100c: ba 58 01 01 00 movl $65880, %edx
-// DISASM-NEXT: 11011: ba 88 01 01 00 movl $65928, %edx
-// DISASM-NEXT: 11016: ba 89 01 01 00 movl $65929, %edx
+// DISASM-NEXT: 201002: {{.*}} callq 25
+// DISASM-NEXT: 201007: {{.*}} callq 36
+// DISASM-NEXT: 20100c: {{.*}} movl $2097496, %edx
+// DISASM-NEXT: 201011: {{.*}} movl $2097544, %edx
+// DISASM-NEXT: 201016: {{.*}} movl $2097545, %edx
// DISASM-NEXT: Disassembly of section .plt:
// DISASM-NEXT: .plt:
-// DISASM-NEXT: 11020: ff 35 e2 0f 00 00 pushq 4066(%rip)
-// DISASM-NEXT: 11026: ff 25 e4 0f 00 00 jmpq *4068(%rip)
-// DISASM-NEXT: 1102c: 0f 1f 40 00 nopl (%rax)
-// DISASM-NEXT: 11030: ff 25 e2 0f 00 00 jmpq *4066(%rip)
-// DISASM-NEXT: 11036: 68 00 00 00 00 pushq $0
-// DISASM-NEXT: 1103b: e9 e0 ff ff ff jmp -32
-// DISASM-NEXT: 11040: ff 25 da 0f 00 00 jmpq *4058(%rip)
-// DISASM-NEXT: 11046: 68 01 00 00 00 pushq $1
-// DISASM-NEXT: 1104b: e9 d0 ff ff ff jmp -48
+// DISASM-NEXT: 201020: {{.*}} jmpq *4058(%rip)
+// DISASM-NEXT: 201026: {{.*}} pushq $0
+// DISASM-NEXT: 20102b: {{.*}} jmp -32 <_start+0xE>
+// DISASM-NEXT: 201030: {{.*}} jmpq *4050(%rip)
+// DISASM-NEXT: 201036: {{.*}} pushq $1
+// DISASM-NEXT: 20103b: {{.*}} jmp -48 <_start+0xE>
.text
.type foo STT_GNU_IFUNC
diff --git a/test/ELF/got-aarch64.s b/test/ELF/got-aarch64.s
index b4aa456b4e81..8f39d5a4804f 100644
--- a/test/ELF/got-aarch64.s
+++ b/test/ELF/got-aarch64.s
@@ -10,7 +10,7 @@
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x2090
+// CHECK-NEXT: Address: 0x20090
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 8
// CHECK-NEXT: Link: 0
@@ -19,16 +19,16 @@
// CHECK: Relocations [
// CHECK-NEXT: Section ({{.*}}) .rela.dyn {
-// CHECK-NEXT: 0x2090 R_AARCH64_GLOB_DAT dat 0x0
+// CHECK-NEXT: 0x20090 R_AARCH64_GLOB_DAT dat 0x0
// CHECK-NEXT: }
// CHECK-NEXT: ]
-// Page(0x2098) - Page(0x1000) = 0x1000 = 4096
-// 0x2098 & 0xff8 = 0x98 = 152
+// Page(0x20098) - Page(0x10000) = 0x10000 = 65536
+// 0x20098 & 0xff8 = 0x98 = 152
// DISASM: main:
-// DISASM-NEXT: 1000: {{.*}} adrp x0, #4096
-// DISASM-NEXT: 1004: {{.*}} ldr x0, [x0, #144]
+// DISASM-NEXT: 10000: 80 00 00 90 adrp x0, #65536
+// DISASM-NEXT: 10004: 00 48 40 f9 ldr x0, [x0, #144]
.global main,foo,dat
.text
diff --git a/test/ELF/got.s b/test/ELF/got.s
index 600377ded2f7..57c1baddfd0c 100644
--- a/test/ELF/got.s
+++ b/test/ELF/got.s
@@ -12,7 +12,7 @@
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x120B0
+// CHECK-NEXT: Address: 0x2020B0
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 16
// CHECK-NEXT: Link: 0
@@ -21,22 +21,22 @@
// CHECK: Relocations [
// CHECK-NEXT: Section ({{.*}}) .rela.dyn {
-// CHECK-NEXT: 0x120B0 R_X86_64_GLOB_DAT bar 0x0
-// CHECK-NEXT: 0x120B8 R_X86_64_GLOB_DAT zed 0x0
+// CHECK-NEXT: 0x2020B0 R_X86_64_GLOB_DAT bar 0x0
+// CHECK-NEXT: 0x2020B8 R_X86_64_GLOB_DAT zed 0x0
// CHECK-NEXT: }
// CHECK-NEXT: ]
// Unfortunately FileCheck can't do math, so we have to check for explicit
// values:
-// 0x120B0 - (0x11000 + 2) - 4 = 4266
-// 0x120B0 - (0x11006 + 2) - 4 = 4260
-// 0x120A8 - (0x1100c + 2) - 4 = 4262
+// 0x2020B0 - (0x201000 + 2) - 4 = 4266
+// 0x2020B0 - (0x201006 + 2) - 4 = 4260
+// 0x2020A8 - (0x20100c + 2) - 4 = 4262
// DISASM: _start:
-// DISASM-NEXT: 11000: {{.*}} jmpq *4266(%rip)
-// DISASM-NEXT: 11006: {{.*}} jmpq *4260(%rip)
-// DISASM-NEXT: 1100c: {{.*}} jmpq *4262(%rip)
+// DISASM-NEXT: 201000: {{.*}} jmpq *4266(%rip)
+// DISASM-NEXT: 201006: {{.*}} jmpq *4260(%rip)
+// DISASM-NEXT: 20100c: {{.*}} jmpq *4262(%rip)
.global _start
_start:
diff --git a/test/ELF/gotpc-relax-nopic.s b/test/ELF/gotpc-relax-nopic.s
index 278173557837..3c7a4012a7d9 100644
--- a/test/ELF/gotpc-relax-nopic.s
+++ b/test/ELF/gotpc-relax-nopic.s
@@ -10,23 +10,23 @@
# SYMRELOC: Symbols [
# SYMRELOC: Symbol {
# SYMRELOC: Name: bar
-# SYMRELOC-NEXT: Value: 0x12000
+# SYMRELOC-NEXT: Value: 0x202000
-## 73728 = 0x12000 (bar)
+## 2105344 = 0x202000 (bar)
# DISASM: Disassembly of section .text:
# DISASM-NEXT: _start:
-# DISASM-NEXT: 11000: 48 81 d0 00 20 01 00 adcq $73728, %rax
-# DISASM-NEXT: 11007: 48 81 c3 00 20 01 00 addq $73728, %rbx
-# DISASM-NEXT: 1100e: 48 81 e1 00 20 01 00 andq $73728, %rcx
-# DISASM-NEXT: 11015: 48 81 fa 00 20 01 00 cmpq $73728, %rdx
-# DISASM-NEXT: 1101c: 48 81 cf 00 20 01 00 orq $73728, %rdi
-# DISASM-NEXT: 11023: 48 81 de 00 20 01 00 sbbq $73728, %rsi
-# DISASM-NEXT: 1102a: 48 81 ed 00 20 01 00 subq $73728, %rbp
-# DISASM-NEXT: 11031: 49 81 f0 00 20 01 00 xorq $73728, %r8
-# DISASM-NEXT: 11038: 49 f7 c7 00 20 01 00 testq $73728, %r15
+# DISASM-NEXT: 201000: {{.*}} adcq $2105344, %rax
+# DISASM-NEXT: 201007: {{.*}} addq $2105344, %rbx
+# DISASM-NEXT: 20100e: {{.*}} andq $2105344, %rcx
+# DISASM-NEXT: 201015: {{.*}} cmpq $2105344, %rdx
+# DISASM-NEXT: 20101c: {{.*}} orq $2105344, %rdi
+# DISASM-NEXT: 201023: {{.*}} sbbq $2105344, %rsi
+# DISASM-NEXT: 20102a: {{.*}} subq $2105344, %rbp
+# DISASM-NEXT: 201031: {{.*}} xorq $2105344, %r8
+# DISASM-NEXT: 201038: {{.*}} testq $2105344, %r15
# RUN: ld.lld -shared %t.o -o %t2
-# RUN: llvm-readobj -s %t2 | FileCheck --check-prefix=SEC-PIC %s
+# RUN: llvm-readobj -s -r -d %t2 | FileCheck --check-prefix=SEC-PIC %s
# RUN: llvm-objdump -d %t2 | FileCheck --check-prefix=DISASM-PIC %s
# SEC-PIC: Section {
# SEC-PIC: Index:
@@ -36,29 +36,35 @@
# SEC-PIC-NEXT: SHF_ALLOC
# SEC-PIC-NEXT: SHF_WRITE
# SEC-PIC-NEXT: ]
-# SEC-PIC-NEXT: Address: 0x2090
-# SEC-PIC-NEXT: Offset: 0x2090
+# SEC-PIC-NEXT: Address: 0x20A0
+# SEC-PIC-NEXT: Offset: 0x20A0
# SEC-PIC-NEXT: Size: 8
# SEC-PIC-NEXT: Link:
# SEC-PIC-NEXT: Info:
# SEC-PIC-NEXT: AddressAlignment:
# SEC-PIC-NEXT: EntrySize:
# SEC-PIC-NEXT: }
+# SEC-PIC: Relocations [
+# SEC-PIC-NEXT: Section ({{.*}}) .rela.dyn {
+# SEC-PIC-NEXT: 0x20A0 R_X86_64_RELATIVE - 0x3000
+# SEC-PIC-NEXT: }
+# SEC-PIC-NEXT: ]
+# SEC-PIC: 0x000000006FFFFFF9 RELACOUNT 1
## Check that there was no relaxation performed. All values refer to got entry.
-## Ex: 0x1000 + 4233 + 7 = 0x2090
-## 0x102a + 4191 + 7 = 0x2090
+## Ex: 0x1000 + 4249 + 7 = 0x20A0
+## 0x102a + 4207 + 7 = 0x20A0
# DISASM-PIC: Disassembly of section .text:
# DISASM-PIC-NEXT: _start:
-# DISASM-PIC-NEXT: 1000: 48 13 05 89 10 00 00 adcq 4233(%rip), %rax
-# DISASM-PIC-NEXT: 1007: 48 03 1d 82 10 00 00 addq 4226(%rip), %rbx
-# DISASM-PIC-NEXT: 100e: 48 23 0d 7b 10 00 00 andq 4219(%rip), %rcx
-# DISASM-PIC-NEXT: 1015: 48 3b 15 74 10 00 00 cmpq 4212(%rip), %rdx
-# DISASM-PIC-NEXT: 101c: 48 0b 3d 6d 10 00 00 orq 4205(%rip), %rdi
-# DISASM-PIC-NEXT: 1023: 48 1b 35 66 10 00 00 sbbq 4198(%rip), %rsi
-# DISASM-PIC-NEXT: 102a: 48 2b 2d 5f 10 00 00 subq 4191(%rip), %rbp
-# DISASM-PIC-NEXT: 1031: 4c 33 05 58 10 00 00 xorq 4184(%rip), %r8
-# DISASM-PIC-NEXT: 1038: 4c 85 3d 51 10 00 00 testq 4177(%rip), %r15
+# DISASM-PIC-NEXT: 1000: {{.*}} adcq 4249(%rip), %rax
+# DISASM-PIC-NEXT: 1007: {{.*}} addq 4242(%rip), %rbx
+# DISASM-PIC-NEXT: 100e: {{.*}} andq 4235(%rip), %rcx
+# DISASM-PIC-NEXT: 1015: {{.*}} cmpq 4228(%rip), %rdx
+# DISASM-PIC-NEXT: 101c: {{.*}} orq 4221(%rip), %rdi
+# DISASM-PIC-NEXT: 1023: {{.*}} sbbq 4214(%rip), %rsi
+# DISASM-PIC-NEXT: 102a: {{.*}} subq 4207(%rip), %rbp
+# DISASM-PIC-NEXT: 1031: {{.*}} xorq 4200(%rip), %r8
+# DISASM-PIC-NEXT: 1038: {{.*}} testq 4193(%rip), %r15
.data
.type bar, @object
diff --git a/test/ELF/gotpc-relax.s b/test/ELF/gotpc-relax.s
index 422e10439d00..3d2f2ab578e8 100644
--- a/test/ELF/gotpc-relax.s
+++ b/test/ELF/gotpc-relax.s
@@ -8,17 +8,17 @@
# RELOC: Relocations [
# RELOC: ]
-# 0x11003 + 7 - 10 = 0x11000
-# 0x1100a + 7 - 17 = 0x11000
-# 0x11011 + 7 - 23 = 0x11001
-# 0x11018 + 7 - 30 = 0x11001
+# 0x201003 + 7 - 10 = 0x201000
+# 0x20100a + 7 - 17 = 0x201000
+# 0x201011 + 7 - 23 = 0x201001
+# 0x201018 + 7 - 30 = 0x201001
# DISASM: Disassembly of section .text:
# DISASM-NEXT: foo:
-# DISASM-NEXT: 11000: 90 nop
+# DISASM-NEXT: 201000: 90 nop
# DISASM: hid:
-# DISASM-NEXT: 11001: 90 nop
+# DISASM-NEXT: 201001: 90 nop
# DISASM: ifunc:
-# DISASM-NEXT: 11002: c3 retq
+# DISASM-NEXT: 201002: c3 retq
# DISASM: _start:
# DISASM-NEXT: leaq -10(%rip), %rax
# DISASM-NEXT: leaq -17(%rip), %rax
diff --git a/test/ELF/i386-gotoff-shared.s b/test/ELF/i386-gotoff-shared.s
new file mode 100644
index 000000000000..01242ad01c35
--- /dev/null
+++ b/test/ELF/i386-gotoff-shared.s
@@ -0,0 +1,23 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o
+// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: llvm-readobj -s %t.so | FileCheck %s
+// RUN: llvm-objdump -d %t.so | FileCheck --check-prefix=DISASM %s
+
+bar:
+ movl bar@GOTOFF(%ebx), %eax
+ mov bar@GOT, %eax
+
+// CHECK: Name: .got
+// CHECK-NEXT: Type: SHT_PROGBITS
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: SHF_WRITE
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x2050
+// CHECK-NEXT: Offset: 0x2050
+// CHECK-NEXT: Size: 4
+
+// 0x1000 - (0x2050 + 4) = -4180
+
+// DISASM: 1000: {{.*}} movl -4180(%ebx), %eax
diff --git a/test/ELF/i386-gotpc.s b/test/ELF/i386-gotpc.s
index 14c2fcbec064..8222effd6655 100644
--- a/test/ELF/i386-gotpc.s
+++ b/test/ELF/i386-gotpc.s
@@ -6,7 +6,7 @@
movl $_GLOBAL_OFFSET_TABLE_, %eax
-// CHECK: Name: .got (38)
+// CHECK: Name: .got
// CHECK-NEXT: Type: SHT_PROGBITS
// CHECK-NEXT: Flags [
// CHECK-NEXT: SHF_ALLOC
diff --git a/test/ELF/i386-pc16.test b/test/ELF/i386-pc16.test
new file mode 100644
index 000000000000..46c54ebb1510
--- /dev/null
+++ b/test/ELF/i386-pc16.test
@@ -0,0 +1,40 @@
+# REQUIRES: x86
+
+# RUN: yaml2obj %s -o %t.o
+# RUN: ld.lld %t.o -o %t.exe
+# RUN: llvm-objdump -s -section=.text %t.exe 2>&1 | FileCheck %s
+
+# CHECK: Contents of section .text:
+# CHECK-NEXT: 11000 56441111 52341111
+
+!ELF
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_386
+Sections:
+ - Type: SHT_PROGBITS
+ Name: .text
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x04
+ Content: "1111111111111111"
+ - Type: SHT_REL
+ Name: .rel.text
+ Link: .symtab
+ Info: .text
+ AddressAlign: 0x04
+ Relocations:
+ - Offset: 0
+ Symbol: _start
+ Type: R_386_16
+ - Offset: 4
+ Symbol: _start
+ Type: R_386_PC16
+Symbols:
+ Global:
+ - Name: _start
+ Type: STT_FUNC
+ Section: .text
+ Value: 0x12345
+ Size: 4
diff --git a/test/ELF/i386-tls-got.s b/test/ELF/i386-tls-got.s
new file mode 100644
index 000000000000..56be4a138d9b
--- /dev/null
+++ b/test/ELF/i386-tls-got.s
@@ -0,0 +1,7 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=i386-pc-linux %S/Inputs/i386-tls-got.s -o %t1.o
+# RUN: ld.lld %t1.o -o %t1.so -shared
+# RUN: llvm-mc -filetype=obj -triple=i386-pc-linux %s -o %t2.o
+# RUN: ld.lld %t2.o %t1.so -o %t
+
+ addl foobar@INDNTPOFF, %eax
diff --git a/test/ELF/i386-tls-ie-shared.s b/test/ELF/i386-tls-ie-shared.s
index a2c25e2d2f81..8becc3199f95 100644
--- a/test/ELF/i386-tls-ie-shared.s
+++ b/test/ELF/i386-tls-ie-shared.s
@@ -2,7 +2,7 @@
// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %p/Inputs/tls-opt-iele-i686-nopic.s -o %tso.o
// RUN: ld.lld -shared %tso.o -o %tso
// RUN: ld.lld -shared %t.o %tso -o %t1
-// RUN: llvm-readobj -s -r %t1 | FileCheck --check-prefix=GOTRELSHARED %s
+// RUN: llvm-readobj -s -r -d %t1 | FileCheck --check-prefix=GOTRELSHARED %s
// RUN: llvm-objdump -d %t1 | FileCheck --check-prefix=DISASMSHARED %s
// GOTRELSHARED: Section {
@@ -13,8 +13,8 @@
// GOTRELSHARED-NEXT: SHF_ALLOC
// GOTRELSHARED-NEXT: SHF_WRITE
// GOTRELSHARED-NEXT: ]
-// GOTRELSHARED-NEXT: Address: 0x1050
-// GOTRELSHARED-NEXT: Offset: 0x1050
+// GOTRELSHARED-NEXT: Address: 0x1058
+// GOTRELSHARED-NEXT: Offset: 0x1058
// GOTRELSHARED-NEXT: Size: 16
// GOTRELSHARED-NEXT: Link: 0
// GOTRELSHARED-NEXT: Info: 0
@@ -31,12 +31,13 @@
// GOTRELSHARED-NEXT: 0x202D R_386_RELATIVE - 0x0
// GOTRELSHARED-NEXT: 0x2036 R_386_RELATIVE - 0x0
// GOTRELSHARED-NEXT: 0x203F R_386_RELATIVE - 0x0
-// GOTRELSHARED-NEXT: 0x1050 R_386_TLS_TPOFF tlslocal0 0x0
-// GOTRELSHARED-NEXT: 0x1054 R_386_TLS_TPOFF tlslocal1 0x0
-// GOTRELSHARED-NEXT: 0x1058 R_386_TLS_TPOFF tlsshared0 0x0
-// GOTRELSHARED-NEXT: 0x105C R_386_TLS_TPOFF tlsshared1 0x0
+// GOTRELSHARED-NEXT: 0x1058 R_386_TLS_TPOFF tlslocal0 0x0
+// GOTRELSHARED-NEXT: 0x105C R_386_TLS_TPOFF tlslocal1 0x0
+// GOTRELSHARED-NEXT: 0x1060 R_386_TLS_TPOFF tlsshared0 0x0
+// GOTRELSHARED-NEXT: 0x1064 R_386_TLS_TPOFF tlsshared1 0x0
// GOTRELSHARED-NEXT: }
// GOTRELSHARED-NEXT: ]
+// GOTRELSHARED: 0x6FFFFFFA RELCOUNT 8
// DISASMSHARED: Disassembly of section test:
// DISASMSHARED-NEXT: _start:
@@ -44,22 +45,22 @@
// (.got)[1] = 0x2054 = 8276
// (.got)[2] = 0x2058 = 8280
// (.got)[3] = 0x205C = 8284
-// DISASMSHARED-NEXT: 2000: 8b 0d 50 10 00 00 movl 4176, %ecx
-// DISASMSHARED-NEXT: 2006: 65 8b 01 movl %gs:(%ecx), %eax
-// DISASMSHARED-NEXT: 2009: a1 50 10 00 00 movl 4176, %eax
-// DISASMSHARED-NEXT: 200e: 65 8b 00 movl %gs:(%eax), %eax
-// DISASMSHARED-NEXT: 2011: 03 0d 50 10 00 00 addl 4176, %ecx
-// DISASMSHARED-NEXT: 2017: 65 8b 01 movl %gs:(%ecx), %eax
-// DISASMSHARED-NEXT: 201a: 8b 0d 54 10 00 00 movl 4180, %ecx
-// DISASMSHARED-NEXT: 2020: 65 8b 01 movl %gs:(%ecx), %eax
-// DISASMSHARED-NEXT: 2023: a1 54 10 00 00 movl 4180, %eax
-// DISASMSHARED-NEXT: 2028: 65 8b 00 movl %gs:(%eax), %eax
-// DISASMSHARED-NEXT: 202b: 03 0d 54 10 00 00 addl 4180, %ecx
-// DISASMSHARED-NEXT: 2031: 65 8b 01 movl %gs:(%ecx), %eax
-// DISASMSHARED-NEXT: 2034: 8b 0d 58 10 00 00 movl 4184, %ecx
-// DISASMSHARED-NEXT: 203a: 65 8b 01 movl %gs:(%ecx), %eax
-// DISASMSHARED-NEXT: 203d: 03 0d 5c 10 00 00 addl 4188, %ecx
-// DISASMSHARED-NEXT: 2043: 65 8b 01 movl %gs:(%ecx), %eax
+// DISASMSHARED-NEXT: 2000: 8b 0d 58 10 00 00 movl 4184, %ecx
+// DISASMSHARED-NEXT: 2006: 65 8b 01 movl %gs:(%ecx), %eax
+// DISASMSHARED-NEXT: 2009: a1 58 10 00 00 movl 4184, %eax
+// DISASMSHARED-NEXT: 200e: 65 8b 00 movl %gs:(%eax), %eax
+// DISASMSHARED-NEXT: 2011: 03 0d 58 10 00 00 addl 4184, %ecx
+// DISASMSHARED-NEXT: 2017: 65 8b 01 movl %gs:(%ecx), %eax
+// DISASMSHARED-NEXT: 201a: 8b 0d 5c 10 00 00 movl 4188, %ecx
+// DISASMSHARED-NEXT: 2020: 65 8b 01 movl %gs:(%ecx), %eax
+// DISASMSHARED-NEXT: 2023: a1 5c 10 00 00 movl 4188, %eax
+// DISASMSHARED-NEXT: 2028: 65 8b 00 movl %gs:(%eax), %eax
+// DISASMSHARED-NEXT: 202b: 03 0d 5c 10 00 00 addl 4188, %ecx
+// DISASMSHARED-NEXT: 2031: 65 8b 01 movl %gs:(%ecx), %eax
+// DISASMSHARED-NEXT: 2034: 8b 0d 60 10 00 00 movl 4192, %ecx
+// DISASMSHARED-NEXT: 203a: 65 8b 01 movl %gs:(%ecx), %eax
+// DISASMSHARED-NEXT: 203d: 03 0d 64 10 00 00 addl 4196, %ecx
+// DISASMSHARED-NEXT: 2043: 65 8b 01 movl %gs:(%ecx), %eax
.type tlslocal0,@object
.section .tbss,"awT",@nobits
diff --git a/test/ELF/icf-non-mergeable.s b/test/ELF/icf-non-mergeable.s
new file mode 100644
index 000000000000..378c7b33696d
--- /dev/null
+++ b/test/ELF/icf-non-mergeable.s
@@ -0,0 +1,28 @@
+// REQUIRES: x86
+
+// This file contains two functions. They are themselves identical,
+// but because they have reloactions against different data section,
+// they are not mergeable.
+
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \
+// RUN: %p/Inputs/icf-non-mergeable.s -o %t2
+
+// RUN: ld.lld %t1 %t2 -o %t3 --icf=all --verbose | FileCheck %s
+
+// CHECK-NOT: selected .text.f1
+// CHECK-NOT: removed .text.f2
+
+.globl _start, f1, f2, d1, d2
+_start:
+ ret
+
+.section .text.f1, "ax"
+f1:
+ movl $5, d1
+ ret
+
+.section .text.f2, "ax"
+f2:
+ movl $5, d2
+ ret
diff --git a/test/ELF/icf4.s b/test/ELF/icf4.s
index ad16d48775d6..08830c8e503c 100644
--- a/test/ELF/icf4.s
+++ b/test/ELF/icf4.s
@@ -1,7 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: ld.lld %t -o %t --icf=all --verbose | FileCheck %s
+# RUN: ld.lld %t -o %t2 --icf=all --verbose | FileCheck %s
# CHECK-NOT: Selected .text.f1
# CHECK-NOT: Selected .text.f2
diff --git a/test/ELF/icf5.s b/test/ELF/icf5.s
index cf466585c581..952fe3601a4d 100644
--- a/test/ELF/icf5.s
+++ b/test/ELF/icf5.s
@@ -1,7 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: ld.lld %t -o %t --icf=all --verbose | FileCheck %s
+# RUN: ld.lld %t -o %t2 --icf=all --verbose | FileCheck %s
# CHECK-NOT: Selected .text.f1
# CHECK-NOT: Selected .text.f2
diff --git a/test/ELF/icf7.s b/test/ELF/icf7.s
index f1fca5b2f2d5..8504ca2ac610 100644
--- a/test/ELF/icf7.s
+++ b/test/ELF/icf7.s
@@ -7,8 +7,8 @@
# CHECK: selected .text.f1
# CHECK: removed .text.f2
-# ALIGN: 0000000000011000 .text 00000000 _start
-# ALIGN: 0000000000011100 .text 00000000 f1
+# ALIGN: 0000000000201000 .text 00000000 _start
+# ALIGN: 0000000000201100 .text 00000000 f1
.globl _start, f1, f2
_start:
diff --git a/test/ELF/image-base.s b/test/ELF/image-base.s
index 0be5059dcad8..a6d160744b70 100644
--- a/test/ELF/image-base.s
+++ b/test/ELF/image-base.s
@@ -3,6 +3,10 @@
# RUN: ld.lld -image-base=0x1000000 %t -o %t1
# RUN: llvm-readobj -program-headers %t1 | FileCheck %s
+# RUN: ld.lld -image-base=0x1000 -z max-page-size=0x2000 %t -o %t1 2>&1 | FileCheck --check-prefix=WARN %s
+# WARN: warning: -image-base: address isn't multiple of page size: 0x1000
+
+
.global _start
_start:
nop
diff --git a/test/ELF/incompatible-ar-first.s b/test/ELF/incompatible-ar-first.s
index 8a8aaec220dd..e076561d11ae 100644
--- a/test/ELF/incompatible-ar-first.s
+++ b/test/ELF/incompatible-ar-first.s
@@ -6,6 +6,6 @@
// We used to crash when
// * The first object seen by the symbol table is from an archive.
// * -m was not used.
-// CHECK: a.o is incompatible with {{.*}}b.o
+// CHECK: .a({{.*}}a.o) is incompatible with {{.*}}b.o
// REQUIRES: x86
diff --git a/test/ELF/incompatible.s b/test/ELF/incompatible.s
index 82055b7a104a..ce84606ad7e2 100644
--- a/test/ELF/incompatible.s
+++ b/test/ELF/incompatible.s
@@ -50,7 +50,7 @@
// RUN: llvm-ar rc %t.a %ta.o
// RUN: llvm-mc -filetype=obj -triple=i686-linux %s -o %tb.o
// RUN: not ld.lld %t.a %tb.o 2>&1 | FileCheck --check-prefix=ARCHIVE %s
-// ARCHIVE: a.o is incompatible with {{.*}}b.o
+// ARCHIVE: .a({{.*}}a.o) is incompatible with {{.*}}b.o
.global _start
_start:
.data
diff --git a/test/ELF/invalid-cie-length.s b/test/ELF/invalid-cie-length.s
index e9ad3ca8ba82..e5ba27cf4616 100644
--- a/test/ELF/invalid-cie-length.s
+++ b/test/ELF/invalid-cie-length.s
@@ -6,4 +6,4 @@
.section .eh_frame
.byte 0
-// CHECK: CIE/FDE too small
+// CHECK: {{.*}}:(.eh_frame+0x0): CIE/FDE too small
diff --git a/test/ELF/invalid-cie-length2.s b/test/ELF/invalid-cie-length2.s
index 94a677eb8913..4aea66952134 100644
--- a/test/ELF/invalid-cie-length2.s
+++ b/test/ELF/invalid-cie-length2.s
@@ -6,4 +6,4 @@
.section .eh_frame
.long 42
-// CHECK: CIE/FIE ends past the end of the section
+// CHECK: {{.*}}:(.eh_frame+0x0): CIE/FDE ends past the end of the section
diff --git a/test/ELF/invalid-cie-length3.s b/test/ELF/invalid-cie-length3.s
index 665fc80a24b4..ebd2690a9d98 100644
--- a/test/ELF/invalid-cie-length3.s
+++ b/test/ELF/invalid-cie-length3.s
@@ -6,4 +6,4 @@
.section .eh_frame
.long 0xFFFFFFFC
-// CHECK: CIE/FIE ends past the end of the section
+// CHECK: {{.*}}:(.eh_frame+0x0): CIE/FDE ends past the end of the section
diff --git a/test/ELF/invalid-cie-length4.s b/test/ELF/invalid-cie-length4.s
index daa20d196811..8c7cc00dfcaf 100644
--- a/test/ELF/invalid-cie-length4.s
+++ b/test/ELF/invalid-cie-length4.s
@@ -7,4 +7,4 @@
.long 0xFFFFFFFF
.byte 0
-// CHECK: CIE/FDE too large
+// CHECK: {{.*}}:(.eh_frame+0x0): CIE/FDE too large
diff --git a/test/ELF/invalid-dynamic-list.test b/test/ELF/invalid-dynamic-list.test
index 0e7c820f563e..f560ceed0f8b 100644
--- a/test/ELF/invalid-dynamic-list.test
+++ b/test/ELF/invalid-dynamic-list.test
@@ -11,27 +11,27 @@
# RUN: echo foobar > %t1
# RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR1 %s
-# ERR1: line 1: { expected, but got foobar
+# ERR1: {{.*}}:1: { expected, but got foobar
# RUN: echo "{ foobar;" > %t1
# RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR2 %s
-# ERR2: line 1: unexpected EOF
+# ERR2: {{.*}}:1: unexpected EOF
## Missing ';' before '}'
# RUN: echo "{ foobar }" > %t1
# RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR3 %s
-# ERR3: line 1: ; expected, but got }
+# ERR3: {{.*}}:1: ; expected, but got }
## Missing final ';'
# RUN: echo "{ foobar; }" > %t1
# RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR4 %s
-# ERR4: line 1: unexpected EOF
+# ERR4: {{.*}}:1: unexpected EOF
## Missing \" in foobar definition
# RUN echo "{ \"foobar; };" > %t1
# RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR5 %s
-# ERR5: line 1: unexpected EOF
+# ERR5: {{.*}}:1: unexpected EOF
# RUN: echo "{ extern \"BOGUS\" { test }; };" > %t1
# RUN: not ld.lld --dynamic-list %t1 2>&1 | FileCheck -check-prefix=ERR6 %s
-# ERR6: line 1: ; expected, but got BOGUS
+# ERR6: {{.*}}:1: Unknown language
diff --git a/test/ELF/invalid-elf.test b/test/ELF/invalid-elf.test
deleted file mode 100644
index c3a97d37ffa9..000000000000
--- a/test/ELF/invalid-elf.test
+++ /dev/null
@@ -1,35 +0,0 @@
-# RUN: llvm-mc %s -o %t -filetype=obj -triple x86_64-pc-linux
-
-# RUN: not ld.lld %t %p/Inputs/invalid-data-encoding.a -o %t2 2>&1 | \
-# RUN: FileCheck --check-prefix=INVALID-DATA-ENC %s
-# INVALID-DATA-ENC: invalid data encoding: test.o
-
-# RUN: not ld.lld %t %p/Inputs/invalid-file-class.a -o %t2 2>&1 | \
-# RUN: FileCheck --check-prefix=INVALID-FILE-CLASS %s
-# INVALID-FILE-CLASS: invalid file class: test.o
-
-# RUN: not ld.lld %p/Inputs/invalid-symtab-sh_info.elf -o %t2 2>&1 | \
-# RUN: FileCheck --check-prefix=INVALID-SYMTAB-SHINFO %s
-# INVALID-SYMTAB-SHINFO: invalid sh_info in symbol table
-
-# RUN: not ld.lld %p/Inputs/invalid-binding.elf -o %t2 2>&1 | \
-# RUN: FileCheck --check-prefix=INVALID-BINDING %s
-# INVALID-BINDING: unexpected binding
-
-# RUN: not ld.lld %p/Inputs/invalid-section-index.elf -o %t2 2>&1 | \
-# RUN: FileCheck --check-prefix=INVALID-SECTION-INDEX-LLD %s
-# INVALID-SECTION-INDEX-LLD: invalid section index
-
-# RUN: not ld.lld %p/Inputs/invalid-shstrndx.so -o %t2 2>&1 | \
-# RUN: FileCheck --check-prefix=INVALID-SECTION-INDEX %s
-# INVALID-SECTION-INDEX: Invalid section index
-
-# RUN: not ld.lld %p/Inputs/invalid-shentsize-zero.elf -o %t2 2>&1 | \
-# RUN: FileCheck --check-prefix=INVALID-SHENTSIZE-ZERO %s
-# INVALID-SHENTSIZE-ZERO: SHF_MERGE section size must be a multiple of sh_entsize
-
-# RUN: not ld.lld %p/Inputs/invalid-multiple-eh-relocs.elf -o %t2 2>&1 | \
-# RUN: FileCheck --check-prefix=INVALID-EH-RELOCS %s
-# INVALID-EH-RELOCS: multiple relocation sections to .eh_frame are not supported
-
-.long foo
diff --git a/test/ELF/invalid-linkerscript.test b/test/ELF/invalid-linkerscript.test
index e0881662b962..3316becce1dc 100644
--- a/test/ELF/invalid-linkerscript.test
+++ b/test/ELF/invalid-linkerscript.test
@@ -15,7 +15,7 @@
# RUN: echo foobar > %t1
# RUN: not ld.lld %t1 no-such-file 2>&1 | FileCheck -check-prefix=ERR1 %s
-# ERR1: unknown directive: foobar
+# ERR1: unexpected EOF
# ERR1: cannot open no-such-file:
# RUN: echo "foo \"bar" > %t2
diff --git a/test/ELF/Inputs/invalid-binding.elf b/test/ELF/invalid/Inputs/binding.elf
index 61b5af9b456c..61b5af9b456c 100644
--- a/test/ELF/Inputs/invalid-binding.elf
+++ b/test/ELF/invalid/Inputs/binding.elf
Binary files differ
diff --git a/test/ELF/invalid/Inputs/broken-relaxation-x64.elf b/test/ELF/invalid/Inputs/broken-relaxation-x64.elf
new file mode 100644
index 000000000000..01a6af9a7fb5
--- /dev/null
+++ b/test/ELF/invalid/Inputs/broken-relaxation-x64.elf
Binary files differ
diff --git a/test/ELF/Inputs/invalid-cie-version2.elf b/test/ELF/invalid/Inputs/cie-version2.elf
index 87f8a5be76f4..87f8a5be76f4 100644
--- a/test/ELF/Inputs/invalid-cie-version2.elf
+++ b/test/ELF/invalid/Inputs/cie-version2.elf
Binary files differ
diff --git a/test/ELF/invalid/Inputs/common-symbol-alignment.elf b/test/ELF/invalid/Inputs/common-symbol-alignment.elf
new file mode 100644
index 000000000000..9e7823b852b2
--- /dev/null
+++ b/test/ELF/invalid/Inputs/common-symbol-alignment.elf
Binary files differ
diff --git a/test/ELF/invalid/Inputs/common-symbol-alignment2.elf b/test/ELF/invalid/Inputs/common-symbol-alignment2.elf
new file mode 100644
index 000000000000..21c2457cbea1
--- /dev/null
+++ b/test/ELF/invalid/Inputs/common-symbol-alignment2.elf
Binary files differ
diff --git a/test/ELF/Inputs/invalid-data-encoding.a b/test/ELF/invalid/Inputs/data-encoding.a
index ff2b37318c1e..ff2b37318c1e 100644
--- a/test/ELF/Inputs/invalid-data-encoding.a
+++ b/test/ELF/invalid/Inputs/data-encoding.a
Binary files differ
diff --git a/test/ELF/invalid/Inputs/dynamic-section-sh_size.elf b/test/ELF/invalid/Inputs/dynamic-section-sh_size.elf
new file mode 100644
index 000000000000..ea35dba95179
--- /dev/null
+++ b/test/ELF/invalid/Inputs/dynamic-section-sh_size.elf
Binary files differ
diff --git a/test/ELF/Inputs/invalid-file-class.a b/test/ELF/invalid/Inputs/file-class.a
index f0ce607b4fdc..f0ce607b4fdc 100644
--- a/test/ELF/Inputs/invalid-file-class.a
+++ b/test/ELF/invalid/Inputs/file-class.a
Binary files differ
diff --git a/test/ELF/invalid/Inputs/invalid-e_shnum.elf b/test/ELF/invalid/Inputs/invalid-e_shnum.elf
new file mode 100644
index 000000000000..0b46fbd8cec0
--- /dev/null
+++ b/test/ELF/invalid/Inputs/invalid-e_shnum.elf
Binary files differ
diff --git a/test/ELF/invalid/Inputs/invalid-relocation-x64.elf b/test/ELF/invalid/Inputs/invalid-relocation-x64.elf
new file mode 100644
index 000000000000..25df2944614e
--- /dev/null
+++ b/test/ELF/invalid/Inputs/invalid-relocation-x64.elf
Binary files differ
diff --git a/test/ELF/invalid/Inputs/mips-invalid-options-descriptor.elf b/test/ELF/invalid/Inputs/mips-invalid-options-descriptor.elf
new file mode 100644
index 000000000000..85feeebc6198
--- /dev/null
+++ b/test/ELF/invalid/Inputs/mips-invalid-options-descriptor.elf
Binary files differ
diff --git a/test/ELF/Inputs/invalid-multiple-eh-relocs.elf b/test/ELF/invalid/Inputs/multiple-eh-relocs.elf
index 62914596256a..62914596256a 100644
--- a/test/ELF/Inputs/invalid-multiple-eh-relocs.elf
+++ b/test/ELF/invalid/Inputs/multiple-eh-relocs.elf
Binary files differ
diff --git a/test/ELF/invalid/Inputs/section-alignment-notpow2.elf b/test/ELF/invalid/Inputs/section-alignment-notpow2.elf
new file mode 100644
index 000000000000..59d4de7e7303
--- /dev/null
+++ b/test/ELF/invalid/Inputs/section-alignment-notpow2.elf
Binary files differ
diff --git a/test/ELF/Inputs/invalid-section-index.elf b/test/ELF/invalid/Inputs/section-index.elf
index ec5adcf3b0b8..ec5adcf3b0b8 100644
--- a/test/ELF/Inputs/invalid-section-index.elf
+++ b/test/ELF/invalid/Inputs/section-index.elf
Binary files differ
diff --git a/test/ELF/invalid/Inputs/section-index2.elf b/test/ELF/invalid/Inputs/section-index2.elf
new file mode 100644
index 000000000000..5d842880b375
--- /dev/null
+++ b/test/ELF/invalid/Inputs/section-index2.elf
Binary files differ
diff --git a/test/ELF/Inputs/invalid-shentsize-zero.elf b/test/ELF/invalid/Inputs/shentsize-zero.elf
index 5fa7df245619..5fa7df245619 100644
--- a/test/ELF/Inputs/invalid-shentsize-zero.elf
+++ b/test/ELF/invalid/Inputs/shentsize-zero.elf
Binary files differ
diff --git a/test/ELF/invalid/Inputs/sht-group.elf b/test/ELF/invalid/Inputs/sht-group.elf
new file mode 100644
index 000000000000..5cb033628418
--- /dev/null
+++ b/test/ELF/invalid/Inputs/sht-group.elf
Binary files differ
diff --git a/test/ELF/invalid/Inputs/symbol-index.elf b/test/ELF/invalid/Inputs/symbol-index.elf
new file mode 100644
index 000000000000..f31ffbf602c3
--- /dev/null
+++ b/test/ELF/invalid/Inputs/symbol-index.elf
Binary files differ
diff --git a/test/ELF/invalid/Inputs/symbol-name-offset.elf b/test/ELF/invalid/Inputs/symbol-name-offset.elf
new file mode 100644
index 000000000000..8b8abde79873
--- /dev/null
+++ b/test/ELF/invalid/Inputs/symbol-name-offset.elf
Binary files differ
diff --git a/test/ELF/Inputs/invalid-symtab-sh_info.elf b/test/ELF/invalid/Inputs/symtab-sh_info.elf
index aa63d582d757..aa63d582d757 100644
--- a/test/ELF/Inputs/invalid-symtab-sh_info.elf
+++ b/test/ELF/invalid/Inputs/symtab-sh_info.elf
Binary files differ
diff --git a/test/ELF/invalid/Inputs/symtab-sh_info2.elf b/test/ELF/invalid/Inputs/symtab-sh_info2.elf
new file mode 100644
index 000000000000..4fea12cf2033
--- /dev/null
+++ b/test/ELF/invalid/Inputs/symtab-sh_info2.elf
Binary files differ
diff --git a/test/ELF/invalid/Inputs/symtab-sh_info3.elf b/test/ELF/invalid/Inputs/symtab-sh_info3.elf
new file mode 100644
index 000000000000..ca889fbca1aa
--- /dev/null
+++ b/test/ELF/invalid/Inputs/symtab-sh_info3.elf
Binary files differ
diff --git a/test/ELF/invalid/Inputs/tls-symbol.elf b/test/ELF/invalid/Inputs/tls-symbol.elf
new file mode 100644
index 000000000000..78c759897ddc
--- /dev/null
+++ b/test/ELF/invalid/Inputs/tls-symbol.elf
Binary files differ
diff --git a/test/ELF/invalid/Inputs/too-short.elf b/test/ELF/invalid/Inputs/too-short.elf
new file mode 100644
index 000000000000..077f392f1dc4
--- /dev/null
+++ b/test/ELF/invalid/Inputs/too-short.elf
Binary files differ
diff --git a/test/ELF/invalid/broken-relaxation-x64.s b/test/ELF/invalid/broken-relaxation-x64.s
new file mode 100644
index 000000000000..10d4655847d5
--- /dev/null
+++ b/test/ELF/invalid/broken-relaxation-x64.s
@@ -0,0 +1,46 @@
+# REQUIRES: x86
+
+# RUN: yaml2obj %s -o %t.o
+# RUN: not ld.lld %t.o -o %t.exe 2>&1 | FileCheck --check-prefix=ERR %s
+# ERR: R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only
+# ERR: R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only
+
+## YAML below contains 2 relocations of type R_X86_64_GOTTPOFF, and a .text
+## with fake content filled by 0xFF. That means instructions for relaxation are
+## "broken", so they does not match any known valid relaxations. We also generate
+## .tls section because we need it for correct proccessing of STT_TLS symbol.
+!ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ OSABI: ELFOSABI_FREEBSD
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Type: SHT_PROGBITS
+ Name: .text
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x04
+ Content: "FFFFFFFFFFFFFFFF"
+ - Type: SHT_PROGBITS
+ Name: .tls
+ Flags: [ SHF_ALLOC, SHF_TLS ]
+ - Type: SHT_REL
+ Name: .rel.text
+ Link: .symtab
+ Info: .text
+ AddressAlign: 0x04
+ Relocations:
+ - Offset: 4
+ Symbol: foo
+ Type: R_X86_64_GOTTPOFF
+ - Offset: 4
+ Symbol: foo
+ Type: R_X86_64_GOTTPOFF
+Symbols:
+ Global:
+ - Name: foo
+ Type: STT_TLS
+ Section: .text
+ Value: 0x12345
+ Size: 4
diff --git a/test/ELF/invalid/common-symbol-alignment.s b/test/ELF/invalid/common-symbol-alignment.s
new file mode 100644
index 000000000000..2a654b11479b
--- /dev/null
+++ b/test/ELF/invalid/common-symbol-alignment.s
@@ -0,0 +1,12 @@
+# REQUIRES: x86
+
+## common-symbol-alignment.elf contains common symbol with zero alignment.
+# RUN: not ld.lld %S/Inputs/common-symbol-alignment.elf \
+# RUN: -o %t 2>&1 | FileCheck %s
+# CHECK: common symbol 'bar' has invalid alignment: 0
+
+## common-symbol-alignment2.elf contains common symbol alignment greater
+## than UINT32_MAX.
+# RUN: not ld.lld %S/Inputs/common-symbol-alignment2.elf \
+# RUN: -o %t 2>&1 | FileCheck %s --check-prefix=BIG
+# BIG: common symbol 'bar' has invalid alignment: 271644049215
diff --git a/test/ELF/invalid/dynamic-section-size.s b/test/ELF/invalid/dynamic-section-size.s
new file mode 100644
index 000000000000..7d12904801e4
--- /dev/null
+++ b/test/ELF/invalid/dynamic-section-size.s
@@ -0,0 +1,4 @@
+## dynamic-section-sh_size.elf has incorrect sh_size of dynamic section.
+# RUN: not ld.lld %p/Inputs/dynamic-section-sh_size.elf -o %t2 2>&1 | \
+# RUN: FileCheck %s
+# CHECK: getSectionContentsAsArray failed: Invalid data was encountered while parsing the file
diff --git a/test/ELF/invalid/eh-frame-hdr-no-out.s b/test/ELF/invalid/eh-frame-hdr-no-out.s
new file mode 100644
index 000000000000..8379253d9fd0
--- /dev/null
+++ b/test/ELF/invalid/eh-frame-hdr-no-out.s
@@ -0,0 +1,6 @@
+// REQUIRES: x86
+// RUN: not ld.lld --eh-frame-hdr %p/Inputs/cie-version2.elf -o %t >& %t.log
+// RUN: FileCheck %s < %t.log
+
+// cie-version2.elf contains unsupported version of CIE = 2.
+// CHECK: FDE version 1 or 3 expected, but got 2
diff --git a/test/ELF/invalid/invalid-e_shnum.s b/test/ELF/invalid/invalid-e_shnum.s
new file mode 100644
index 000000000000..0c720ffa1713
--- /dev/null
+++ b/test/ELF/invalid/invalid-e_shnum.s
@@ -0,0 +1,3 @@
+## Spec says that "If a file has no section header table, e_shnum holds the value zero.", though
+## in this test case it holds non-zero and lld used to crash.
+# RUN: ld.lld %p/Inputs/invalid-e_shnum.elf -o %t2
diff --git a/test/ELF/invalid/invalid-elf.test b/test/ELF/invalid/invalid-elf.test
new file mode 100644
index 000000000000..e03450ed289c
--- /dev/null
+++ b/test/ELF/invalid/invalid-elf.test
@@ -0,0 +1,31 @@
+# RUN: llvm-mc %s -o %t -filetype=obj -triple x86_64-pc-linux
+
+# RUN: not ld.lld %t %p/Inputs/data-encoding.a -o %t2 2>&1 | \
+# RUN: FileCheck --check-prefix=INVALID-DATA-ENC %s
+# INVALID-DATA-ENC: test.o: invalid data encoding
+
+# RUN: not ld.lld %t %p/Inputs/file-class.a -o %t2 2>&1 | \
+# RUN: FileCheck --check-prefix=INVALID-FILE-CLASS %s
+# INVALID-FILE-CLASS: test.o: invalid file class
+
+# RUN: not ld.lld %p/Inputs/symtab-sh_info.elf -o %t2 2>&1 | \
+# RUN: FileCheck --check-prefix=INVALID-SYMTAB-SHINFO %s
+# INVALID-SYMTAB-SHINFO: invalid sh_info in symbol table
+
+# RUN: not ld.lld %p/Inputs/binding.elf -o %t2 2>&1 | \
+# RUN: FileCheck --check-prefix=INVALID-BINDING %s
+# INVALID-BINDING: unexpected binding
+
+# RUN: not ld.lld %p/Inputs/section-index.elf -o %t2 2>&1 | \
+# RUN: FileCheck --check-prefix=INVALID-SECTION-INDEX-LLD %s
+# INVALID-SECTION-INDEX-LLD: invalid section index
+
+## section-index2.elf has local symbol with incorrect section index.
+# RUN: not ld.lld %p/Inputs/section-index2.elf -o %t2 2>&1 | \
+# RUN: FileCheck --check-prefix=INVALID-SECTION-INDEX-LLD %s
+
+# RUN: not ld.lld %p/Inputs/multiple-eh-relocs.elf -o %t2 2>&1 | \
+# RUN: FileCheck --check-prefix=INVALID-EH-RELOCS %s
+# INVALID-EH-RELOCS: multiple relocation sections to one section are not supported
+
+.long foo
diff --git a/test/ELF/invalid/invalid-relocation-x64.s b/test/ELF/invalid/invalid-relocation-x64.s
new file mode 100644
index 000000000000..1ad9afa4c649
--- /dev/null
+++ b/test/ELF/invalid/invalid-relocation-x64.s
@@ -0,0 +1,30 @@
+## invalid-relocation-x64.elf contains relocations with invalid relocation number.
+## Next yaml code was used to create initial binary. After that it
+## was modified with hex-editor to replace known relocations with fake ones,
+## that have 0x98 and 0x98 numbers.
+!ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ OSABI: ELFOSABI_FREEBSD
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC ]
+ - Name: .rela.text
+ Type: SHT_RELA
+ Link: .symtab
+ Info: .text
+ Relocations:
+ - Offset: 0x0000000000000000
+ Symbol: ''
+ Type: R_X86_64_NONE
+ - Offset: 0x0000000000000000
+ Symbol: ''
+ Type: R_X86_64_NONE
+
+# RUN: not ld.lld %p/Inputs/invalid-relocation-x64.elf -o %t2 2>&1 | FileCheck %s
+# CHECK: unrecognized reloc 152
+# CHECK: unrecognized reloc 153
diff --git a/test/ELF/merge-invalid-size.s b/test/ELF/invalid/merge-invalid-size.s
index 2a99e14ad836..1dbd7cf95b7d 100644
--- a/test/ELF/merge-invalid-size.s
+++ b/test/ELF/invalid/merge-invalid-size.s
@@ -3,5 +3,8 @@
// RUN: not ld.lld %t.o -o %t.so 2>&1 | FileCheck %s
// CHECK: SHF_MERGE section size must be a multiple of sh_entsize
- .section .foo,"aM",@progbits,4
- .short 42
+// Test that we accept a zero sh_entsize.
+// RUN: ld.lld %p/Inputs/shentsize-zero.elf -o %t2
+
+.section .foo,"aM",@progbits,4
+.short 42
diff --git a/test/ELF/invalid/mips-invalid-options-descriptor.s b/test/ELF/invalid/mips-invalid-options-descriptor.s
new file mode 100644
index 000000000000..b47bd9037cc5
--- /dev/null
+++ b/test/ELF/invalid/mips-invalid-options-descriptor.s
@@ -0,0 +1,5 @@
+## mips-invalid-options-descriptor.elf has option descriptor in
+## .MIPS.options with size of zero.
+# RUN: not ld.lld %p/Inputs/mips-invalid-options-descriptor.elf -o %t2 2>&1 | \
+# RUN: FileCheck %s
+# CHECK: error: invalid section offset
diff --git a/test/ELF/invalid/section-alignment.test b/test/ELF/invalid/section-alignment.test
new file mode 100644
index 000000000000..48da12243ac8
--- /dev/null
+++ b/test/ELF/invalid/section-alignment.test
@@ -0,0 +1,19 @@
+# RUN: yaml2obj %s -o %t
+# RUN: not ld.lld %t -o %tout 2>&1 | FileCheck %s
+
+## In current lld implementation, we do not accept sh_addralign
+## larger than UINT32_MAX.
+!ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x1000000000000000
+ Content: "00000000"
+
+# CHECK: section sh_addralign is too large
diff --git a/test/ELF/invalid/section-alignment2.s b/test/ELF/invalid/section-alignment2.s
new file mode 100644
index 000000000000..aaef9f8bff5d
--- /dev/null
+++ b/test/ELF/invalid/section-alignment2.s
@@ -0,0 +1,5 @@
+## section-alignment-notpow2.elf has section alignment
+## 0xFFFFFFFF which is not a power of 2.
+# RUN: not ld.lld %p/Inputs/section-alignment-notpow2.elf -o %t2 2>&1 | \
+# RUN: FileCheck %s
+# CHECK: section sh_addralign is not a power of 2
diff --git a/test/ELF/invalid/sht-group.s b/test/ELF/invalid/sht-group.s
new file mode 100644
index 000000000000..f28035f0dac9
--- /dev/null
+++ b/test/ELF/invalid/sht-group.s
@@ -0,0 +1,3 @@
+## sht-group.elf contains SHT_GROUP section with invalid sh_info.
+# RUN: not ld.lld %p/Inputs/sht-group.elf -o %t2 2>&1 | FileCheck %s
+# CHECK: invalid symbol index
diff --git a/test/ELF/invalid/symbol-index.s b/test/ELF/invalid/symbol-index.s
new file mode 100644
index 000000000000..4ad1d6cb232c
--- /dev/null
+++ b/test/ELF/invalid/symbol-index.s
@@ -0,0 +1,10 @@
+## symbol-index.elf has incorrect type of .symtab section.
+## There is no symbol bodies because of that and any symbol index becomes incorrect.
+## Section Headers:
+## [Nr] Name Type Address Off Size ES Flg Lk Inf Al
+## [ 0] NULL 0000000000000000 000000 000000 00 0 0 0
+## ...
+## [ 4] .symtab RELA 0000000000000000 000048 000030 18 1 2 8
+# RUN: not ld.lld %p/Inputs/symbol-index.elf -o %t2 2>&1 | \
+# RUN: FileCheck --check-prefix=INVALID-SYMBOL-INDEX %s
+# INVALID-SYMBOL-INDEX: invalid symbol index
diff --git a/test/ELF/invalid/symbol-name.s b/test/ELF/invalid/symbol-name.s
new file mode 100644
index 000000000000..8daee1a3fa78
--- /dev/null
+++ b/test/ELF/invalid/symbol-name.s
@@ -0,0 +1,7 @@
+# REQUIRES: x86
+
+## symbol-name-offset.elf contains symbol with invalid (too large)
+## st_name value.
+# RUN: not ld.lld %S/Inputs/symbol-name-offset.elf \
+# RUN: -o %t 2>&1 | FileCheck %s
+# CHECK: invalid symbol name offset
diff --git a/test/ELF/invalid/symtab-sh-info.s b/test/ELF/invalid/symtab-sh-info.s
new file mode 100644
index 000000000000..3522ae550c08
--- /dev/null
+++ b/test/ELF/invalid/symtab-sh-info.s
@@ -0,0 +1,9 @@
+## sh_info contains zero value. First entry in a symbol table is always completely zeroed,
+## so sh_info should be at least 1 in a valid ELF.
+# RUN: not ld.lld %p/Inputs/symtab-sh_info2.elf -o %t2 2>&1 | FileCheck %s
+# CHECK: invalid sh_info in symbol table
+
+## sh_info contains invalid value saying non-local symbol is local.
+# RUN: not ld.lld %p/Inputs/symtab-sh_info3.elf -o %t2 2>&1 | \
+# RUN: FileCheck --check-prefix=INVALID-SYMTAB-SHINFO %s
+# INVALID-SYMTAB-SHINFO: broken object: getLocalSymbols returns a non-local symbol
diff --git a/test/ELF/invalid/symtab-symbols.test b/test/ELF/invalid/symtab-symbols.test
new file mode 100644
index 000000000000..953b2e32a4ac
--- /dev/null
+++ b/test/ELF/invalid/symtab-symbols.test
@@ -0,0 +1,25 @@
+# RUN: yaml2obj %s -o %t
+# RUN: ld.lld -shared %t -o %tout
+
+# GNU assembler 2.17.50 [FreeBSD] 2007-07-03 could generate
+# broken objects.
+# Verify that lld can handle STT_NOTYPE symbols associated
+# with SHT_SYMTAB section.
+
+!ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ OSABI: ELFOSABI_FREEBSD
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x0000000000000010
+ Content: "00000000"
+Symbols:
+ Local:
+ - Type: STT_NOTYPE
+ Section: .symtab
diff --git a/test/ELF/invalid/tls-symbol.s b/test/ELF/invalid/tls-symbol.s
new file mode 100644
index 000000000000..7c65c6c9f2c7
--- /dev/null
+++ b/test/ELF/invalid/tls-symbol.s
@@ -0,0 +1,5 @@
+# REQUIRES: x86
+
+## The test file contains a STT_TLS symbol but has no TLS section.
+# RUN: not ld.lld %S/Inputs/tls-symbol.elf -o %t 2>&1 | FileCheck %s
+# CHECK: has a STT_TLS symbol but doesn't have a PT_TLS section
diff --git a/test/ELF/invalid/too-short.s b/test/ELF/invalid/too-short.s
new file mode 100644
index 000000000000..deaf8218d6e0
--- /dev/null
+++ b/test/ELF/invalid/too-short.s
@@ -0,0 +1,5 @@
+# REQUIRES: x86
+
+## too-short.elf file is a truncated ELF.
+# RUN: not ld.lld %S/Inputs/too-short.elf -o %t 2>&1 | FileCheck %s
+# CHECK: file is too short
diff --git a/test/ELF/invalid/verdef-no-symtab.test b/test/ELF/invalid/verdef-no-symtab.test
new file mode 100644
index 000000000000..5b5c32e4ff5c
--- /dev/null
+++ b/test/ELF/invalid/verdef-no-symtab.test
@@ -0,0 +1,26 @@
+# RUN: yaml2obj %s -o %t
+# RUN: not ld.lld %t -o %tout 2>&1 | FileCheck %s
+
+## When we have SHT_GNU_versym section, it is should be associated
+## with symbol table section.
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .versym
+ Type: SHT_GNU_versym
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x1
+ Content: "00000000"
+
+ - Name: .verdef
+ Type: SHT_GNU_verdef
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x1
+ Content: "00000000"
+
+
+# CHECK: SHT_GNU_versym should be associated with symbol table
diff --git a/test/ELF/libsearch.s b/test/ELF/libsearch.s
index 782d755f7341..20783c5b6465 100644
--- a/test/ELF/libsearch.s
+++ b/test/ELF/libsearch.s
@@ -3,6 +3,8 @@
// RUN: %p/Inputs/libsearch-dyn.s -o %tdyn.o
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \
// RUN: %p/Inputs/libsearch-st.s -o %tst.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \
+// RUN: %p/Inputs/use-bar.s -o %tbar.o
// RUN: mkdir -p %t.dir
// RUN: ld.lld -shared %tdyn.o -o %t.dir/libls.so
// RUN: cp -f %t.dir/libls.so %t.dir/libls2.so
@@ -13,12 +15,14 @@
// Should fail if no library specified
// RUN: not ld.lld -l 2>&1 \
// RUN: | FileCheck --check-prefix=NOLIBRARY %s
-// NOLIBRARY: missing arg value for "-l", expected 1 argument.
+// NOLIBRARY: -l: missing argument
+// Should link normally, because _bar is not used
+// RUN: ld.lld -o %t3 %t.o
// Should not link because of undefined symbol _bar
-// RUN: not ld.lld -o %t3 %t.o 2>&1 \
+// RUN: not ld.lld -o %t3 %t.o %tbar.o 2>&1 \
// RUN: | FileCheck --check-prefix=UNDEFINED %s
-// UNDEFINED: undefined symbol: _bar
+// UNDEFINED: error: {{.*}}:(.bar+0x0): undefined symbol '_bar'
// Should fail if cannot find specified library (without -L switch)
// RUN: not ld.lld -o %t3 %t.o -lls 2>&1 \
@@ -85,5 +89,12 @@
// RUN: ld.lld -o %t3 %t.o -L%t.dir -Bstatic -call_shared -lls
// RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=DYNAMIC %s
+// -nostdlib
+// RUN: echo 'SEARCH_DIR("'%t.dir'")' > %t.script
+// RUN: ld.lld -o %t3 %t.o -script %t.script -lls
+// RUN: not ld.lld -o %t3 %t.o -script %t.script -lls -nostdlib \
+// RUN: 2>&1 | FileCheck --check-prefix=NOSTDLIB %s
+// NOSTDLIB: unable to find library -lls
+
.globl _start,_bar
_start:
diff --git a/test/ELF/linkerscript-align.s b/test/ELF/linkerscript-align.s
deleted file mode 100644
index 5f6e6f770766..000000000000
--- a/test/ELF/linkerscript-align.s
+++ /dev/null
@@ -1,41 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-
-# RUN: echo "SECTIONS { \
-# RUN: . = 0x10000; \
-# RUN: .aaa : \
-# RUN: { \
-# RUN: *(.aaa) \
-# RUN: } \
-# RUN: . = ALIGN(4096); \
-# RUN: .bbb : \
-# RUN: { \
-# RUN: *(.bbb) \
-# RUN: } \
-# RUN: . = ALIGN(4096 * 4); \
-# RUN: .ccc : \
-# RUN: { \
-# RUN: *(.ccc) \
-# RUN: } \
-# RUN: }" > %t.script
-# RUN: ld.lld -o %t1 --script %t.script %t
-# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
-# CHECK: Sections:
-# CHECK-NEXT: Idx Name Size Address Type
-# CHECK-NEXT: 0 00000000 0000000000000000
-# CHECK-NEXT: 1 .aaa 00000008 0000000000010000 DATA
-# CHECK-NEXT: 2 .bbb 00000008 0000000000011000 DATA
-# CHECK-NEXT: 3 .ccc 00000008 0000000000014000 DATA
-
-.global _start
-_start:
- nop
-
-.section .aaa, "a"
-.quad 0
-
-.section .bbb, "a"
-.quad 0
-
-.section .ccc, "a"
-.quad 0
diff --git a/test/ELF/linkerscript-diagnostic.s b/test/ELF/linkerscript-diagnostic.s
deleted file mode 100644
index f42cbe82cf38..000000000000
--- a/test/ELF/linkerscript-diagnostic.s
+++ /dev/null
@@ -1,66 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-
-## Take some valid script with multiline comments
-## and check it actually works:
-# RUN: echo "SECTIONS {" > %t.script
-# RUN: echo ".text : { *(.text) }" >> %t.script
-# RUN: echo ".keep : { *(.keep) } /*" >> %t.script
-# RUN: echo "comment line 1" >> %t.script
-# RUN: echo "comment line 2 */" >> %t.script
-# RUN: echo ".temp : { *(.temp) } }" >> %t.script
-# RUN: ld.lld -shared %t -o %t1 --script %t.script
-
-## Change ":" to "+" at line 2, check that error
-## message starts from correct line number:
-# RUN: echo "SECTIONS {" > %t.script
-# RUN: echo ".text + { *(.text) }" >> %t.script
-# RUN: echo ".keep : { *(.keep) } /*" >> %t.script
-# RUN: echo "comment line 1" >> %t.script
-# RUN: echo "comment line 2 */" >> %t.script
-# RUN: echo ".temp : { *(.temp) } }" >> %t.script
-# RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | FileCheck -check-prefix=ERR1 %s
-# ERR1: line 2:
-
-## Change ":" to "+" at line 3 now, check correct error line number:
-# RUN: echo "SECTIONS {" > %t.script
-# RUN: echo ".text : { *(.text) }" >> %t.script
-# RUN: echo ".keep + { *(.keep) } /*" >> %t.script
-# RUN: echo "comment line 1" >> %t.script
-# RUN: echo "comment line 2 */" >> %t.script
-# RUN: echo ".temp : { *(.temp) } }" >> %t.script
-# RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | FileCheck -check-prefix=ERR2 %s
-# ERR2: line 3:
-
-## Change ":" to "+" at line 6, after multiline comment,
-## check correct error line number:
-# RUN: echo "SECTIONS {" > %t.script
-# RUN: echo ".text : { *(.text) }" >> %t.script
-# RUN: echo ".keep : { *(.keep) } /*" >> %t.script
-# RUN: echo "comment line 1" >> %t.script
-# RUN: echo "comment line 2 */" >> %t.script
-# RUN: echo ".temp + { *(.temp) } }" >> %t.script
-# RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | FileCheck -check-prefix=ERR5 %s
-# ERR5: line 6:
-
-## Check that text of lines and pointer to 'bad' token are working ok.
-# RUN: echo "UNKNOWN_TAG {" > %t.script
-# RUN: echo ".text : { *(.text) }" >> %t.script
-# RUN: echo ".keep : { *(.keep) }" >> %t.script
-# RUN: echo ".temp : { *(.temp) } }" >> %t.script
-# RUN: not ld.lld -shared %t -o %t1 --script %t.script > %t.log 2>&1
-# RUN: FileCheck -check-prefix=ERR6 %s < %t.log
-# ERR6: line 1:
-# ERR6-NEXT: UNKNOWN_TAG {
-# RUN: grep '^^' %t.log
-
-## One more check that text of lines and pointer to 'bad' token are working ok.
-# RUN: echo "SECTIONS {" > %t.script
-# RUN: echo ".text : { *(.text) }" >> %t.script
-# RUN: echo ".keep : { *(.keep) }" >> %t.script
-# RUN: echo "boom .temp : { *(.temp) } }" >> %t.script
-# RUN: not ld.lld -shared %t -o %t1 --script %t.script > %t.log 2>&1
-# RUN: FileCheck -check-prefix=ERR7 %s < %t.log
-# ERR7: line 4: : expected, but got .temp
-# ERR7-NEXT: boom .temp : { *(.temp) } }
-# RUN: grep '^ ^' %t.log
diff --git a/test/ELF/linkerscript-locationcounter.s b/test/ELF/linkerscript-locationcounter.s
deleted file mode 100644
index c6e8e7ef8ea8..000000000000
--- a/test/ELF/linkerscript-locationcounter.s
+++ /dev/null
@@ -1,340 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: echo "SECTIONS { \
-# RUN: . = 0xFFF0; \
-# RUN: . = . + 0x10; \
-# RUN: .plus : { *(.plus) } \
-# RUN: . = 0x11010 - 0x10; \
-# RUN: .minus : { *(.minus) } \
-# RUN: . = 0x24000 / 0x2; \
-# RUN: .div : { *(.div) } \
-# RUN: . = 0x11000 + 0x1000 * 0x2; \
-# RUN: .mul : { *(.mul) } \
-# RUN: . = 0x10000 + (0x1000 + 0x1000) * 0x2; \
-# RUN: .bracket : { *(.bracket) } \
-# RUN: . = 0x17000 & 0x15000; \
-# RUN: .and : { *(.and) } \
-# RUN: . = 0x1 ? 0x16000 : 0x999999; \
-# RUN: .ternary1 : { *(.ternary1) } \
-# RUN: . = 0x0 ? 0x999999 : 0x17000; \
-# RUN: .ternary2 : { *(.ternary2) } \
-# RUN: . = 0x0 < 0x1 ? 0x18000 : 0x999999; \
-# RUN: .less : { *(.less) } \
-# RUN: . = 0x1 <= 0x1 ? 0x19000 : 0x999999; \
-# RUN: .lesseq : { *(.lesseq) } \
-# RUN: . = 0x1 > 0x0 ? 0x20000 : 0x999999; \
-# RUN: .great : { *(.great) } \
-# RUN: . = 0x1 >= 0x1 ? 0x21000 : 0x999999; \
-# RUN: .greateq : { *(.greateq) } \
-# RUN: . = 0x1 == 0x1 ? 0x22000 : 0x999999; \
-# RUN: .eq : { *(.eq) } \
-# RUN: . = 0x2 != 0x1 ? 0x23000 : 0x999999; \
-# RUN: .neq : { *(.neq) } \
-# RUN: }" > %t.script
-# RUN: ld.lld %t --script %t.script -o %t2
-# RUN: llvm-readobj -s %t2 | FileCheck %s
-
-# CHECK: Section {
-# CHECK: Index: 1
-# CHECK: Name: .plus
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x10000
-# CHECK-NEXT: Offset:
-# CHECK-NEXT: Size:
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize:
-# CHECK-NEXT: }
-# CHECK-NEXT: Section {
-# CHECK-NEXT: Index: 2
-# CHECK-NEXT: Name: .minus
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x11000
-# CHECK-NEXT: Offset:
-# CHECK-NEXT: Size:
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize:
-# CHECK-NEXT: }
-# CHECK-NEXT: Section {
-# CHECK-NEXT: Index: 3
-# CHECK-NEXT: Name: .div
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x12000
-# CHECK-NEXT: Offset:
-# CHECK-NEXT: Size:
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize:
-# CHECK-NEXT: }
-# CHECK-NEXT: Section {
-# CHECK-NEXT: Index: 4
-# CHECK-NEXT: Name: .mul
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x13000
-# CHECK-NEXT: Offset:
-# CHECK-NEXT: Size:
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize:
-# CHECK-NEXT: }
-# CHECK-NEXT: Section {
-# CHECK-NEXT: Index: 5
-# CHECK-NEXT: Name: .bracket
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x14000
-# CHECK-NEXT: Offset:
-# CHECK-NEXT: Size:
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize:
-# CHECK-NEXT: }
-# CHECK-NEXT: Section {
-# CHECK-NEXT: Index:
-# CHECK-NEXT: Name: .and
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x15000
-# CHECK-NEXT: Offset:
-# CHECK-NEXT: Size:
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize:
-# CHECK-NEXT: }
-# CHECK-NEXT: Section {
-# CHECK-NEXT: Index:
-# CHECK-NEXT: Name: .ternary1
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x16000
-# CHECK-NEXT: Offset:
-# CHECK-NEXT: Size:
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize:
-# CHECK-NEXT: }
-# CHECK-NEXT: Section {
-# CHECK-NEXT: Index:
-# CHECK-NEXT: Name: .ternary2
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x17000
-# CHECK-NEXT: Offset:
-# CHECK-NEXT: Size:
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize:
-# CHECK-NEXT: }
-# CHECK-NEXT: Section {
-# CHECK-NEXT: Index:
-# CHECK-NEXT: Name: .less
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x18000
-# CHECK-NEXT: Offset:
-# CHECK-NEXT: Size:
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize:
-# CHECK-NEXT: }
-# CHECK-NEXT: Section {
-# CHECK-NEXT: Index:
-# CHECK-NEXT: Name: .lesseq
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x19000
-# CHECK-NEXT: Offset:
-# CHECK-NEXT: Size:
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize:
-# CHECK-NEXT: }
-# CHECK-NEXT: Section {
-# CHECK-NEXT: Index:
-# CHECK-NEXT: Name: .great
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x20000
-# CHECK-NEXT: Offset:
-# CHECK-NEXT: Size:
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize:
-# CHECK-NEXT: }
-# CHECK-NEXT: Section {
-# CHECK-NEXT: Index:
-# CHECK-NEXT: Name: .greateq
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x21000
-# CHECK-NEXT: Offset:
-# CHECK-NEXT: Size:
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize:
-# CHECK-NEXT: }
-# CHECK-NEXT: Section {
-# CHECK-NEXT: Index:
-# CHECK-NEXT: Name: .eq
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x22000
-# CHECK-NEXT: Offset:
-# CHECK-NEXT: Size:
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize:
-# CHECK-NEXT: }
-# CHECK-NEXT: Section {
-# CHECK-NEXT: Index:
-# CHECK-NEXT: Name: .neq
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x23000
-# CHECK-NEXT: Offset:
-# CHECK-NEXT: Size:
-# CHECK-NEXT: Link:
-# CHECK-NEXT: Info:
-# CHECK-NEXT: AddressAlignment:
-# CHECK-NEXT: EntrySize:
-# CHECK-NEXT: }
-
-## Mailformed number error.
-# RUN: echo "SECTIONS { \
-# RUN: . = 0x12Q41; \
-# RUN: }" > %t.script
-# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \
-# RUN: FileCheck --check-prefix=NUMERR %s
-# NUMERR: malformed number: 0x12Q41
-
-## Missing closing bracket.
-# RUN: echo "SECTIONS { \
-# RUN: . = 0x10000 + (0x1000 + 0x1000 * 0x2; \
-# RUN: }" > %t.script
-# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \
-# RUN: FileCheck --check-prefix=BRACKETERR %s
-# BRACKETERR: unexpected EOF
-
-## Missing opening bracket.
-# RUN: echo "SECTIONS { \
-# RUN: . = 0x10000 + 0x1000 + 0x1000) * 0x2; \
-# RUN: }" > %t.script
-# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \
-# RUN: FileCheck --check-prefix=BRACKETERR2 %s
-# BRACKETERR2: stray token: )
-
-## Empty expression.
-# RUN: echo "SECTIONS { \
-# RUN: . = ; \
-# RUN: }" > %t.script
-# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \
-# RUN: FileCheck --check-prefix=ERREXPR %s
-# ERREXPR: error in location counter expression
-
-## Div by zero error.
-# RUN: echo "SECTIONS { \
-# RUN: . = 0x10000 / 0x0; \
-# RUN: }" > %t.script
-# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \
-# RUN: FileCheck --check-prefix=DIVZERO %s
-# DIVZERO: division by zero
-
-## Broken ternary operator expression.
-# RUN: echo "SECTIONS { \
-# RUN: . = 0x1 ? 0x2; \
-# RUN: }" > %t.script
-# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \
-# RUN: FileCheck --check-prefix=TERNERR %s
-# TERNERR: unexpected EOF
-
-.globl _start
-_start:
-nop
-
-.section .plus, "a"
-.quad 0
-
-.section .minus, "a"
-.quad 0
-
-.section .div, "a"
-.quad 0
-
-.section .mul, "a"
-.quad 0
-
-.section .bracket, "a"
-.quad 0
-
-.section .and, "a"
-.quad 0
-
-.section .ternary1, "a"
-.quad 0
-
-.section .ternary2, "a"
-.quad 0
-
-.section .less, "a"
-.quad 0
-
-.section .lesseq, "a"
-.quad 0
-
-.section .great, "a"
-.quad 0
-
-.section .greateq, "a"
-.quad 0
-
-.section .eq, "a"
-.quad 0
-
-.section .neq, "a"
-.quad 0
diff --git a/test/ELF/linkerscript-sections-padding.s b/test/ELF/linkerscript-sections-padding.s
deleted file mode 100644
index 545739efe5a8..000000000000
--- a/test/ELF/linkerscript-sections-padding.s
+++ /dev/null
@@ -1,44 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-
-## Check that padding value works:
-# RUN: echo "SECTIONS { .mysec : { *(.mysec*) } =0x112233445566778899 }" > %t.script
-# RUN: ld.lld -o %t.out --script %t.script %t
-# RUN: hexdump -C %t.out | FileCheck -check-prefix=YES %s
-# YES: 00000120 66 22 33 44 55 66 77 88 99 11 22 33 44 55 66 77
-
-## Confirming that address was correct:
-# RUN: echo "SECTIONS { .mysec : { *(.mysec*) } =0x998877665544332211 }" > %t.script
-# RUN: ld.lld -o %t.out --script %t.script %t
-# RUN: hexdump -C %t.out | FileCheck -check-prefix=YES2 %s
-# YES2: 00000120 66 88 77 66 55 44 33 22 11 99 88 77 66 55 44
-
-## Default padding value is 0x00:
-# RUN: echo "SECTIONS { .mysec : { *(.mysec*) } }" > %t.script
-# RUN: ld.lld -o %t.out --script %t.script %t
-# RUN: hexdump -C %t.out | FileCheck -check-prefix=NO %s
-# NO: 00000120 66 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-
-## Filler should be a hex value (1):
-# RUN: echo "SECTIONS { .mysec : { *(.mysec*) } =99 }" > %t.script
-# RUN: not ld.lld -o %t.out --script %t.script %t 2>&1 \
-# RUN: | FileCheck --check-prefix=ERR %s
-# ERR: filler should be a hexadecimal value
-
-## Filler should be a hex value (2):
-# RUN: echo "SECTIONS { .mysec : { *(.mysec*) } =0x99XX }" > %t.script
-# RUN: not ld.lld -o %t.out --script %t.script %t 2>&1 \
-# RUN: | FileCheck --check-prefix=ERR2 %s
-# ERR2: not a hexadecimal value: XX
-
-.section .mysec.1,"a"
-.align 16
-.byte 0x66
-
-.section .mysec.2,"a"
-.align 16
-.byte 0x66
-
-.globl _start
-_start:
- nop
diff --git a/test/ELF/linkerscript.s b/test/ELF/linkerscript.s
deleted file mode 100644
index 4ee7416a4b84..000000000000
--- a/test/ELF/linkerscript.s
+++ /dev/null
@@ -1,120 +0,0 @@
-# There is some bad quoting interaction between lit's internal shell, which is
-# implemented in Python, and the Cygwin implementations of the Unix utilities.
-# Avoid running these tests on Windows for now by requiring a real shell.
-
-# REQUIRES: shell
-
-# REQUIRES: x86
-# RUN: mkdir -p %t.dir
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \
-# RUN: %p/Inputs/libsearch-st.s -o %t2.o
-# RUN: rm -f %t.dir/libxyz.a
-# RUN: llvm-ar rcs %t.dir/libxyz.a %t2.o
-
-# RUN: echo "EXTERN( undef undef2 )" > %t.script
-# RUN: ld.lld %t -o %t2 %t.script
-# RUN: llvm-readobj %t2 > /dev/null
-
-# RUN: echo "GROUP(\"%t\")" > %t.script
-# RUN: ld.lld -o %t2 %t.script
-# RUN: llvm-readobj %t2 > /dev/null
-
-# RUN: echo "INPUT(\"%t\")" > %t.script
-# RUN: ld.lld -o %t2 %t.script
-# RUN: llvm-readobj %t2 > /dev/null
-
-# RUN: echo "GROUP(\"%t\" libxyz.a )" > %t.script
-# RUN: not ld.lld -o %t2 %t.script
-# RUN: ld.lld -o %t2 %t.script -L%t.dir
-# RUN: llvm-readobj %t2 > /dev/null
-
-# RUN: echo "GROUP(\"%t\" =libxyz.a )" > %t.script
-# RUN: not ld.lld -o %t2 %t.script
-# RUN: ld.lld -o %t2 %t.script --sysroot=%t.dir
-# RUN: llvm-readobj %t2 > /dev/null
-
-# RUN: echo "GROUP(\"%t\" -lxyz )" > %t.script
-# RUN: not ld.lld -o %t2 %t.script
-# RUN: ld.lld -o %t2 %t.script -L%t.dir
-# RUN: llvm-readobj %t2 > /dev/null
-
-# RUN: echo "GROUP(\"%t\" libxyz.a )" > %t.script
-# RUN: not ld.lld -o %t2 %t.script
-# RUN: ld.lld -o %t2 %t.script -L%t.dir
-# RUN: llvm-readobj %t2 > /dev/null
-
-# RUN: echo "GROUP(\"%t\" /libxyz.a )" > %t.script
-# RUN: echo "GROUP(\"%t\" /libxyz.a )" > %t.dir/xyz.script
-# RUN: not ld.lld -o %t2 %t.script
-# RUN: not ld.lld -o %t2 %t.script --sysroot=%t.dir
-# RUN: ld.lld -o %t2 %t.dir/xyz.script --sysroot=%t.dir
-# RUN: llvm-readobj %t2 > /dev/null
-
-# RUN: echo "GROUP(\"%t.script2\")" > %t.script1
-# RUN: echo "GROUP(\"%t\")" > %t.script2
-# RUN: ld.lld -o %t2 %t.script1
-# RUN: llvm-readobj %t2 > /dev/null
-
-# RUN: echo "ENTRY(_label)" > %t.script
-# RUN: ld.lld -o %t2 %t.script %t
-# RUN: llvm-readobj %t2 > /dev/null
-
-# RUN: echo "ENTRY(_wrong_label)" > %t.script
-# RUN: not ld.lld -o %t2 %t.script %t > %t.log 2>&1
-# RUN: FileCheck -check-prefix=ERR-ENTRY %s < %t.log
-
-# ERR-ENTRY: undefined symbol: _wrong_label
-
-# -e has precedence over linker script's ENTRY.
-# RUN: echo "ENTRY(_label)" > %t.script
-# RUN: ld.lld -e _start -o %t2 %t.script %t
-# RUN: llvm-readobj -file-headers -symbols %t2 | \
-# RUN: FileCheck -check-prefix=ENTRY-OVERLOAD %s
-
-# ENTRY-OVERLOAD: Entry: [[ENTRY:0x[0-9A-F]+]]
-# ENTRY-OVERLOAD: Name: _start
-# ENTRY-OVERLOAD-NEXT: Value: [[ENTRY]]
-
-# RUN: echo "OUTPUT_FORMAT(elf64-x86-64) /*/*/ GROUP(\"%t\" )" > %t.script
-# RUN: ld.lld -o %t2 %t.script
-# RUN: llvm-readobj %t2 > /dev/null
-
-# RUN: echo "GROUP(AS_NEEDED(\"%t\"))" > %t.script
-# RUN: ld.lld -o %t2 %t.script
-# RUN: llvm-readobj %t2 > /dev/null
-
-# RUN: rm -f %t.out
-# RUN: echo "OUTPUT(\"%t.out\")" > %t.script
-# RUN: ld.lld %t.script %t
-# RUN: llvm-readobj %t.out > /dev/null
-
-# RUN: echo "SEARCH_DIR(/lib/foo/blah)" > %t.script
-# RUN: ld.lld %t.script %t
-# RUN: llvm-readobj %t.out > /dev/null
-
-# RUN: echo ";SEARCH_DIR(x);SEARCH_DIR(y);" > %t.script
-# RUN: ld.lld %t.script %t
-# RUN: llvm-readobj %t.out > /dev/null
-
-# RUN: echo ";" > %t.script
-# RUN: ld.lld %t.script %t
-# RUN: llvm-readobj %t.out > /dev/null
-
-# RUN: echo "INCLUDE \"%t.script2\" OUTPUT(\"%t.out\")" > %t.script1
-# RUN: echo "GROUP(\"%t\")" > %t.script2
-# RUN: ld.lld %t.script1
-# RUN: llvm-readobj %t2 > /dev/null
-
-# RUN: echo "FOO(BAR)" > %t.script
-# RUN: not ld.lld -o foo %t.script > %t.log 2>&1
-# RUN: FileCheck -check-prefix=ERR1 %s < %t.log
-
-# ERR1: unknown directive: FOO
-
-.globl _start, _label
-_start:
- mov $60, %rax
- mov $42, %rdi
-_label:
- syscall
diff --git a/test/ELF/linkerscript/Inputs/comdat-gc.s b/test/ELF/linkerscript/Inputs/comdat-gc.s
new file mode 100644
index 000000000000..da29d5b3830b
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/comdat-gc.s
@@ -0,0 +1,5 @@
+.file 1 "test/ELF/linkerscript/Inputs/comdat_gc.s"
+
+.section .text._Z3fooIiEvv,"axG",@progbits,_Z3fooIiEvv,comdat
+.loc 1 5
+ ret
diff --git a/test/ELF/linkerscript/Inputs/exclude-multiple1.s b/test/ELF/linkerscript/Inputs/exclude-multiple1.s
new file mode 100644
index 000000000000..1e0f741df40f
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/exclude-multiple1.s
@@ -0,0 +1,8 @@
+.section .foo.1,"a"
+ .quad 4
+
+.section .foo.2,"a"
+ .quad 5
+
+.section .foo.3,"a"
+ .quad 6
diff --git a/test/ELF/linkerscript/Inputs/exclude-multiple2.s b/test/ELF/linkerscript/Inputs/exclude-multiple2.s
new file mode 100644
index 000000000000..60f790fa2356
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/exclude-multiple2.s
@@ -0,0 +1,8 @@
+.section .foo.1,"a"
+ .quad 7
+
+.section .foo.2,"a"
+ .quad 8
+
+.section .foo.3,"a"
+ .quad 9
diff --git a/test/ELF/linkerscript/Inputs/filename-spec.s b/test/ELF/linkerscript/Inputs/filename-spec.s
new file mode 100644
index 000000000000..a688b4db4b60
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/filename-spec.s
@@ -0,0 +1,2 @@
+.section .foo,"a"
+ .quad 0x11
diff --git a/test/ELF/linkerscript/Inputs/implicit-program-header.script b/test/ELF/linkerscript/Inputs/implicit-program-header.script
new file mode 100644
index 000000000000..27dbea84c4e4
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/implicit-program-header.script
@@ -0,0 +1,12 @@
+PHDRS
+{
+ ph_write PT_LOAD FLAGS(2);
+ ph_exec PT_LOAD FLAGS(1);
+}
+
+SECTIONS
+{
+ .bar : { *(.bar) } : ph_exec
+ .foo : { *(.foo) }
+ .text : { *(.text) } : ph_write
+}
diff --git a/test/ELF/linkerscript/Inputs/include.s b/test/ELF/linkerscript/Inputs/include.s
new file mode 100644
index 000000000000..a2f809b522f3
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/include.s
@@ -0,0 +1,5 @@
+.section .text
+.globl _potato
+_potato:
+ nop
+ nop
diff --git a/test/ELF/linkerscript/Inputs/keep.s b/test/ELF/linkerscript/Inputs/keep.s
new file mode 100644
index 000000000000..20fa41c1adff
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/keep.s
@@ -0,0 +1,2 @@
+.section .keep, "a"
+ .long 0x41414141
diff --git a/test/ELF/linkerscript/Inputs/libsearch-dyn.s b/test/ELF/linkerscript/Inputs/libsearch-dyn.s
new file mode 100644
index 000000000000..091b3b9b6ece
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/libsearch-dyn.s
@@ -0,0 +1,3 @@
+.globl _bar,_dynamic
+_bar:
+_dynamic:
diff --git a/test/ELF/linkerscript/Inputs/libsearch-st.s b/test/ELF/linkerscript/Inputs/libsearch-st.s
new file mode 100644
index 000000000000..6da62f79d52f
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/libsearch-st.s
@@ -0,0 +1,3 @@
+.globl _bar,_static
+_bar:
+_static:
diff --git a/test/ELF/linkerscript/Inputs/merge-sections-reloc.s b/test/ELF/linkerscript/Inputs/merge-sections-reloc.s
new file mode 100644
index 000000000000..07e599b2820f
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/merge-sections-reloc.s
@@ -0,0 +1,3 @@
+.globl _start
+_start:
+ .quad 0x11223344
diff --git a/test/ELF/linkerscript/Inputs/notinclude.s b/test/ELF/linkerscript/Inputs/notinclude.s
new file mode 100644
index 000000000000..46c0509b56cc
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/notinclude.s
@@ -0,0 +1,4 @@
+.section .text
+.globl tomato
+tomato:
+ movl $1, %eax
diff --git a/test/ELF/linkerscript/Inputs/segment-start.script b/test/ELF/linkerscript/Inputs/segment-start.script
new file mode 100644
index 000000000000..9f1a59360105
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/segment-start.script
@@ -0,0 +1,7 @@
+SECTIONS
+{
+ PROVIDE (foobar1 = SEGMENT_START("text-segment", 0x8001));
+ PROVIDE (foobar2 = SEGMENT_START("data-segment", 0x8002));
+ PROVIDE (foobar3 = SEGMENT_START("bss-segment", 0x8000 + (4 - 1)));
+ PROVIDE (foobar4 = SEGMENT_START("abc-segment", 0x8004));
+}
diff --git a/test/ELF/linkerscript/Inputs/shared.s b/test/ELF/linkerscript/Inputs/shared.s
new file mode 100644
index 000000000000..c3c22fe4b4fb
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/shared.s
@@ -0,0 +1,10 @@
+.global bar
+.type bar, @function
+bar:
+
+.global bar2
+.type bar2, @function
+bar2:
+
+.global zed
+zed:
diff --git a/test/ELF/linkerscript/Inputs/sort-nested.s b/test/ELF/linkerscript/Inputs/sort-nested.s
new file mode 100644
index 000000000000..c42fa9cd7ed8
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/sort-nested.s
@@ -0,0 +1,7 @@
+.section .aaa.1, "a"
+.align 16
+.quad 0x11
+
+.section .aaa.2, "a"
+.align 4
+.quad 0x22
diff --git a/test/ELF/linkerscript/Inputs/sort.s b/test/ELF/linkerscript/Inputs/sort.s
new file mode 100644
index 000000000000..22e65ad19e1e
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/sort.s
@@ -0,0 +1,19 @@
+.section .aaa.5, "a"
+.align 2
+.quad 0x55
+
+.section .aaa.1, "a"
+.align 32
+.quad 0x11
+
+.section .aaa.3, "a"
+.align 8
+.quad 0x33
+
+.section .aaa.2, "a"
+.align 16
+.quad 0x22
+
+.section .aaa.4, "a"
+.align 4
+.quad 0x44
diff --git a/test/ELF/linkerscript/absolute-expr.s b/test/ELF/linkerscript/absolute-expr.s
new file mode 100644
index 000000000000..a9a674b859f4
--- /dev/null
+++ b/test/ELF/linkerscript/absolute-expr.s
@@ -0,0 +1,82 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { \
+# RUN: .text : { \
+# RUN: bar1 = ALIGNOF(.text); \
+# RUN: bar2 = CONSTANT (MAXPAGESIZE); \
+# RUN: bar3 = SIZEOF (.text); \
+# RUN: bar4 = SIZEOF_HEADERS; \
+# RUN: bar5 = 0x42; \
+# RUN: bar6 = foo + 1; \
+# RUN: *(.text) \
+# RUN: } \
+# RUN: };" > %t.script
+# RUN: ld.lld -o %t.so --script %t.script %t.o -shared
+# RUN: llvm-readobj -t %t.so | FileCheck %s
+
+.global foo
+foo = 0x123
+
+# CHECK: Symbol {
+# CHECK: Name: foo
+# CHECK-NEXT: Value: 0x123
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Absolute (0xFFF1)
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: bar1
+# CHECK-NEXT: Value: 0x4
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Absolute
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: bar2
+# CHECK-NEXT: Value: 0x1000
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Absolute
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: bar3
+# CHECK-NEXT: Value: 0x0
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Absolute
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: bar4
+# CHECK-NEXT: Value: 0x190
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Absolute
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: bar5
+# CHECK-NEXT: Value: 0x42
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Absolute
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: bar6
+# CHECK-NEXT: Value: 0x124
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global (0x1)
+# CHECK-NEXT: Type: None (0x0)
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Absolute (0xFFF1)
+# CHECK-NEXT: }
diff --git a/test/ELF/linkerscript/absolute.s b/test/ELF/linkerscript/absolute.s
new file mode 100644
index 000000000000..e4b156e2f012
--- /dev/null
+++ b/test/ELF/linkerscript/absolute.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS { foo = ABSOLUTE(.) + 1; };" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o
+# RUN: llvm-readobj --symbols %t | FileCheck %s
+
+# CHECK: Name: foo
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding:
+# CHECK-NEXT: Type:
+# CHECK-NEXT: Other:
+# CHECK-NEXT: Section: Absolute
+# CHECK-NEXT: }
+
+.text
+.globl _start
+_start:
diff --git a/test/ELF/linkerscript/addr.s b/test/ELF/linkerscript/addr.s
new file mode 100644
index 000000000000..2d3a7ab35767
--- /dev/null
+++ b/test/ELF/linkerscript/addr.s
@@ -0,0 +1,32 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x1000; \
+# RUN: .text : { *(.text*) } \
+# RUN: .foo.1 : { *(.foo.1) } \
+# RUN: .foo.2 ADDR(.foo.1) + 0x100 : { *(.foo.2) } \
+# RUN: .foo.3 : { *(.foo.3) } \
+# RUN: }" > %t.script
+# RUN: ld.lld %t --script %t.script -o %t1
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+
+# CHECK: Sections:
+# CHECK-NEXT: Idx Name Size Address Type
+# CHECK-NEXT: 0 00000000 0000000000000000
+# CHECK-NEXT: 1 .text 00000000 0000000000001000 TEXT DATA
+# CHECK-NEXT: 2 .foo.1 00000008 0000000000001000 DATA
+# CHECK-NEXT: 3 .foo.2 00000008 0000000000001100 DATA
+# CHECK-NEXT: 4 .foo.3 00000008 0000000000001108 DATA
+
+.text
+.globl _start
+_start:
+
+.section .foo.1,"a"
+ .quad 1
+
+.section .foo.2,"a"
+ .quad 2
+
+.section .foo.3,"a"
+ .quad 3
diff --git a/test/ELF/linkerscript/align-empty.s b/test/ELF/linkerscript/align-empty.s
new file mode 100644
index 000000000000..3ff71578410a
--- /dev/null
+++ b/test/ELF/linkerscript/align-empty.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# RUN: echo "SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
+# RUN: abc : { } \
+# RUN: . = ALIGN(0x1000); \
+# RUN: foo : { *(foo) } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t -shared
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+# CHECK: Sections:
+# CHECK-NEXT: Idx Name Size Address
+# CHECK-NEXT: 0 00000000 0000000000000000
+# CHECK-NEXT: 1 foo 00000001 0000000000001000
+
+ .section foo, "a"
+ .byte 0
diff --git a/test/ELF/linkerscript/align.s b/test/ELF/linkerscript/align.s
new file mode 100644
index 000000000000..1e50fed3fa83
--- /dev/null
+++ b/test/ELF/linkerscript/align.s
@@ -0,0 +1,83 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+## Check that ALIGN command workable using location counter
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x10000; \
+# RUN: .aaa : \
+# RUN: { \
+# RUN: *(.aaa) \
+# RUN: } \
+# RUN: . = ALIGN(4096); \
+# RUN: .bbb : \
+# RUN: { \
+# RUN: *(.bbb) \
+# RUN: } \
+# RUN: . = ALIGN(4096 * 4); \
+# RUN: .ccc : \
+# RUN: { \
+# RUN: *(.ccc) \
+# RUN: } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+
+## Check that the two argument version of ALIGN command works
+# RUN: echo "SECTIONS { \
+# RUN: . = ALIGN(0x1234, 0x10000); \
+# RUN: .aaa : \
+# RUN: { \
+# RUN: *(.aaa) \
+# RUN: } \
+# RUN: . = ALIGN(., 4096); \
+# RUN: .bbb : \
+# RUN: { \
+# RUN: *(.bbb) \
+# RUN: } \
+# RUN: . = ALIGN(., 4096 * 4); \
+# RUN: .ccc : \
+# RUN: { \
+# RUN: *(.ccc) \
+# RUN: } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+
+# CHECK: Sections:
+# CHECK-NEXT: Idx Name Size Address Type
+# CHECK-NEXT: 0 00000000 0000000000000000
+# CHECK-NEXT: 1 .aaa 00000008 0000000000010000 DATA
+# CHECK-NEXT: 2 .bbb 00000008 0000000000011000 DATA
+# CHECK-NEXT: 3 .ccc 00000008 0000000000014000 DATA
+
+## Check output sections ALIGN modificator
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x10000; \
+# RUN: .aaa : \
+# RUN: { \
+# RUN: *(.aaa) \
+# RUN: } \
+# RUN: .bbb : ALIGN(4096) \
+# RUN: { \
+# RUN: *(.bbb) \
+# RUN: } \
+# RUN: .ccc : ALIGN(4096 * 4) \
+# RUN: { \
+# RUN: *(.ccc) \
+# RUN: } \
+# RUN: }" > %t2.script
+# RUN: ld.lld -o %t2 --script %t2.script %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+
+.global _start
+_start:
+ nop
+
+.section .aaa, "a"
+.quad 0
+
+.section .bbb, "a"
+.quad 0
+
+.section .ccc, "a"
+.quad 0
diff --git a/test/ELF/linkerscript/alignof.s b/test/ELF/linkerscript/alignof.s
new file mode 100644
index 000000000000..8880634df243
--- /dev/null
+++ b/test/ELF/linkerscript/alignof.s
@@ -0,0 +1,41 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# RUN: echo "SECTIONS { \
+# RUN: .aaa : { *(.aaa) } \
+# RUN: .bbb : { *(.bbb) } \
+# RUN: .ccc : { *(.ccc) } \
+# RUN: _aaa = ALIGNOF(.aaa); \
+# RUN: _bbb = ALIGNOF(.bbb); \
+# RUN: _ccc = ALIGNOF(.ccc); \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck %s
+# CHECK: SYMBOL TABLE:
+# CHECK: 0000000000000008 *ABS* 00000000 _aaa
+# CHECK-NEXT: 0000000000000010 *ABS* 00000000 _bbb
+# CHECK-NEXT: 0000000000000020 *ABS* 00000000 _ccc
+
+## Check that we error out if trying to get alignment of
+## section that does not exist.
+# RUN: echo "SECTIONS { \
+# RUN: _aaa = ALIGNOF(.foo); \
+# RUN: }" > %t.script
+# RUN: not ld.lld -o %t1 --script %t.script %t 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR %s
+# ERR: {{.*}}.script:1: undefined section .foo
+.global _start
+_start:
+ nop
+
+.section .aaa,"a"
+ .align 8
+ .quad 0
+
+.section .bbb,"a"
+ .align 16
+ .quad 0
+
+.section .ccc,"a"
+ .align 32
+ .quad 0
diff --git a/test/ELF/linkerscript/alternate-sections.s b/test/ELF/linkerscript/alternate-sections.s
new file mode 100644
index 000000000000..6d3004af9a80
--- /dev/null
+++ b/test/ELF/linkerscript/alternate-sections.s
@@ -0,0 +1,34 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { abc : { *(foo) *(bar) *(zed) } }" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o -shared
+# RUN: llvm-readobj -s -section-data %t | FileCheck %s
+
+# This test shows an oddity in lld. When a linker script alternates among
+# different types of output section in the same command, the sections are
+# reordered.
+# In this test we go from regular, to merge and back to regular. The reason
+# for the reordering is that we need two create two output sections and
+# one cannot be in the middle of another.
+# If this ever becomes a problem, some options would be:
+# * Adding an extra layer in between input section and output sections (Chunk).
+# With that this example would have 3 chunks, but only one output section.
+# This would unfortunately complicate the non-script case too.
+# * Just create three output sections.
+# * If having three output sections causes problem, have linkerscript specific
+# code to write the section table and section indexes. That way we could
+# keep 3 sections internally but not expose that.
+
+# CHECK: Name: abc
+# CHECK: 0000: 01000000 00000000 02000000 00000000 |
+# CHECK: Name: abc
+# CHECK: 0000: 61626331 323300 |abc123.|
+
+ .section foo, "a"
+ .quad 1
+
+ .section bar,"aMS",@progbits,1
+ .asciz "abc123"
+
+ .section zed, "a"
+ .quad 2
diff --git a/test/ELF/linkerscript/arm-exidx-phdrs.s b/test/ELF/linkerscript/arm-exidx-phdrs.s
new file mode 100644
index 000000000000..971702f55d7b
--- /dev/null
+++ b/test/ELF/linkerscript/arm-exidx-phdrs.s
@@ -0,0 +1,16 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
+// RUN: echo "PHDRS { ph_text PT_LOAD; } \
+// RUN: SECTIONS { \
+// RUN: . = SIZEOF_HEADERS; \
+// RUN: .text : { *(.text) } : ph_text \
+// RUN: }" > %t.script
+// RUN: ld.lld -T %t.script %t.o -shared -o %t.so
+// RUN: llvm-readobj --program-headers %t.so | FileCheck %s
+
+// CHECK: Type: PT_ARM_EXIDX
+
+.fnstart
+bx lr
+.cantunwind
+.fnend
diff --git a/test/ELF/linkerscript/assert.s b/test/ELF/linkerscript/assert.s
new file mode 100644
index 000000000000..06eeaeec3b99
--- /dev/null
+++ b/test/ELF/linkerscript/assert.s
@@ -0,0 +1,43 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
+
+# RUN: echo "SECTIONS { ASSERT(1, fail) }" > %t1.script
+# RUN: ld.lld -shared -o %t1 --script %t1.script %t1.o
+# RUN: llvm-readobj %t1 > /dev/null
+
+# RUN: echo "SECTIONS { ASSERT(ASSERT(42, fail) == 42, fail) }" > %t2.script
+# RUN: ld.lld -shared -o %t2 --script %t2.script %t1.o
+# RUN: llvm-readobj %t2 > /dev/null
+
+# RUN: echo "SECTIONS { ASSERT(0, fail) }" > %t3.script
+# RUN: not ld.lld -shared -o %t3 --script %t3.script %t1.o > %t.log 2>&1
+# RUN: FileCheck %s -check-prefix=FAIL < %t.log
+# FAIL: fail
+
+# RUN: echo "SECTIONS { . = ASSERT(0x1000, fail); }" > %t4.script
+# RUN: ld.lld -shared -o %t4 --script %t4.script %t1.o
+# RUN: llvm-readobj %t4 > /dev/null
+
+# RUN: echo "SECTIONS { .foo : { *(.foo) } }" > %t5.script
+# RUN: echo "ASSERT(SIZEOF(.foo) == 8, fail);" >> %t5.script
+# RUN: ld.lld -shared -o %t5 --script %t5.script %t1.o
+# RUN: llvm-readobj %t5 > /dev/null
+
+## Even without SECTIONS block we still use section names
+## in expressions
+# RUN: echo "ASSERT(SIZEOF(.foo) == 8, fail);" > %t5.script
+# RUN: ld.lld -shared -o %t5 --script %t5.script %t1.o
+# RUN: llvm-readobj %t5 > /dev/null
+
+## Test assertions inside of output section decriptions.
+# RUN: echo "SECTIONS { .foo : { *(.foo) ASSERT(SIZEOF(.foo) == 8, \"true\"); } }" > %t6.script
+# RUN: ld.lld -shared -o %t6 --script %t6.script %t1.o
+# RUN: llvm-readobj %t6 > /dev/null
+
+# RUN: echo "SECTIONS { .foo : { ASSERT(1, \"true\") } }" > %t7.script
+# RUN: not ld.lld -shared -o %t7 --script %t7.script %t1.o > %t.log 2>&1
+# RUN: FileCheck %s -check-prefix=CHECK-SEMI < %t.log
+# CHECK-SEMI: error: {{.*}}.script:1: ; expected, but got }
+
+.section .foo, "a"
+ .quad 0
diff --git a/test/ELF/linkerscript/at.s b/test/ELF/linkerscript/at.s
new file mode 100644
index 000000000000..a6b6198710ea
--- /dev/null
+++ b/test/ELF/linkerscript/at.s
@@ -0,0 +1,119 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x1000; \
+# RUN: .aaa : AT(0x2000) \
+# RUN: { \
+# RUN: *(.aaa) \
+# RUN: } \
+# RUN: .bbb : \
+# RUN: { \
+# RUN: *(.bbb) \
+# RUN: } \
+# RUN: .ccc : AT(0x3000) \
+# RUN: { \
+# RUN: *(.ccc) \
+# RUN: } \
+# RUN: .ddd : AT(0x4000) \
+# RUN: { \
+# RUN: *(.ddd) \
+# RUN: } \
+# 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_PHDR
+# CHECK-NEXT: Offset: 0x40
+# CHECK-NEXT: VirtualAddress: 0x40
+# CHECK-NEXT: PhysicalAddress: 0x40
+# CHECK-NEXT: FileSize:
+# CHECK-NEXT: MemSize:
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 8
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x0
+# CHECK-NEXT: VirtualAddress: 0x0
+# CHECK-NEXT: PhysicalAddress: 0x0
+# CHECK-NEXT: FileSize:
+# CHECK-NEXT: MemSize:
+# 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: 0x1000
+# CHECK-NEXT: VirtualAddress: 0x1000
+# CHECK-NEXT: PhysicalAddress: 0x2000
+# 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: 0x1010
+# CHECK-NEXT: VirtualAddress: 0x1010
+# CHECK-NEXT: PhysicalAddress: 0x3000
+# CHECK-NEXT: FileSize: 8
+# CHECK-NEXT: MemSize: 8
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: PF_X
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 4096
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x1018
+# CHECK-NEXT: VirtualAddress: 0x1018
+# CHECK-NEXT: PhysicalAddress: 0x4000
+# CHECK-NEXT: FileSize: 9
+# CHECK-NEXT: MemSize: 9
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: PF_X
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 4096
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_GNU_STACK
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: VirtualAddress: 0x0
+# CHECK-NEXT: PhysicalAddress: 0x0
+# CHECK-NEXT: FileSize:
+# CHECK-NEXT: MemSize:
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: PF_W
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 0
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+.global _start
+_start:
+ nop
+
+.section .aaa, "a"
+.quad 0
+
+.section .bbb, "a"
+.quad 0
+
+.section .ccc, "a"
+.quad 0
+
+.section .ddd, "a"
+.quad 0
diff --git a/test/ELF/linkerscript/comdat-gc.s b/test/ELF/linkerscript/comdat-gc.s
new file mode 100644
index 000000000000..63dcf85c5a67
--- /dev/null
+++ b/test/ELF/linkerscript/comdat-gc.s
@@ -0,0 +1,14 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/comdat-gc.s -o %t1
+# RUN: echo "SECTIONS { .text : { *(.text*) } }" > %t.script
+# RUN: ld.lld --gc-sections --script %t.script %t %t1 -o %t2
+# RUN: llvm-readobj -sections -symbols %t2 | FileCheck -check-prefix=GC1 %s
+
+# GC1: Name: .debug_line
+
+.file 1 "test/ELF/linkerscript/comdat_gc.s"
+.section .text._Z3fooIiEvv,"axG",@progbits,_Z3fooIiEvv,comdat
+.loc 1 14
+ ret
diff --git a/test/ELF/linkerscript/common.s b/test/ELF/linkerscript/common.s
new file mode 100644
index 000000000000..2e5972d52379
--- /dev/null
+++ b/test/ELF/linkerscript/common.s
@@ -0,0 +1,49 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { . = SIZEOF_HEADERS; .common : { *(COMMON) } }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-readobj -s -t %t1 | FileCheck %s
+
+# q2 alignment is greater than q1, so it should have smaller offset
+# because of sorting
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .common
+# CHECK-NEXT: Type: SHT_NOBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x200
+# CHECK-NEXT: Offset: 0x
+# CHECK-NEXT: Size: 256
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 256
+# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: }
+# CHECK: Symbol {
+# CHECK: Name: q1
+# CHECK-NEXT: Value: 0x280
+# CHECK-NEXT: Size: 128
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .common
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: q2
+# CHECK-NEXT: Value: 0x200
+# CHECK-NEXT: Size: 128
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .common
+# CHECK-NEXT: }
+
+.globl _start
+_start:
+ jmp _start
+
+.comm q1,128,8
+.comm q2,128,256
diff --git a/test/ELF/linkerscript/data-commands.s b/test/ELF/linkerscript/data-commands.s
new file mode 100644
index 000000000000..ef154eda9f83
--- /dev/null
+++ b/test/ELF/linkerscript/data-commands.s
@@ -0,0 +1,81 @@
+# REQUIRES: x86,mips
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS \
+# RUN: { \
+# RUN: .foo : { \
+# RUN: *(.foo.1) \
+# RUN: BYTE(0x11) \
+# RUN: *(.foo.2) \
+# RUN: SHORT(0x1122) \
+# RUN: *(.foo.3) \
+# RUN: LONG(0x11223344) \
+# RUN: *(.foo.4) \
+# RUN: QUAD(0x1122334455667788) \
+# RUN: } \
+# RUN: .bar : { \
+# RUN: *(.bar.1) \
+# RUN: BYTE(a + 1) \
+# RUN: *(.bar.2) \
+# RUN: SHORT(b) \
+# RUN: *(.bar.3) \
+# RUN: LONG(c + 2) \
+# RUN: *(.bar.4) \
+# RUN: QUAD(d) \
+# RUN: } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t %t.o --script %t.script
+# RUN: llvm-objdump -s %t | FileCheck %s
+
+# CHECK: Contents of section .foo:
+# CHECK-NEXT: ff11ff22 11ff4433 2211ff88 77665544
+# CHECK-NEXT: 332211
+
+# CHECK: Contents of section .bar:
+# CHECK-NEXT: ff12ff22 11ff4633 2211ff88 77665544
+# CHECK-NEXT: 332211
+
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %tmips64be
+# RUN: ld.lld --script %t.script %tmips64be -o %t2
+# RUN: llvm-objdump -s %t2 | FileCheck %s --check-prefix=BE
+# BE: Contents of section .foo:
+# BE-NEXT: ff11ff11 22ff1122 3344ff11 22334455
+# BE-NEXT: 667788
+# BE-NEXT: Contents of section .bar:
+# BE-NEXT: ff12ff11 22ff1122 3346ff11 22334455
+# BE-NEXT: 667788
+
+.global a
+a = 0x11
+
+.global b
+b = 0x1122
+
+.global c
+c = 0x11223344
+
+.global d
+d = 0x1122334455667788
+
+.section .foo.1, "a"
+ .byte 0xFF
+
+.section .foo.2, "a"
+ .byte 0xFF
+
+.section .foo.3, "a"
+ .byte 0xFF
+
+.section .foo.4, "a"
+ .byte 0xFF
+
+.section .bar.1, "a"
+ .byte 0xFF
+
+.section .bar.2, "a"
+ .byte 0xFF
+
+.section .bar.3, "a"
+ .byte 0xFF
+
+.section .bar.4, "a"
+ .byte 0xFF
diff --git a/test/ELF/linkerscript/data-segment-relro.s b/test/ELF/linkerscript/data-segment-relro.s
new file mode 100644
index 000000000000..7f69319dde7a
--- /dev/null
+++ b/test/ELF/linkerscript/data-segment-relro.s
@@ -0,0 +1,70 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o
+# RUN: ld.lld -shared %t2.o -o %t2.so
+
+# RUN: echo "SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
+# RUN: .plt : { *(.plt) } \
+# RUN: .text : { *(.text) } \
+# RUN: . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); \
+# RUN: .dynamic : { *(.dynamic) } \
+# RUN: .got : { *(.got) } \
+# RUN: . = DATA_SEGMENT_RELRO_END (1 ? 24 : 0, .); \
+# RUN: .got.plt : { *(.got.plt) } \
+# RUN: .data : { *(.data) } \
+# RUN: .bss : { *(.bss) } \
+# RUN: . = DATA_SEGMENT_END (.); \
+# RUN: }" > %t.script
+
+## With relro or without DATA_SEGMENT_RELRO_END just aligns to
+## page boundary.
+# RUN: ld.lld -z norelro %t1.o %t2.so --script %t.script -o %t
+# RUN: llvm-readobj -s %t | FileCheck %s
+# RUN: ld.lld -z relro %t1.o %t2.so --script %t.script -o %t2
+# RUN: llvm-readobj -s %t2 | FileCheck %s
+
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .got
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x10F0
+# CHECK-NEXT: Offset: 0x10F0
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Link:
+# CHECK-NEXT: Info:
+# CHECK-NEXT: AddressAlignment:
+# CHECK-NEXT: EntrySize:
+# CHECK-NEXT: }
+# CHECK-NEXT: Section {
+# CHECK-NEXT: Index:
+# CHECK-NEXT: Name: .got.plt
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x2000
+# CHECK-NEXT: Offset: 0x2000
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Link:
+# CHECK-NEXT: Info:
+# CHECK-NEXT: AddressAlignment:
+# CHECK-NEXT: EntrySize:
+# CHECK-NEXT: }
+
+.global _start
+_start:
+ .long bar
+ jmp *bar2@GOTPCREL(%rip)
+
+.section .data,"aw"
+.quad 0
+
+.zero 4
+.section .foo,"aw"
+.section .bss,"",@nobits
diff --git a/test/ELF/linkerscript/define.s b/test/ELF/linkerscript/define.s
new file mode 100644
index 000000000000..b5f0b76e9e6b
--- /dev/null
+++ b/test/ELF/linkerscript/define.s
@@ -0,0 +1,25 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# RUN: echo "SECTIONS \
+# RUN: { \
+# RUN: . = DEFINED(defined) ? 0x11000 : .; \
+# RUN: .foo : { *(.foo*) } \
+# RUN: . = DEFINED(notdefined) ? 0x12000 : 0x13000; \
+# RUN: .bar : { *(.bar*) } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+
+# CHECK: 1 .foo 00000008 0000000000011000 DATA
+# CHECK: 2 .bar 00000008 0000000000013000 DATA
+# CHECK: 3 .text 00000000 0000000000013008 TEXT DATA
+
+.global defined
+defined = 0
+
+.section .foo,"a"
+.quad 1
+
+.section .bar,"a"
+.quad 1
diff --git a/test/ELF/linkerscript/diagnostic.s b/test/ELF/linkerscript/diagnostic.s
new file mode 100644
index 000000000000..bd1ebd01b5d2
--- /dev/null
+++ b/test/ELF/linkerscript/diagnostic.s
@@ -0,0 +1,106 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+## Take some valid script with multiline comments
+## and check it actually works:
+# RUN: echo "SECTIONS {" > %t.script
+# RUN: echo ".text : { *(.text) }" >> %t.script
+# RUN: echo ".keep : { *(.keep) } /*" >> %t.script
+# RUN: echo "comment line 1" >> %t.script
+# RUN: echo "comment line 2 */" >> %t.script
+# RUN: echo ".temp : { *(.temp) } }" >> %t.script
+# RUN: ld.lld -shared %t -o %t1 --script %t.script
+
+## Change ":" to "+" at line 2, check that error
+## message starts from correct line number:
+# RUN: echo "SECTIONS {" > %t.script
+# RUN: echo ".text + { *(.text) }" >> %t.script
+# RUN: echo ".keep : { *(.keep) } /*" >> %t.script
+# RUN: echo "comment line 1" >> %t.script
+# RUN: echo "comment line 2 */" >> %t.script
+# RUN: echo ".temp : { *(.temp) } }" >> %t.script
+# RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | FileCheck -check-prefix=ERR1 %s
+# ERR1: {{.*}}.script:2:
+
+## Change ":" to "+" at line 3 now, check correct error line number:
+# RUN: echo "SECTIONS {" > %t.script
+# RUN: echo ".text : { *(.text) }" >> %t.script
+# RUN: echo ".keep + { *(.keep) } /*" >> %t.script
+# RUN: echo "comment line 1" >> %t.script
+# RUN: echo "comment line 2 */" >> %t.script
+# RUN: echo ".temp : { *(.temp) } }" >> %t.script
+# RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | FileCheck -check-prefix=ERR2 %s
+# ERR2: {{.*}}.script:3:
+
+## Change ":" to "+" at line 6, after multiline comment,
+## check correct error line number:
+# RUN: echo "SECTIONS {" > %t.script
+# RUN: echo ".text : { *(.text) }" >> %t.script
+# RUN: echo ".keep : { *(.keep) } /*" >> %t.script
+# RUN: echo "comment line 1" >> %t.script
+# RUN: echo "comment line 2 */" >> %t.script
+# RUN: echo ".temp + { *(.temp) } }" >> %t.script
+# RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | FileCheck -check-prefix=ERR5 %s
+# ERR5: {{.*}}.script:6:
+
+## Check that text of lines and pointer to 'bad' token are working ok.
+# RUN: echo "UNKNOWN_TAG {" > %t.script
+# RUN: echo ".text : { *(.text) }" >> %t.script
+# RUN: echo ".keep : { *(.keep) }" >> %t.script
+# RUN: echo ".temp : { *(.temp) } }" >> %t.script
+# RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | \
+# RUN: FileCheck -check-prefix=ERR6 -strict-whitespace %s
+# ERR6: error: {{.*}}.script:1:
+# ERR6-NEXT: error: {{.*}}.script:1: UNKNOWN_TAG {
+# ERR6-NEXT: error: {{.*}}.script:1: ^
+
+## One more check that text of lines and pointer to 'bad' token are working ok.
+# RUN: echo "SECTIONS {" > %t.script
+# RUN: echo ".text : { *(.text) }" >> %t.script
+# RUN: echo ".keep : { *(.keep) }" >> %t.script
+# RUN: echo "boom .temp : { *(.temp) } }" >> %t.script
+# RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | \
+# RUN: FileCheck -check-prefix=ERR7 -strict-whitespace %s
+# ERR7: error: {{.*}}.script:4: malformed number: .temp
+# ERR7-NEXT: error: {{.*}}.script:4: boom .temp : { *(.temp) } }
+# ERR7-NEXT: error: {{.*}}.script:4: ^
+
+## Check tokenize() error
+# RUN: echo "SECTIONS {}" > %t.script
+# RUN: echo "\"" >> %t.script
+# RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | \
+# RUN: FileCheck -check-prefix=ERR8 -strict-whitespace %s
+# ERR8: {{.*}}.script:2: unclosed quote
+
+## Check tokenize() error in included script file
+# RUN: echo "SECTIONS {}" > %t.script.inc
+# RUN: echo "\"" >> %t.script.inc
+# RUN: echo "INCLUDE \"%t.script.inc\"" > %t.script
+# RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | \
+# RUN: FileCheck -check-prefix=ERR9 -strict-whitespace %s
+# ERR9: {{.*}}.script.inc:2: unclosed quote
+
+## Check error reporting correctness for included files.
+# RUN: echo "SECTIONS {" > %t.script.inc
+# RUN: echo ".text : { *(.text) }" >> %t.script.inc
+# RUN: echo ".keep : { *(.keep) }" >> %t.script.inc
+# RUN: echo "boom .temp : { *(.temp) } }" >> %t.script.inc
+# RUN: echo "INCLUDE \"%t.script.inc\"" > %t.script
+# RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | \
+# RUN: FileCheck -check-prefix=ERR10 -strict-whitespace %s
+# ERR10: error: {{.*}}.script.inc:4: malformed number: .temp
+# ERR10-NEXT: error: {{.*}}.script.inc:4: boom .temp : { *(.temp) } }
+# ERR10-NEXT: error: {{.*}}.script.inc:4: ^
+
+## Check error reporting in script with INCLUDE directive.
+# RUN: echo "SECTIONS {" > %t.script.inc
+# RUN: echo ".text : { *(.text) }" >> %t.script.inc
+# RUN: echo ".keep : { *(.keep) }" >> %t.script.inc
+# RUN: echo ".temp : { *(.temp) } }" >> %t.script.inc
+# RUN: echo "/* One line before INCLUDE */" > %t.script
+# RUN: echo "INCLUDE \"%t.script.inc\"" >> %t.script
+# RUN: echo "/* One line ater INCLUDE */" >> %t.script
+# RUN: echo "Error" >> %t.script
+# RUN: not ld.lld -shared %t -o %t1 --script %t.script 2>&1 | \
+# RUN: FileCheck -check-prefix=ERR11 -strict-whitespace %s
+# ERR11: error: {{.*}}.script:4: unexpected EOF
diff --git a/test/ELF/linkerscript/discard-interp.s b/test/ELF/linkerscript/discard-interp.s
new file mode 100644
index 000000000000..261509e2e76b
--- /dev/null
+++ b/test/ELF/linkerscript/discard-interp.s
@@ -0,0 +1,12 @@
+// RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %s -o %t.o
+// RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %p/../Inputs/shared.s -o %t2.o
+// RUN: ld.lld -shared %t2.o -o %t2.so
+// RUN: echo "PHDRS { text PT_LOAD FILEHDR PHDRS; } \
+// RUN: SECTIONS { . = SIZEOF_HEADERS; .text : { *(.text) } : text }" > %t.script
+// RUN: ld.lld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -rpath foo -rpath bar --script %t.script --export-dynamic %t.o %t2.so -o %t
+// RUN: llvm-readobj -s %t | FileCheck %s
+
+// CHECK-NOT: Name: .interp
+
+.global _start
+_start:
diff --git a/test/ELF/linkerscript/discard-section.s b/test/ELF/linkerscript/discard-section.s
new file mode 100644
index 000000000000..a5af90806829
--- /dev/null
+++ b/test/ELF/linkerscript/discard-section.s
@@ -0,0 +1,14 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { /DISCARD/ : { *(.aaa*) } }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+
+# CHECK-NOT: .aaa
+
+.section .aaa,"a"
+aab:
+ .quad 0
+
+.section .zzz,"a"
+ .quad aab
diff --git a/test/ELF/linkerscript/dot-is-not-abs.s b/test/ELF/linkerscript/dot-is-not-abs.s
new file mode 100644
index 000000000000..4532cd59f2a1
--- /dev/null
+++ b/test/ELF/linkerscript/dot-is-not-abs.s
@@ -0,0 +1,53 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+
+# RUN: echo "SECTIONS { .text : { *(.text) } \
+# RUN: foo = .; \
+# RUN: .bar : { *(.bar) } }" > %t1.script
+# RUN: ld.lld -o %t1 --script %t1.script %t.o -shared
+# RUN: llvm-readobj -t -s -section-data %t1 | FileCheck %s
+
+.hidden foo
+.long foo - .
+
+.section .bar, "a"
+.long 0
+
+# The symbol foo is defined as a position in the file. This means that it is
+# not absolute and it is possible to compute the distance from foo to some other
+# position in the file. The symbol is not really in any output section, but
+# ELF has no magic constant for not absolute, but not in any section.
+# Fortunately the value of a symbol in a non relocatable file is a virtual
+# address, so the section can be arbitrary.
+
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .text
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_EXECINSTR
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x0
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 4
+# CHECK-NEXT: Link:
+# CHECK-NEXT: Info:
+# CHECK-NEXT: AddressAlignment:
+# CHECK-NEXT: EntrySize:
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 04000000 |
+# CHECK-NEXT: )
+# CHECK-NEXT: }
+
+# CHECK: Symbol {
+# CHECK: Name: foo
+# CHECK-NEXT: Value: 0x4
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Local
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other [
+# CHECK-NEXT: STV_HIDDEN
+# CHECK-NEXT: ]
+# CHECK-NEXT: Section: .text
+# CHECK-NEXT: }
diff --git a/test/ELF/linkerscript/double-bss.s b/test/ELF/linkerscript/double-bss.s
new file mode 100644
index 000000000000..c24332f5e5a6
--- /dev/null
+++ b/test/ELF/linkerscript/double-bss.s
@@ -0,0 +1,21 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { . = SIZEOF_HEADERS; " > %t.script
+# RUN: echo ".text : { *(.text*) }" >> %t.script
+# RUN: echo ".bss1 : { *(.bss) }" >> %t.script
+# RUN: echo ".bss2 : { *(COMMON) }" >> %t.script
+# RUN: echo "}" >> %t.script
+
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+# CHECK: .bss1 00000004 0000000000000122 BSS
+# CHECK-NEXT: .bss2 00000080 0000000000000128 BSS
+
+.globl _start
+_start:
+ jmp _start
+
+.bss
+.zero 4
+
+.comm q,128,8
diff --git a/test/ELF/linkerscript/dynamic-sym.s b/test/ELF/linkerscript/dynamic-sym.s
new file mode 100644
index 000000000000..e3193b1a765d
--- /dev/null
+++ b/test/ELF/linkerscript/dynamic-sym.s
@@ -0,0 +1,17 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "_DYNAMIC = 0x123;" > %t.script
+# RUN: ld.lld -T %t.script %t.o -shared -o %t.so
+# RUN: llvm-readobj -t %t.so | FileCheck %s
+
+# CHECK: Symbol {
+# CHECK: Name: _DYNAMIC
+# CHECK-NEXT: Value: 0x123
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Local
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other [
+# CHECK-NEXT: STV_HIDDEN
+# CHECK-NEXT: ]
+# CHECK-NEXT: Section: Absolute
+# CHECK-NEXT: }
diff --git a/test/ELF/linkerscript/dynamic.s b/test/ELF/linkerscript/dynamic.s
new file mode 100644
index 000000000000..ab65ab831f8f
--- /dev/null
+++ b/test/ELF/linkerscript/dynamic.s
@@ -0,0 +1,28 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o
+# RUN: ld.lld -shared %t2.o -o %t2.so
+
+# RUN: echo "SECTIONS { }" > %t.script
+# RUN: ld.lld %t1.o %t2.so -o %t
+# RUN: llvm-readobj -dynamic-table %t | FileCheck %s
+
+# CHECK: DynamicSection [
+# CHECK-NEXT: Tag Type Name/Value
+# CHECK: 0x0000000000000021 PREINIT_ARRAYSZ 9 (bytes)
+# CHECK: 0x000000000000001B INIT_ARRAYSZ 8 (bytes)
+# CHECK: 0x000000000000001C FINI_ARRAYSZ 10 (bytes)
+
+.globl _start
+_start:
+
+.section .init_array,"aw",@init_array
+ .quad 0
+
+.section .preinit_array,"aw",@preinit_array
+ .quad 0
+ .byte 0
+
+.section .fini_array,"aw",@fini_array
+ .quad 0
+ .short 0
diff --git a/test/ELF/linkerscript/edata-etext.s b/test/ELF/linkerscript/edata-etext.s
new file mode 100644
index 000000000000..fdf5ec1f5671
--- /dev/null
+++ b/test/ELF/linkerscript/edata-etext.s
@@ -0,0 +1,20 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { }" > %t.script
+# RUN: not ld.lld %t.o -script %t.script -o %t 2>&1 | FileCheck %s
+# CHECK: error: {{.*}}:(.text+0x0): undefined symbol '_edata'
+# CHECK: error: {{.*}}:(.text+0x8): undefined symbol '_etext'
+# CHECK: error: {{.*}}:(.text+0x10): undefined symbol '_end'
+
+.global _start,_end,_etext,_edata
+.text
+_start:
+ .quad _edata + 0x1
+ .quad _etext + 0x1
+ .quad _end + 0x1
+
+.data
+ .word 1
+.bss
+ .align 4
+ .space 6
diff --git a/test/ELF/linkerscript/eh-frame-hdr.s b/test/ELF/linkerscript/eh-frame-hdr.s
new file mode 100644
index 000000000000..d1545be632a3
--- /dev/null
+++ b/test/ELF/linkerscript/eh-frame-hdr.s
@@ -0,0 +1,20 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { \
+# RUN: .eh_frame_hdr : {} \
+# RUN: .eh_frame : {} \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --eh-frame-hdr --script %t.script %t
+# RUN: llvm-objdump -s -section=".eh_frame_hdr" %t1 | FileCheck %s
+
+# CHECK: 011b033b 14000000 01000000 49000000
+# CHECK-NEXT: 30000000
+
+.global _start
+_start:
+ nop
+
+.section .dah,"ax",@progbits
+.cfi_startproc
+ nop
+.cfi_endproc
diff --git a/test/ELF/linkerscript/ehdr_start.s b/test/ELF/linkerscript/ehdr_start.s
new file mode 100644
index 000000000000..0ae4a4dcfbc9
--- /dev/null
+++ b/test/ELF/linkerscript/ehdr_start.s
@@ -0,0 +1,11 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS { }" > %t.script
+# RUN: not ld.lld %t.o -script %t.script -o %t 2>&1 | FileCheck %s
+# CHECK: error: {{.*}}:(.text+0x0): undefined symbol '__ehdr_start'
+
+.text
+.global _start, __ehdr_start
+_start:
+ .quad __ehdr_start
diff --git a/test/ELF/linkerscript/empty-load.s b/test/ELF/linkerscript/empty-load.s
new file mode 100644
index 000000000000..0a87d5727474
--- /dev/null
+++ b/test/ELF/linkerscript/empty-load.s
@@ -0,0 +1,22 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { .rw : { *(.rw) } .text : { *(.text) } }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -private-headers %t1 | FileCheck %s
+
+## We expect 2 PT_LOAD segments
+# CHECK: PHDR
+# CHECK-NEXT: filesz {{0x[0-9a-f]+}} memsz {{0x[0-9a-f]+}} flags r--
+# CHECK-NEXT: LOAD
+# CHECK-NEXT: filesz {{0x[0-9a-f]+}} memsz {{0x[0-9a-f]+}} flags rw-
+# CHECK-NEXT: LOAD
+# CHECK-NEXT: filesz {{0x[0-9a-f]+}} memsz {{0x[0-9a-f]+}} flags r-x
+# CHECK-NEXT: STACK
+# CHECK-NEXT: filesz
+
+.globl _start
+_start:
+ jmp _start
+
+.section .rw, "aw"
+ .quad 0
diff --git a/test/ELF/linkerscript/empty-tls.s b/test/ELF/linkerscript/empty-tls.s
new file mode 100644
index 000000000000..919ccbffbe43
--- /dev/null
+++ b/test/ELF/linkerscript/empty-tls.s
@@ -0,0 +1,14 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: echo "PHDRS { ph_tls PT_TLS; }" > %t.script
+// RUN: ld.lld -o %t.so -T %t.script %t.o -shared
+// RUN: llvm-readobj -l %t.so | FileCheck %s
+
+// test that we don't crash with an empty PT_TLS
+
+// CHECK: Type: PT_TLS
+// CHECK-NEXT: Offset: 0x0
+// CHECK-NEXT: VirtualAddress: 0x0
+// CHECK-NEXT: PhysicalAddress: 0x0
+// CHECK-NEXT: FileSize: 0
+// CHECK-NEXT: MemSize: 0
diff --git a/test/ELF/linkerscript/entry.s b/test/ELF/linkerscript/entry.s
new file mode 100644
index 000000000000..d75b1345a8aa
--- /dev/null
+++ b/test/ELF/linkerscript/entry.s
@@ -0,0 +1,42 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# RUN: echo "ENTRY(_label)" > %t.script
+# RUN: ld.lld -o %t2 %t.script %t
+# RUN: llvm-readobj %t2 > /dev/null
+
+# The entry symbol should not cause an undefined error.
+# RUN: echo "ENTRY(_wrong_label)" > %t.script
+# RUN: ld.lld -o %t2 %t.script %t
+# RUN: ld.lld --entry=abc -o %t2 %t
+
+# -e has precedence over linker script's ENTRY.
+# RUN: echo "ENTRY(_label)" > %t.script
+# RUN: ld.lld -e _start -o %t2 %t.script %t
+# RUN: llvm-readobj -file-headers -symbols %t2 | \
+# RUN: FileCheck -check-prefix=OVERLOAD %s
+
+# OVERLOAD: Entry: [[ENTRY:0x[0-9A-F]+]]
+# OVERLOAD: Name: _start
+# OVERLOAD-NEXT: Value: [[ENTRY]]
+
+# The entry symbol can be a linker-script-defined symbol.
+# RUN: echo "ENTRY(foo); foo = 1;" > %t.script
+# RUN: ld.lld -o %t2 %t.script %t
+# RUN: llvm-readobj -file-headers -symbols %t2 | \
+# RUN: FileCheck -check-prefix=SCRIPT %s
+
+# SCRIPT: Entry: 0x1
+
+# RUN: echo "ENTRY(no_such_symbol);" > %t.script
+# RUN: ld.lld -o %t2 %t.script %t 2>&1 | \
+# RUN: FileCheck -check-prefix=MISSING %s
+
+# MISSING: warning: cannot find entry symbol no_such_symbol
+
+.globl _start, _label
+_start:
+ ret
+_label:
+ ret
diff --git a/test/ELF/linkerscript/exclude-multiple.s b/test/ELF/linkerscript/exclude-multiple.s
new file mode 100644
index 000000000000..46ec18cfa125
--- /dev/null
+++ b/test/ELF/linkerscript/exclude-multiple.s
@@ -0,0 +1,37 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %tfile1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/exclude-multiple1.s -o %tfile2.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/exclude-multiple2.s -o %tfile3.o
+# RUN: echo "SECTIONS { \
+# RUN: .foo : { *(.foo.1 EXCLUDE_FILE (*file1.o) .foo.2 EXCLUDE_FILE (*file2.o) .foo.3) } \
+# RUN: }" > %t1.script
+# RUN: ld.lld -script %t1.script %tfile1.o %tfile2.o %tfile3.o -o %t1.o
+# RUN: llvm-objdump -s %t1.o | FileCheck %s
+
+# CHECK: Contents of section .foo:
+# CHECK-NEXT: 01000000 00000000 04000000 00000000
+# CHECK-NEXT: 07000000 00000000 05000000 00000000
+# CHECK-NEXT: 08000000 00000000 03000000 00000000
+# CHECK-NEXT: 09000000 00000000
+# CHECK-NEXT: Contents of section .foo.2:
+# CHECK-NEXT: 02000000 00000000
+# CHECK-NEXT: Contents of section .foo.3:
+# CHECK-NEXT: 06000000 00000000
+
+# RUN: echo "SECTIONS { .foo : { *(EXCLUDE_FILE (*file1.o) EXCLUDE_FILE (*file2.o) .foo.3) } }" > %t2.script
+# RUN: not ld.lld -script %t2.script %tfile1.o %tfile2.o %tfile3.o -o %t2.o 2>&1 | \
+# RUN: FileCheck %s --check-prefix=ERR
+# ERR: section pattern is expected
+
+# RUN: echo "SECTIONS { .foo : { *(EXCLUDE_FILE (*file1.o)) } }" > %t3.script
+# RUN: not ld.lld -script %t3.script %tfile1.o %tfile2.o %tfile3.o -o %t2.o 2>&1 | \
+# RUN: FileCheck %s --check-prefix=ERR
+
+.section .foo.1,"a"
+ .quad 1
+
+.section .foo.2,"a"
+ .quad 2
+
+.section .foo.3,"a"
+ .quad 3
diff --git a/test/ELF/linkerscript/excludefile.s b/test/ELF/linkerscript/excludefile.s
new file mode 100644
index 000000000000..8a154354dbc2
--- /dev/null
+++ b/test/ELF/linkerscript/excludefile.s
@@ -0,0 +1,46 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \
+# RUN: %p/Inputs/include.s -o %t2
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \
+# RUN: %p/Inputs/notinclude.s -o %t3.notinclude
+
+# RUN: echo "SECTIONS {} " > %t.script
+# RUN: ld.lld -o %t --script %t.script %t1 %t2 %t3.notinclude
+# RUN: llvm-objdump -d %t | FileCheck %s
+
+# CHECK: Disassembly of section .text:
+# CHECK: _start:
+# CHECK-NEXT: : 48 c7 c0 3c 00 00 00 movq $60, %rax
+# CHECK-NEXT: : 48 c7 c7 2a 00 00 00 movq $42, %rdi
+# CHECK-NEXT: : 00 00 addb %al, (%rax)
+# CHECK: _potato:
+# CHECK-NEXT: : 90 nop
+# CHECK-NEXT: : 90 nop
+# CHECK-NEXT: : 00 00 addb %al, (%rax)
+# CHECK: tomato:
+# CHECK-NEXT: : b8 01 00 00 00 movl $1, %eax
+
+# RUN: echo "SECTIONS { .patatino : \
+# RUN: { KEEP(*(EXCLUDE_FILE(*notinclude) .text)) } }" \
+# RUN: > %t.script
+# RUN: ld.lld -o %t4 --script %t.script %t1 %t2 %t3.notinclude
+# RUN: llvm-objdump -d %t4 | FileCheck %s --check-prefix=EXCLUDE
+
+# EXCLUDE: Disassembly of section .patatino:
+# EXCLUDE: _start:
+# EXCLUDE-NEXT: : 48 c7 c0 3c 00 00 00 movq $60, %rax
+# EXCLUDE-NEXT: : 48 c7 c7 2a 00 00 00 movq $42, %rdi
+# EXCLUDE-NEXT: : 00 00 addb %al, (%rax)
+# EXCLUDE: _potato:
+# EXCLUDE-NEXT: : 90 nop
+# EXCLUDE-NEXT: : 90 nop
+# EXCLUDE: Disassembly of section .text:
+# EXCLUDE: tomato:
+# EXCLUDE-NEXT: : b8 01 00 00 00 movl $1, %eax
+
+.section .text
+.globl _start
+_start:
+ mov $60, %rax
+ mov $42, %rdi
diff --git a/test/ELF/linkerscript/extend-pt-load.s b/test/ELF/linkerscript/extend-pt-load.s
new file mode 100644
index 000000000000..f9a77c8c12bf
--- /dev/null
+++ b/test/ELF/linkerscript/extend-pt-load.s
@@ -0,0 +1,69 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+
+# This test demonstrates an odd consequence of the way we handle sections with just symbol
+# assignments.
+
+# First, run a test with no such section.
+
+# RUN: echo "SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
+# RUN: .dynsym : { } \
+# RUN: .hash : { } \
+# RUN: .dynstr : { } \
+# RUN: .text : { *(.text) } \
+# RUN: . = ALIGN(0x1000); \
+# RUN: .data.rel.ro : { *(.data.rel.ro) } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t.o -shared
+# RUN: llvm-readobj --elf-output-style=GNU -l -s %t1 | FileCheck --check-prefix=CHECK1 %s
+
+# CHECK1: .text PROGBITS 00000000000001bc 0001bc 000001 00 AX
+# CHECK1-NEXT: .data.rel.ro PROGBITS 0000000000001000 001000 000001 00 WA
+
+# CHECK1: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0001bd 0x0001bd R E
+# CHECK1-NEXT: LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x000068 0x000068 RW
+
+# Then add the section bar. Note how bar is given AX flags, which causes the PT_LOAD to now
+# cover the padding bits created by ALIGN.
+
+# RUN: echo "SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
+# RUN: .dynsym : { } \
+# RUN: .hash : { } \
+# RUN: .dynstr : { } \
+# RUN: .text : { *(.text) } \
+# RUN: . = ALIGN(0x1000); \
+# RUN: bar : { HIDDEN(bar_sym = .); } \
+# RUN: .data.rel.ro : { *(.data.rel.ro) } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t2 --script %t.script %t.o -shared
+# RUN: llvm-readobj --elf-output-style=GNU -l -s %t2 | FileCheck --check-prefix=CHECK2 %s
+
+# CHECK2: .text PROGBITS 00000000000001bc 0001bc 000001 00 AX
+# CHECK2-NEXT: bar PROGBITS 0000000000001000 001000 000000 00 AX
+# CHECK2-NEXT: .data.rel.ro PROGBITS 0000000000001000 001000 000001 00 WA
+
+# CHECK2: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x001000 0x001000 R E
+# CHECK2-NEXT: LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x000068 0x000068 RW
+
+# If the current behavior becomes a problem we should consider just moving the commands out
+# of the section. That is, handle the above like the following test.
+
+# RUN: echo "SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
+# RUN: .dynsym : { } \
+# RUN: .hash : { } \
+# RUN: .dynstr : { } \
+# RUN: .text : { *(.text) } \
+# RUN: . = ALIGN(0x1000); \
+# RUN: HIDDEN(bar_sym = .); \
+# RUN: .data.rel.ro : { *(.data.rel.ro) } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t3 --script %t.script %t.o -shared
+# RUN: llvm-readobj --elf-output-style=GNU -l -s %t3 | FileCheck --check-prefix=CHECK1 %s
+
+nop
+
+.section .data.rel.ro, "aw"
+.byte 0
diff --git a/test/ELF/linkerscript/filename-spec.s b/test/ELF/linkerscript/filename-spec.s
new file mode 100644
index 000000000000..d4bc495efb81
--- /dev/null
+++ b/test/ELF/linkerscript/filename-spec.s
@@ -0,0 +1,59 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %tfirst.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \
+# RUN: %p/Inputs/filename-spec.s -o %tsecond.o
+
+# RUN: echo "SECTIONS { .foo : { \
+# RUN: KEEP(*first.o(.foo)) \
+# RUN: KEEP(*second.o(.foo)) } }" > %t1.script
+# RUN: ld.lld -o %t1 --script %t1.script %tfirst.o %tsecond.o
+# RUN: llvm-objdump -s %t1 | FileCheck --check-prefix=FIRSTSECOND %s
+# FIRSTSECOND: Contents of section .foo:
+# FIRSTSECOND-NEXT: 01000000 00000000 11000000 00000000
+
+# RUN: echo "SECTIONS { .foo : { \
+# RUN: KEEP(*second.o(.foo)) \
+# RUN: KEEP(*first.o(.foo)) } }" > %t2.script
+# RUN: ld.lld -o %t2 --script %t2.script %tfirst.o %tsecond.o
+# RUN: llvm-objdump -s %t2 | FileCheck --check-prefix=SECONDFIRST %s
+# SECONDFIRST: Contents of section .foo:
+# SECONDFIRST-NEXT: 11000000 00000000 01000000 00000000
+
+## Now the same tests but without KEEP. Checking that file name inside
+## KEEP is parsed fine.
+# RUN: echo "SECTIONS { .foo : { \
+# RUN: *first.o(.foo) \
+# RUN: *second.o(.foo) } }" > %t3.script
+# RUN: ld.lld -o %t3 --script %t3.script %tfirst.o %tsecond.o
+# RUN: llvm-objdump -s %t3 | FileCheck --check-prefix=FIRSTSECOND %s
+
+# RUN: echo "SECTIONS { .foo : { \
+# RUN: *second.o(.foo) \
+# RUN: *first.o(.foo) } }" > %t4.script
+# RUN: ld.lld -o %t4 --script %t4.script %tfirst.o %tsecond.o
+# RUN: llvm-objdump -s %t4 | FileCheck --check-prefix=SECONDFIRST %s
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %T/filename-spec1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \
+# RUN: %p/Inputs/filename-spec.s -o %T/filename-spec2.o
+
+# RUN: echo "SECTIONS { .foo : { \
+# RUN: filename-spec2.o(.foo) \
+# RUN: filename-spec1.o(.foo) } }" > %t5.script
+# RUN: ld.lld -o %t5 --script %t5.script \
+# RUN: %T/filename-spec1.o %T/filename-spec2.o
+# RUN: llvm-objdump -s %t5 | FileCheck --check-prefix=SECONDFIRST %s
+
+# RUN: echo "SECTIONS { .foo : { \
+# RUN: filename-spec1.o(.foo) \
+# RUN: filename-spec2.o(.foo) } }" > %t6.script
+# RUN: ld.lld -o %t6 --script %t6.script \
+# RUN: %T/filename-spec1.o %T/filename-spec2.o
+# RUN: llvm-objdump -s %t6 | FileCheck --check-prefix=FIRSTSECOND %s
+
+.global _start
+_start:
+ nop
+
+.section .foo,"a"
+ .quad 1
diff --git a/test/ELF/linkerscript/fill.s b/test/ELF/linkerscript/fill.s
new file mode 100644
index 000000000000..57a19e0943d0
--- /dev/null
+++ b/test/ELF/linkerscript/fill.s
@@ -0,0 +1,30 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS { \
+# RUN: .out : { \
+# RUN: FILL(0x11111111); \
+# RUN: *(.aaa) \
+# RUN: . += 4; \
+# RUN: *(.bbb) \
+# RUN: . += 4; \
+# RUN: FILL(0x22222222); \
+# RUN: . += 4; \
+# RUN: } \
+# RUN: }; " > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o
+# RUN: llvm-objdump -s %t | FileCheck %s
+
+# CHECK: Contents of section .out:
+# CHECK-NEXT: aa222222 22bb2222 22222222 2222
+
+.text
+.globl _start
+_start:
+
+.section .aaa, "a"
+.align 1
+.byte 0xAA
+
+.section .bbb, "a"
+.align 1
+.byte 0xBB
diff --git a/test/ELF/linkerscript/group.s b/test/ELF/linkerscript/group.s
new file mode 100644
index 000000000000..53a1c89ebfe3
--- /dev/null
+++ b/test/ELF/linkerscript/group.s
@@ -0,0 +1,56 @@
+# REQUIRES: x86
+
+# RUN: mkdir -p %t.dir
+# RUN: rm -f %t.dir/libxyz.a
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \
+# RUN: %p/Inputs/libsearch-st.s -o %t2.o
+# RUN: llvm-ar rcs %t.dir/libxyz.a %t2.o
+
+# RUN: echo "GROUP(\"%t\")" > %t.script
+# RUN: ld.lld -o %t2 %t.script
+# RUN: llvm-readobj %t2 > /dev/null
+
+# RUN: echo "INPUT(\"%t\")" > %t.script
+# RUN: ld.lld -o %t2 %t.script
+# RUN: llvm-readobj %t2 > /dev/null
+
+# RUN: echo "GROUP(\"%t\" libxyz.a )" > %t.script
+# RUN: not ld.lld -o %t2 %t.script 2>/dev/null
+# RUN: ld.lld -o %t2 %t.script -L%t.dir
+# RUN: llvm-readobj %t2 > /dev/null
+
+# RUN: echo "GROUP(\"%t\" =libxyz.a )" > %t.script
+# RUN: not ld.lld -o %t2 %t.script 2>/dev/null
+# RUN: ld.lld -o %t2 %t.script --sysroot=%t.dir
+# RUN: llvm-readobj %t2 > /dev/null
+
+# RUN: echo "GROUP(\"%t\" -lxyz )" > %t.script
+# RUN: not ld.lld -o %t2 %t.script 2>/dev/null
+# RUN: ld.lld -o %t2 %t.script -L%t.dir
+# RUN: llvm-readobj %t2 > /dev/null
+
+# RUN: echo "GROUP(\"%t\" libxyz.a )" > %t.script
+# RUN: not ld.lld -o %t2 %t.script 2>/dev/null
+# RUN: ld.lld -o %t2 %t.script -L%t.dir
+# RUN: llvm-readobj %t2 > /dev/null
+
+# RUN: echo "GROUP(\"%t\" /libxyz.a )" > %t.script
+# RUN: echo "GROUP(\"%t\" /libxyz.a )" > %t.dir/xyz.script
+# RUN: not ld.lld -o %t2 %t.script 2>/dev/null
+# RUN: not ld.lld -o %t2 %t.script --sysroot=%t.dir 2>/dev/null
+# RUN: ld.lld -o %t2 %t.dir/xyz.script --sysroot=%t.dir
+# RUN: llvm-readobj %t2 > /dev/null
+
+# RUN: echo "GROUP(\"%t.script2\")" > %t.script1
+# RUN: echo "GROUP(\"%t\")" > %t.script2
+# RUN: ld.lld -o %t2 %t.script1
+# RUN: llvm-readobj %t2 > /dev/null
+
+# RUN: echo "GROUP(AS_NEEDED(\"%t\"))" > %t.script
+# RUN: ld.lld -o %t2 %t.script
+# RUN: llvm-readobj %t2 > /dev/null
+
+.globl _start
+_start:
+ ret
diff --git a/test/ELF/linkerscript/header-addr.s b/test/ELF/linkerscript/header-addr.s
new file mode 100644
index 000000000000..f37d319aaabe
--- /dev/null
+++ b/test/ELF/linkerscript/header-addr.s
@@ -0,0 +1,47 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "PHDRS {all PT_LOAD PHDRS;} \
+# RUN: SECTIONS { \
+# RUN: . = 0x2000; \
+# RUN: .text : {*(.text)} :all \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t.so --script %t.script %t.o -shared
+# RUN: llvm-readobj -program-headers %t.so | FileCheck %s
+
+# CHECK: ProgramHeaders [
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x40
+# CHECK-NEXT: VirtualAddress: 0x1040
+# CHECK-NEXT: PhysicalAddress: 0x1040
+# CHECK-NEXT: FileSize: 4176
+# CHECK-NEXT: MemSize: 4176
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R (0x4)
+# CHECK-NEXT: PF_W (0x2)
+# CHECK-NEXT: PF_X (0x1)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 4096
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+# RUN: ld.lld -o %t2.so --script %t.script %t.o -shared -z max-page-size=0x2000
+# RUN: llvm-readobj -program-headers %t2.so \
+# RUN: | FileCheck --check-prefix=MAXPAGE %s
+
+# MAXPAGE: ProgramHeaders [
+# MAXPAGE-NEXT: ProgramHeader {
+# MAXPAGE-NEXT: Type: PT_LOAD
+# MAXPAGE-NEXT: Offset: 0x40
+# MAXPAGE-NEXT: VirtualAddress: 0x40
+# MAXPAGE-NEXT: PhysicalAddress: 0x40
+# MAXPAGE-NEXT: FileSize: 8272
+# MAXPAGE-NEXT: MemSize: 8272
+# MAXPAGE-NEXT: Flags [
+# MAXPAGE-NEXT: PF_R
+# MAXPAGE-NEXT: PF_W
+# MAXPAGE-NEXT: PF_X
+# MAXPAGE-NEXT: ]
+# MAXPAGE-NEXT: Alignment: 8192
+# MAXPAGE-NEXT: }
+# MAXPAGE-NEXT: ]
diff --git a/test/ELF/linkerscript/implicit-program-header.s b/test/ELF/linkerscript/implicit-program-header.s
new file mode 100644
index 000000000000..9598a6abebb0
--- /dev/null
+++ b/test/ELF/linkerscript/implicit-program-header.s
@@ -0,0 +1,13 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld -o %t1 --script %S/Inputs/implicit-program-header.script \
+# RUN: %t.o -shared
+# RUN: llvm-readobj -elf-output-style=GNU -l %t1 | FileCheck %s
+
+# CHECK: Segment Sections...
+# CHECK-NEXT: 00 .text .dynsym .hash .dynstr .dynamic
+# CHECK-NEXT: 01 .foo
+
+.quad 0
+.section .foo,"ax"
+.quad 0
diff --git a/test/ELF/linkerscript/input-order.s b/test/ELF/linkerscript/input-order.s
new file mode 100644
index 000000000000..cb9ea354cdbf
--- /dev/null
+++ b/test/ELF/linkerscript/input-order.s
@@ -0,0 +1,38 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# This test case should place input sections in script order:
+# .foo.1 .foo.2 .bar.1 .bar.2
+# RUN: echo "SECTIONS { . = 0x1000; .foo : {*(.foo.*) *(.bar.*) } }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -section=.foo -s %t1 | FileCheck --check-prefix=SCRIPT_ORDER %s
+# SCRIPT_ORDER: Contents of section .foo:
+# SCRIPT_ORDER-NEXT: 1000 00000000 00000000 ffffffff eeeeeeee
+
+# This test case should place input sections in native order:
+# .bar.1 .foo.1 .bar.2 .foo.2
+# RUN: echo "SECTIONS { . = 0x1000; .foo : {*(.foo.* .bar.*)} }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -section=.foo -s %t1 | FileCheck --check-prefix=FILE_ORDER %s
+# FILE_ORDER: Contents of section .foo:
+# FILE_ORDER-NEXT: 1000 ffffffff 00000000 eeeeeeee 00000000
+
+.global _start
+_start:
+ nop
+
+.section .bar.1,"a"
+bar1:
+ .long 0xFFFFFFFF
+
+.section .foo.1,"a"
+foo1:
+ .long 0
+
+.section .bar.2,"a"
+bar2:
+ .long 0xEEEEEEEE
+
+.section .foo.2,"a"
+foo2:
+ .long 0
diff --git a/test/ELF/linkerscript/input-sec-dup.s b/test/ELF/linkerscript/input-sec-dup.s
new file mode 100644
index 000000000000..fd889393c74c
--- /dev/null
+++ b/test/ELF/linkerscript/input-sec-dup.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# RUN: echo "SECTIONS {.foo : { *(.foo) *(.foo) } }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+# CHECK: Sections:
+# CHECK-NEXT: Idx Name Size
+# CHECK-NEXT: 0 00000000
+# CHECK-NEXT: 1 .foo 00000004
+# CHECK-NEXT: 2 .text 00000001
+
+.global _start
+_start:
+ nop
+
+.section .foo,"a"
+ .long 0
diff --git a/test/ELF/linkerscript/linkerscript.s b/test/ELF/linkerscript/linkerscript.s
new file mode 100644
index 000000000000..cac902af4270
--- /dev/null
+++ b/test/ELF/linkerscript/linkerscript.s
@@ -0,0 +1,54 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \
+# RUN: %p/Inputs/libsearch-st.s -o %t2.o
+
+# RUN: echo "EXTERN( undef undef2 )" > %t.script
+# RUN: ld.lld %t -o %t2 %t.script
+# RUN: llvm-readobj %t2 > /dev/null
+
+# RUN: echo "OUTPUT_FORMAT(elf64-x86-64) /*/*/ GROUP(\"%t\" )" > %t.script
+# RUN: ld.lld -o %t2 %t.script
+# RUN: llvm-readobj %t2 > /dev/null
+
+# RUN: rm -f %t.out
+# RUN: echo "OUTPUT(\"%t.out\")" > %t.script
+# RUN: ld.lld %t.script %t
+# RUN: llvm-readobj %t.out > /dev/null
+
+# RUN: echo "SEARCH_DIR(/lib/foo/blah)" > %t.script
+# RUN: ld.lld %t.script %t
+# RUN: llvm-readobj %t.out > /dev/null
+
+# RUN: echo ";SEARCH_DIR(x);SEARCH_DIR(y);" > %t.script
+# RUN: ld.lld %t.script %t
+# RUN: llvm-readobj %t.out > /dev/null
+
+# RUN: echo ";" > %t.script
+# RUN: ld.lld %t.script %t
+# RUN: llvm-readobj %t.out > /dev/null
+
+# RUN: echo "INCLUDE \"%t.script2\" OUTPUT(\"%t.out\")" > %t.script1
+# RUN: echo "GROUP(\"%t\")" > %t.script2
+# RUN: ld.lld %t.script1
+# RUN: llvm-readobj %t2 > /dev/null
+
+# RUN: echo "INCLUDE \"foo.script\"" > %t.script
+# RUN: echo "OUTPUT(\"%t.out\")" > %T/foo.script
+# RUN: not ld.lld %t.script > %t.log 2>&1
+# RUN: FileCheck -check-prefix=INCLUDE_ERR %s < %t.log
+# INCLUDE_ERR: error: {{.+}}.script:1: cannot open foo.script
+# INCLUDE_ERR-NEXT: error: {{.+}}.script:1: INCLUDE "foo.script"
+# RUN: ld.lld -L %T %t.script %t
+
+# RUN: echo "FOO(BAR)" > %t.script
+# RUN: not ld.lld -o foo %t.script > %t.log 2>&1
+# RUN: FileCheck -check-prefix=ERR1 %s < %t.log
+
+# ERR1: unknown directive: FOO
+
+.globl _start, _label
+_start:
+ ret
+_label:
+ ret
diff --git a/test/ELF/linkerscript/loadaddr.s b/test/ELF/linkerscript/loadaddr.s
new file mode 100644
index 000000000000..33c5f21b52ba
--- /dev/null
+++ b/test/ELF/linkerscript/loadaddr.s
@@ -0,0 +1,42 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x1000; \
+# RUN: .aaa : AT(0x2000) { *(.aaa) } \
+# RUN: .bbb : { *(.bbb) } \
+# RUN: .ccc : AT(0x3000) { *(.ccc) } \
+# RUN: .ddd : AT(0x4000) { *(.ddd) } \
+# RUN: .text : { *(.text) } \
+# RUN: aaa_lma = LOADADDR(.aaa); \
+# RUN: bbb_lma = LOADADDR(.bbb); \
+# RUN: ccc_lma = LOADADDR(.ccc); \
+# RUN: ddd_lma = LOADADDR(.ddd); \
+# RUN: txt_lma = LOADADDR(.text); \
+# RUN: }" > %t.script
+# RUN: ld.lld %t --script %t.script -o %t2
+# RUN: llvm-objdump -t %t2 | FileCheck %s
+# RUN: echo "SECTIONS { v = LOADADDR(.zzz); }" > %t.script
+# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | FileCheck --check-prefix=ERROR %s
+
+# CHECK: 0000000000002000 *ABS* 00000000 aaa_lma
+# CHECK-NEXT: 0000000000002008 *ABS* 00000000 bbb_lma
+# CHECK-NEXT: 0000000000003000 *ABS* 00000000 ccc_lma
+# CHECK-NEXT: 0000000000004000 *ABS* 00000000 ddd_lma
+# CHECK-NEXT: 0000000000004008 *ABS* 00000000 txt_lma
+# ERROR: {{.*}}.script:1: undefined section .zzz
+
+.global _start
+_start:
+ nop
+
+.section .aaa, "a"
+.quad 0
+
+.section .bbb, "a"
+.quad 0
+
+.section .ccc, "a"
+.quad 0
+
+.section .ddd, "a"
+.quad 0
diff --git a/test/ELF/linkerscript/locationcounter.s b/test/ELF/linkerscript/locationcounter.s
new file mode 100644
index 000000000000..c57a17c5c9cb
--- /dev/null
+++ b/test/ELF/linkerscript/locationcounter.s
@@ -0,0 +1,189 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { \
+# RUN: . = 0xFFF0; \
+# RUN: . = . + 0x10; \
+# RUN: .plus : { *(.plus) } \
+# RUN: . = 0x11010 - 0x10; \
+# RUN: .minus : { *(.minus) } \
+# RUN: . = 0x24000 / 0x2; \
+# RUN: .div : { *(.div) } \
+# RUN: . = 0x11000 + 0x1000 * 0x2; \
+# RUN: .mul : { *(.mul) } \
+# RUN: . = 0x10000 + (0x1000 + 0x1000) * 0x2; \
+# RUN: .bracket : { *(.bracket) } \
+# RUN: . = 0x17000 & 0x15000; \
+# RUN: .and : { *(.and) } \
+# RUN: . = 0x1 ? 0x16000 : 0x999999; \
+# RUN: .ternary1 : { *(.ternary1) } \
+# RUN: . = 0x0 ? 0x999999 : 0x17000; \
+# RUN: .ternary2 : { *(.ternary2) } \
+# RUN: . = 0x0 < 0x1 ? 0x18000 : 0x999999; \
+# RUN: .less : { *(.less) } \
+# RUN: . = 0x1 <= 0x1 ? 0x19000 : 0x999999; \
+# RUN: .lesseq : { *(.lesseq) } \
+# RUN: . = 0x1 > 0x0 ? 0x20000 : 0x999999; \
+# RUN: .great : { *(.great) } \
+# RUN: . = 0x1 >= 0x1 ? 0x21000 : 0x999999; \
+# RUN: .greateq : { *(.greateq) } \
+# RUN: . = 0x1 == 0x1 ? 0x22000 : 0x999999; \
+# RUN: .eq : { *(.eq) } \
+# RUN: . = 0x2 != 0x1 ? 0x23000 : 0x999999; \
+# RUN: .neq : { *(.neq) } \
+# RUN: . = CONSTANT (MAXPAGESIZE) * 0x24; \
+# RUN: .maxpagesize : { *(.maxpagesize) } \
+# RUN: . = CONSTANT (COMMONPAGESIZE) * 0x25; \
+# RUN: .commonpagesize : { *(.commonpagesize) } \
+# RUN: . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); \
+# RUN: .datasegmentalign : { *(.datasegmentalign) } \
+# RUN: . = DATA_SEGMENT_END (.); \
+# RUN: . = 0x27000; \
+# RUN: . += 0x1000; \
+# RUN: .plusassign : { *(.plusassign) } \
+# RUN: . = ((. + 0x1fff) & ~(0x1000 + -1)); \
+# RUN: .unary : { *(.unary) } \
+# RUN: . = 0x30000 + (1 + 1 << 5); \
+# RUN: .shiftl : { *(.shiftl) } \
+# RUN: . = 0x30000 + (1 + 1023 >> 2); \
+# RUN: .shiftr : { *(.shiftr) } \
+
+# RUN: }" > %t.script
+# RUN: ld.lld %t --script %t.script -o %t2
+# RUN: llvm-objdump -section-headers %t2 | FileCheck %s
+
+# CHECK: .plus {{.*}} 0000000000010000
+# CHECK: .minus {{.*}} 0000000000011000
+# CHECK: .div {{.*}} 0000000000012000
+# CHECK: .mul {{.*}} 0000000000013000
+# CHECK: .bracket {{.*}} 0000000000014000
+# CHECK: .and {{.*}} 0000000000015000
+# CHECK: .ternary1 {{.*}} 0000000000016000
+# CHECK: .ternary2 {{.*}} 0000000000017000
+# CHECK: .less {{.*}} 0000000000018000
+# CHECK: .lesseq {{.*}} 0000000000019000
+# CHECK: .great {{.*}} 0000000000020000
+# CHECK: .greateq {{.*}} 0000000000021000
+# CHECK: .eq {{.*}} 0000000000022000
+# CHECK: .neq {{.*}} 0000000000023000
+# CHECK: .maxpagesize {{.*}} 0000000000024000
+# CHECK: .commonpagesize {{.*}} 0000000000025000
+# CHECK: .datasegmentalign {{.*}} 0000000000026000
+# CHECK: .plusassign {{.*}} 0000000000028000
+# CHECK: .unary {{.*}} 000000000002a000
+# CHECK: .shiftl {{.*}} 0000000000030040
+# CHECK: .shiftr {{.*}} 0000000000030100
+
+## Mailformed number error.
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x12Q41; \
+# RUN: }" > %t.script
+# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \
+# RUN: FileCheck --check-prefix=NUMERR %s
+# NUMERR: malformed number: 0x12Q41
+
+## Missing closing bracket.
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x10000 + (0x1000 + 0x1000 * 0x2; \
+# RUN: }" > %t.script
+# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \
+# RUN: FileCheck --check-prefix=BRACKETERR %s
+# BRACKETERR: ) expected, but got ;
+
+## Missing opening bracket.
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x10000 + 0x1000 + 0x1000) * 0x2; \
+# RUN: }" > %t.script
+# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \
+# RUN: FileCheck --check-prefix=BRACKETERR2 %s
+# BRACKETERR2: ; expected, but got )
+
+## Empty expression.
+# RUN: echo "SECTIONS { \
+# RUN: . = ; \
+# RUN: }" > %t.script
+# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \
+# RUN: FileCheck --check-prefix=ERREXPR %s
+# ERREXPR: malformed number: ;
+
+## Div by zero error.
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x10000 / 0x0; \
+# RUN: }" > %t.script
+# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \
+# RUN: FileCheck --check-prefix=DIVZERO %s
+# DIVZERO: division by zero
+
+## Broken ternary operator expression.
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x1 ? 0x2; \
+# RUN: }" > %t.script
+# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \
+# RUN: FileCheck --check-prefix=TERNERR %s
+# TERNERR: : expected, but got ;
+
+.globl _start
+_start:
+nop
+
+.section .plus, "a"
+.quad 0
+
+.section .minus, "a"
+.quad 0
+
+.section .div, "a"
+.quad 0
+
+.section .mul, "a"
+.quad 0
+
+.section .bracket, "a"
+.quad 0
+
+.section .and, "a"
+.quad 0
+
+.section .ternary1, "a"
+.quad 0
+
+.section .ternary2, "a"
+.quad 0
+
+.section .less, "a"
+.quad 0
+
+.section .lesseq, "a"
+.quad 0
+
+.section .great, "a"
+.quad 0
+
+.section .greateq, "a"
+.quad 0
+
+.section .eq, "a"
+.quad 0
+
+.section .neq, "a"
+.quad 0
+
+.section .maxpagesize, "a"
+.quad 0
+
+.section .commonpagesize, "a"
+.quad 0
+
+.section .datasegmentalign, "a"
+.quad 0
+
+.section .plusassign, "a"
+.quad 0
+
+.section .unary, "a"
+.quad 0
+
+.section .shiftl, "a"
+.quad 0
+
+.section .shiftr, "a"
+.quad 0
diff --git a/test/ELF/linkerscript/locationcountererr.s b/test/ELF/linkerscript/locationcountererr.s
new file mode 100644
index 000000000000..7664d8d46917
--- /dev/null
+++ b/test/ELF/linkerscript/locationcountererr.s
@@ -0,0 +1,9 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { .text 0x2000 : {. = 0x10 ; *(.text) } }" > %t.script
+# RUN: not ld.lld %t --script %t.script -o %t1 2>&1 | FileCheck %s
+# CHECK: unable to move location counter backward for: .text
+
+.globl _start
+_start:
+nop
diff --git a/test/ELF/linkerscript/merge-sections-reloc.s b/test/ELF/linkerscript/merge-sections-reloc.s
new file mode 100644
index 000000000000..639341540350
--- /dev/null
+++ b/test/ELF/linkerscript/merge-sections-reloc.s
@@ -0,0 +1,16 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/merge-sections-reloc.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t2.o
+# RUN: echo "SECTIONS {}" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t1.o %t2.o
+# RUN: llvm-objdump -s %t | FileCheck %s
+
+## Check that sections content is not corrupted.
+# CHECK: Contents of section .text:
+# CHECK-NEXT: 44332211 00000000 44332211 00000000
+# CHECK-NEXT: f0ffffff ffffffff
+
+.globl _start
+_foo:
+ .quad 0x11223344
+ .quad _start - .
diff --git a/test/ELF/linkerscript/merge-sections.s b/test/ELF/linkerscript/merge-sections.s
new file mode 100644
index 000000000000..7252fe576674
--- /dev/null
+++ b/test/ELF/linkerscript/merge-sections.s
@@ -0,0 +1,94 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# RUN: echo "SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
+# RUN: .foo : { begin = .; *(.foo.*) end = .;} \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t -shared
+# RUN: llvm-readobj -s -t %t1 | FileCheck %s
+
+# CHECK: Name: .foo
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_MERGE
+# CHECK-NEXT: SHF_STRINGS
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x[[ADDR1:.*]]
+# CHECK-NEXT: Offset: 0x[[ADDR1]]
+# CHECK-NEXT: Size: 4
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 1
+# CHECK-NEXT: EntrySize: 1
+# CHECK-NEXT: }
+# CHECK-NEXT: Section {
+# CHECK-NEXT: Index:
+# CHECK-NEXT: Name: .foo
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_MERGE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x
+# CHECK-NEXT: Offset: 0x
+# CHECK-NEXT: Size: 1
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 1
+# CHECK-NEXT: EntrySize: 1
+# CHECK-NEXT: }
+# CHECK-NEXT: Section {
+# CHECK-NEXT: Index:
+# CHECK-NEXT: Name: .foo
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_MERGE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x
+# CHECK-NEXT: Offset: 0x
+# CHECK-NEXT: Size: 2
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 2
+# CHECK-NEXT: EntrySize: 2
+# CHECK-NEXT: }
+# CHECK-NEXT: Section {
+# CHECK-NEXT: Index:
+# CHECK-NEXT: Name:
+# CHECK-NEXT: Type:
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_EXECINSTR
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x[[ADDR2:.*]]
+# CHECK-NEXT: Offset: 0x[[ADDR2]]
+
+
+# CHECK: Name: begin
+# CHECK-NEXT: Value: 0x[[ADDR1]]
+
+# CHECK: Name: end
+# CHECK-NEXT: Value: 0x[[ADDR2]]
+
+.section .foo.1a,"aMS",@progbits,1
+.asciz "foo"
+
+.section .foo.1b,"aMS",@progbits,1
+.asciz "foo"
+
+.section .foo.2a,"aM",@progbits,1
+.byte 42
+
+.section .foo.2b,"aM",@progbits,1
+.byte 42
+
+.section .foo.3a,"aM",@progbits,2
+.align 2
+.short 42
+
+.section .foo.3b,"aM",@progbits,2
+.align 2
+.short 42
diff --git a/test/ELF/linkerscript/multi-sections-constraint.s b/test/ELF/linkerscript/multi-sections-constraint.s
new file mode 100644
index 000000000000..bfedd10fc285
--- /dev/null
+++ b/test/ELF/linkerscript/multi-sections-constraint.s
@@ -0,0 +1,34 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { \
+# RUN: .text : { *(.text) } \
+# RUN: . = 0x1000; .aaa : ONLY_IF_RO { *(.aaa.*) } \
+# RUN: . = 0x2000; .aaa : ONLY_IF_RW { *(.aaa.*) } } " > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+
+# CHECK: Sections:
+# CHECK-NEXT: Idx Name Size Address Type
+# CHECK: .aaa 00000010 0000000000002000 DATA
+
+
+# RUN: echo "SECTIONS { \
+# RUN: .text : { *(.text) } \
+# RUN: . = 0x1000; .aaa : ONLY_IF_RW { *(.aaa.*) } \
+# RUN: . = 0x2000; .aaa : ONLY_IF_RO { *(.aaa.*) } } " > %t2.script
+# RUN: ld.lld -o %t2 --script %t2.script %t
+# RUN: llvm-objdump -section-headers %t2 | FileCheck %s --check-prefix=REV
+
+# REV: Sections:
+# REV-NEXT: Idx Name Size Address Type
+# REV: .aaa 00000010 0000000000001000 DATA
+
+.global _start
+_start:
+ nop
+
+.section .aaa.1, "aw"
+.quad 1
+
+.section .aaa.2, "aw"
+.quad 1
diff --git a/test/ELF/linkerscript/multiple-tbss.s b/test/ELF/linkerscript/multiple-tbss.s
new file mode 100644
index 000000000000..6bbbca92c239
--- /dev/null
+++ b/test/ELF/linkerscript/multiple-tbss.s
@@ -0,0 +1,45 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { }" > %t.script
+# RUN: ld.lld -T %t.script %t.o -o %t
+# RUN: llvm-readobj -l -s %t | FileCheck %s
+
+# CHECK: Name: .tbss
+# CHECK-NEXT: Type: SHT_NOBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_TLS
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 8
+# CHECK-NEXT: Link:
+# CHECK-NEXT: Info:
+# CHECK-NEXT: AddressAlignment:
+# CHECK-NEXT: EntrySize:
+# CHECK-NEXT: }
+# CHECK-NEXT: Section {
+# CHECK-NEXT: Index:
+# CHECK-NEXT: Name: foo
+# CHECK-NEXT: Type: SHT_NOBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_TLS
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 1
+
+# CHECK: Type: PT_TLS
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: VirtualAddress:
+# CHECK-NEXT: PhysicalAddress:
+# CHECK-NEXT: FileSize: 0
+# CHECK-NEXT: MemSize: 9
+
+.section .tbss,"awT",@nobits
+.quad 0
+.section foo,"awT",@nobits
+.byte 0
diff --git a/test/ELF/linkerscript/no-pt-load.s b/test/ELF/linkerscript/no-pt-load.s
new file mode 100644
index 000000000000..c70402501391
--- /dev/null
+++ b/test/ELF/linkerscript/no-pt-load.s
@@ -0,0 +1,5 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "PHDRS {foo PT_DYNAMIC ;} " \
+# RUN: "SECTIONS { .text : { *(.text) } : foo }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t.o
diff --git a/test/ELF/linkerscript/no-space.s b/test/ELF/linkerscript/no-space.s
new file mode 100644
index 000000000000..a96797269e43
--- /dev/null
+++ b/test/ELF/linkerscript/no-space.s
@@ -0,0 +1,26 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+
+# RUN: echo "SECTIONS {foo 0 : {*(foo*)} }" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o -shared
+# RUN: llvm-readobj -elf-output-style=GNU -l %t | FileCheck %s
+
+# RUN: echo "SECTIONS {foo : {*(foo*)} }" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o -shared
+# RUN: llvm-readobj -elf-output-style=GNU -l %t | FileCheck %s
+
+# There is not enough address space available for the header, so just start the PT_LOAD
+# after it.
+
+# CHECK: Program Headers:
+# CHECK-NEXT: Type Offset VirtAddr PhysAddr
+# CHECK-NEXT: PHDR
+# CHECK-NEXT: LOAD 0x001000 0x0000000000000000 0x0000000000000000
+
+# CHECK: Section to Segment mapping:
+# CHECK-NEXT: Segment Sections...
+# CHECK-NEXT: 00
+# CHECK-NEXT: 01 foo .text .dynsym .hash .dynstr
+
+.section foo, "a"
+.quad 0
diff --git a/test/ELF/linkerscript/non-alloc.s b/test/ELF/linkerscript/non-alloc.s
new file mode 100644
index 000000000000..2060129a8020
--- /dev/null
+++ b/test/ELF/linkerscript/non-alloc.s
@@ -0,0 +1,25 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
+
+# RUN: echo "SECTIONS { .foo 0 : {*(foo)} }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t -shared
+# RUN: llvm-readobj -elf-output-style=GNU -s -l %t1 | FileCheck %s
+
+# Test that we create all necessary PT_LOAD. We use to stop at the first
+# non-alloc, causing us to not create PT_LOAD for linker generated sections.
+
+# CHECK: Program Headers:
+# CHECK-NEXT: Type
+# CHECK-NEXT: PHDR
+# CHECK-NEXT: LOAD {{.*}} R E
+# CHECK-NEXT: LOAD {{.*}} RW
+
+# CHECK: Section to Segment mapping:
+# CHECK-NEXT: Segment Sections...
+# CHECK-NEXT: 00
+# CHECK-NEXT: 01 .text .dynsym .hash .dynstr
+# CHECK-NEXT: 02 .dynamic
+
+nop
+.section foo
+.quad 0
diff --git a/test/ELF/linkerscript/numbers.s b/test/ELF/linkerscript/numbers.s
new file mode 100644
index 000000000000..653f94bd4e6e
--- /dev/null
+++ b/test/ELF/linkerscript/numbers.s
@@ -0,0 +1,72 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { \
+# RUN: . = 1000h; \
+# RUN: .hex1 : { *(.hex.1) } \
+# RUN: . = 1010H; \
+# RUN: .hex2 : { *(.hex.2) } \
+# RUN: . = 10k; \
+# RUN: .kilo1 : { *(.kilo.1) } \
+# RUN: . = 11K; \
+# RUN: .kilo2 : { *(.kilo.2) } \
+# RUN: . = 1m; \
+# RUN: .mega1 : { *(.mega.1) } \
+# RUN: . = 2M; \
+# RUN: .mega2 : { *(.mega.2) } \
+# RUN: }" > %t.script
+# RUN: ld.lld %t --script %t.script -o %t2
+# RUN: llvm-objdump -section-headers %t2 | FileCheck %s
+
+# CHECK: Sections:
+# CHECK-NEXT: Idx Name Size Address
+# CHECK-NEXT: 0 00000000 0000000000000000
+# CHECK-NEXT: 1 .hex1 00000008 0000000000001000
+# CHECK-NEXT: 2 .hex2 00000008 0000000000001010
+# CHECK-NEXT: 3 .kilo1 00000008 0000000000002800
+# CHECK-NEXT: 4 .kilo2 00000008 0000000000002c00
+# CHECK-NEXT: 5 .mega1 00000008 0000000000100000
+# CHECK-NEXT: 6 .mega2 00000008 0000000000200000
+
+## Mailformed number errors.
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x11h; \
+# RUN: }" > %t2.script
+# RUN: not ld.lld %t --script %t2.script -o %t3 2>&1 | \
+# RUN: FileCheck --check-prefix=ERR1 %s
+# ERR1: malformed number: 0x11h
+
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x11k; \
+# RUN: }" > %t3.script
+# RUN: not ld.lld %t --script %t3.script -o %t4 2>&1 | \
+# RUN: FileCheck --check-prefix=ERR2 %s
+# ERR2: malformed number: 0x11k
+
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x11m; \
+# RUN: }" > %t4.script
+# RUN: not ld.lld %t --script %t4.script -o %t5 2>&1 | \
+# RUN: FileCheck --check-prefix=ERR3 %s
+# ERR3: malformed number: 0x11m
+
+.globl _start
+_start:
+nop
+
+.section .hex.1, "a"
+.quad 0
+
+.section .kilo.1, "a"
+.quad 0
+
+.section .mega.1, "a"
+.quad 0
+
+.section .hex.2, "a"
+.quad 0
+
+.section .kilo.2, "a"
+.quad 0
+
+.section .mega.2, "a"
+.quad 0
diff --git a/test/ELF/linkerscript/openbsd-bootdata.s b/test/ELF/linkerscript/openbsd-bootdata.s
new file mode 100644
index 000000000000..3e90574bb3a3
--- /dev/null
+++ b/test/ELF/linkerscript/openbsd-bootdata.s
@@ -0,0 +1,7 @@
+# RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %s -o %t.o
+# RUN: echo "PHDRS { boot PT_OPENBSD_BOOTDATA; }" > %t.script
+# RUN: ld.lld --script %t.script %t.o -o %t
+# RUN: llvm-readobj --program-headers -s %t | FileCheck %s
+
+# CHECK: ProgramHeader {
+# CHECK: Type: PT_OPENBSD_BOOTDATA (0x65A41BE6)
diff --git a/test/ELF/linkerscript/openbsd-randomize.s b/test/ELF/linkerscript/openbsd-randomize.s
new file mode 100644
index 000000000000..bf885f423b02
--- /dev/null
+++ b/test/ELF/linkerscript/openbsd-randomize.s
@@ -0,0 +1,23 @@
+# RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %s -o %t.o
+# RUN: echo "PHDRS { text PT_LOAD FILEHDR PHDRS; rand PT_OPENBSD_RANDOMIZE; } \
+# RUN: SECTIONS { . = SIZEOF_HEADERS; \
+# RUN: .text : { *(.text) } \
+# RUN: .openbsd.randomdata : { *(.openbsd.randomdata) } : rand }" > %t.script
+# RUN: ld.lld --script %t.script %t.o -o %t
+# RUN: llvm-readobj --program-headers -s %t | FileCheck %s
+
+# CHECK: ProgramHeader {
+# CHECK: Type: PT_OPENBSD_RANDOMIZE (0x65A3DBE6)
+# CHECK-NEXT: Offset: 0x74
+# CHECK-NEXT: VirtualAddress: 0x74
+# CHECK-NEXT: PhysicalAddress: 0x74
+# CHECK-NEXT: FileSize: 8
+# CHECK-NEXT: MemSize: 8
+# CHECK-NEXT: Flags [ (0x4)
+# CHECK-NEXT: PF_R (0x4)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 1
+# CHECK-NEXT: }
+
+.section .openbsd.randomdata, "a"
+.quad 0
diff --git a/test/ELF/linkerscript/openbsd-wxneeded.s b/test/ELF/linkerscript/openbsd-wxneeded.s
new file mode 100644
index 000000000000..d371da9d8645
--- /dev/null
+++ b/test/ELF/linkerscript/openbsd-wxneeded.s
@@ -0,0 +1,17 @@
+# RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %s -o %t.o
+# RUN: echo "PHDRS { text PT_LOAD FILEHDR PHDRS; wxneeded PT_OPENBSD_WXNEEDED; }" > %t.script
+# RUN: ld.lld -z wxneeded --script %t.script %t.o -o %t
+# RUN: llvm-readobj --program-headers %t | FileCheck %s
+
+# CHECK: ProgramHeader {
+# CHECK: Type: PT_OPENBSD_WXNEEDED (0x65A3DBE7)
+# CHECK-NEXT: Offset: 0x0
+# CHECK-NEXT: VirtualAddress: 0x0
+# CHECK-NEXT: PhysicalAddress: 0x0
+# CHECK-NEXT: FileSize: 0
+# CHECK-NEXT: MemSize: 0
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 0
+# CHECK-NEXT: }
diff --git a/test/ELF/linkerscript/orphan-align.s b/test/ELF/linkerscript/orphan-align.s
new file mode 100644
index 000000000000..edd637b92820
--- /dev/null
+++ b/test/ELF/linkerscript/orphan-align.s
@@ -0,0 +1,28 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
+# RUN: .text : { *(.text) } \
+# RUN: . = ALIGN(0x1000); \
+# RUN: .data.rel.ro : { *(.data.rel.ro) } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t -T %t.script %t.o -shared
+# RUN: llvm-readobj -l %t | FileCheck %s
+
+
+# Test that the orphan section foo is placed before the ALIGN and so the second
+# PT_LOAD is aligned.
+
+
+# CHECK: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x0
+
+# CHECK: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x1000
+
+nop
+.section .data.rel.ro, "aw"
+.byte 0
+
+.section foo, "ax"
+nop
diff --git a/test/ELF/linkerscript/orphan-first-cmd.s b/test/ELF/linkerscript/orphan-first-cmd.s
new file mode 100644
index 000000000000..3fb3b31b9a40
--- /dev/null
+++ b/test/ELF/linkerscript/orphan-first-cmd.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { \
+# RUN: foo = 123; \
+# RUN: . = 0x1000; \
+# RUN: . = 0x2000; \
+# RUN: .bar : { . = . + 1; } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t -T %t.script %t.o -shared
+# RUN: llvm-readobj -s %t | FileCheck %s
+
+# CHECK: Name: .text
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_EXECINSTR
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x1000
diff --git a/test/ELF/linkerscript/orphan.s b/test/ELF/linkerscript/orphan.s
new file mode 100644
index 000000000000..3daa6fbf63c9
--- /dev/null
+++ b/test/ELF/linkerscript/orphan.s
@@ -0,0 +1,36 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { \
+# RUN: .text : { *(.text) } \
+# RUN: .rw1 : { *(.rw1) } \
+# RUN: .rw2 : { *(.rw2) } \
+# RUN: .rw3 : { *(.rw3) } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+
+## .jcr is a relro section and should be placed before other RW sections.
+## .bss is SHT_NOBITS section and should be last RW section, so some space
+## in ELF file could be saved.
+# CHECK: 0 00000000 0000000000000000
+# CHECK-NEXT: 1 .text 00000000 0000000000000000 TEXT DATA
+# CHECK-NEXT: 2 .jcr 00000008 0000000000000000 DATA
+# CHECK-NEXT: 3 .rw1 00000008 0000000000000008 DATA
+# CHECK-NEXT: 4 .rw2 00000008 0000000000000010 DATA
+# CHECK-NEXT: 5 .rw3 00000008 0000000000000018 DATA
+# CHECK-NEXT: 6 .bss 00000008 0000000000000020 BSS
+
+.section .rw1, "aw"
+ .quad 0
+
+.section .rw2, "aw"
+ .quad 0
+
+.section .rw3, "aw"
+ .quad 0
+
+.section .jcr, "aw"
+ .quad 0
+
+.section .bss, "aw",@nobits
+ .quad 0
diff --git a/test/ELF/linkerscript-orphans.s b/test/ELF/linkerscript/orphans.s
index fa7d30bf7f7c..0a8c7ab1129b 100644
--- a/test/ELF/linkerscript-orphans.s
+++ b/test/ELF/linkerscript/orphans.s
@@ -14,8 +14,8 @@
# TEXTORPHAN: Sections:
# TEXTORPHAN-NEXT: Idx Name
# TEXTORPHAN-NEXT: 0
-# TEXTORPHAN-NEXT: 1 .writable
-# TEXTORPHAN-NEXT: 2 .text
+# TEXTORPHAN-NEXT: 1 .text
+# TEXTORPHAN-NEXT: 2 .writable
# WRITABLEORPHAN: Sections:
# WRITABLEORPHAN-NEXT: Idx Name
diff --git a/test/ELF/linkerscript-ouputformat.s b/test/ELF/linkerscript/ouputformat.s
index 3c6cfdc8f966..7d4402a557a0 100644
--- a/test/ELF/linkerscript-ouputformat.s
+++ b/test/ELF/linkerscript/ouputformat.s
@@ -7,4 +7,3 @@
# RUN: echo "OUTPUT_FORMAT(x, y)" > %t.script
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-freebsd %s -o %t1
# RUN: not ld.lld -shared -o %t2 %t1 %t.script
-# RUN: llvm-readobj %t2 > /dev/null
diff --git a/test/ELF/linkerscript-outputarch.s b/test/ELF/linkerscript/outputarch.s
index 99089615cb17..99089615cb17 100644
--- a/test/ELF/linkerscript-outputarch.s
+++ b/test/ELF/linkerscript/outputarch.s
diff --git a/test/ELF/linkerscript/outsections-addr.s b/test/ELF/linkerscript/outsections-addr.s
new file mode 100644
index 000000000000..003b2771c336
--- /dev/null
+++ b/test/ELF/linkerscript/outsections-addr.s
@@ -0,0 +1,122 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { \
+# RUN: .aaa 0x2000 : \
+# RUN: { \
+# RUN: *(.aaa) \
+# RUN: } \
+# RUN: .bbb 0x1 ? 0x3000 : 0x4000 : \
+# RUN: { \
+# RUN: *(.bbb) \
+# RUN: } \
+# RUN: .ccc ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) : \
+# RUN: { \
+# RUN: *(.ccc) \
+# RUN: } \
+# RUN: .ddd 0x5001 : \
+# RUN: { \
+# RUN: *(.ddd) \
+# RUN: } \
+# RUN: }" > %t.script
+# RUN: ld.lld %t --script %t.script -o %tout
+# RUN: llvm-readobj -s %tout | FileCheck %s
+
+## Check:
+## 1) Simple constant as address.
+## 2) That something that contains ":" character, like ternary
+## operator works as expression.
+## 3) That complex expressions work.
+## 4) That section alignment still applied to explicitly specified address.
+
+#CHECK:Sections [
+#CHECK: Section {
+#CHECK: Index: 0
+#CHECK: Name:
+#CHECK: Type: SHT_NULL
+#CHECK: Flags [
+#CHECK: ]
+#CHECK: Address: 0x0
+#CHECK: Offset: 0x0
+#CHECK: Size: 0
+#CHECK: Link: 0
+#CHECK: Info: 0
+#CHECK: AddressAlignment: 0
+#CHECK: EntrySize: 0
+#CHECK: }
+#CHECK: Section {
+#CHECK: Index: 1
+#CHECK: Name: .aaa
+#CHECK: Type: SHT_PROGBITS
+#CHECK: Flags [
+#CHECK: SHF_ALLOC
+#CHECK: ]
+#CHECK: Address: 0x2000
+#CHECK: Offset: 0x1000
+#CHECK: Size: 8
+#CHECK: Link: 0
+#CHECK: Info: 0
+#CHECK: AddressAlignment: 1
+#CHECK: EntrySize: 0
+#CHECK: }
+#CHECK: Section {
+#CHECK: Index: 2
+#CHECK: Name: .bbb
+#CHECK: Type: SHT_PROGBITS
+#CHECK: Flags [
+#CHECK: SHF_ALLOC
+#CHECK: ]
+#CHECK: Address: 0x3000
+#CHECK: Offset: 0x2000
+#CHECK: Size: 8
+#CHECK: Link: 0
+#CHECK: Info: 0
+#CHECK: AddressAlignment: 1
+#CHECK: EntrySize: 0
+#CHECK: }
+#CHECK: Section {
+#CHECK: Index: 3
+#CHECK: Name: .ccc
+#CHECK: Type: SHT_PROGBITS
+#CHECK: Flags [
+#CHECK: SHF_ALLOC
+#CHECK: ]
+#CHECK: Address: 0x4008
+#CHECK: Offset: 0x3008
+#CHECK: Size: 8
+#CHECK: Link: 0
+#CHECK: Info: 0
+#CHECK: AddressAlignment: 1
+#CHECK: EntrySize: 0
+#CHECK: }
+#CHECK: Section {
+#CHECK: Index: 4
+#CHECK: Name: .ddd
+#CHECK: Type: SHT_PROGBITS
+#CHECK: Flags [
+#CHECK: SHF_ALLOC
+#CHECK: ]
+#CHECK: Address: 0x5010
+#CHECK: Offset: 0x4010
+#CHECK: Size: 8
+#CHECK: Link: 0
+#CHECK: Info: 0
+#CHECK: AddressAlignment: 16
+#CHECK: EntrySize: 0
+#CHECK: }
+
+.globl _start
+_start:
+nop
+
+.section .aaa, "a"
+.quad 0
+
+.section .bbb, "a"
+.quad 0
+
+.section .ccc, "a"
+.quad 0
+
+.section .ddd, "a"
+.align 16
+.quad 0
diff --git a/test/ELF/linkerscript/page-size-align.s b/test/ELF/linkerscript/page-size-align.s
new file mode 100644
index 000000000000..771bb13a8e6d
--- /dev/null
+++ b/test/ELF/linkerscript/page-size-align.s
@@ -0,0 +1,22 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+
+# RUN: echo "SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
+# RUN: .text : { *(.text) } \
+# RUN: . = ALIGN(CONSTANT(MAXPAGESIZE)); \
+# RUN: . = . + 0x3000; \
+# RUN: .dynamic : { *(.dynamic) } \
+# RUN: }" > %t.script
+
+# RUN: ld.lld -T %t.script -z max-page-size=0x4000 %t.o -o %t.so -shared
+# RUN: llvm-readobj -s %t.so | FileCheck %s
+
+# CHECK: Name: .dynamic
+# CHECK-NEXT: Type: SHT_DYNAMIC
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x7000
+# CHECK-NEXT: Offset: 0x3000
diff --git a/test/ELF/linkerscript/page-size.s b/test/ELF/linkerscript/page-size.s
new file mode 100644
index 000000000000..3d97816a68ee
--- /dev/null
+++ b/test/ELF/linkerscript/page-size.s
@@ -0,0 +1,68 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# RUN: ld.lld -z max-page-size=0x4000 %t -o %t2
+# RUN: llvm-readobj -program-headers %t2 | FileCheck %s
+
+# CHECK: ProgramHeaders [
+# CHECK: ProgramHeader {
+# CHECK: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x0
+# CHECK-NEXT: VirtualAddress: 0x200000
+# CHECK-NEXT: PhysicalAddress: 0x200000
+# CHECK-NEXT: FileSize: 344
+# CHECK-NEXT: MemSize: 344
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 16384
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x4000
+# CHECK-NEXT: VirtualAddress: 0x204000
+# CHECK-NEXT: PhysicalAddress: 0x204000
+# CHECK-NEXT: FileSize: 1
+# CHECK-NEXT: MemSize: 1
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: PF_X
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 16384
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x8000
+# CHECK-NEXT: VirtualAddress: 0x208000
+# CHECK-NEXT: PhysicalAddress: 0x208000
+# CHECK-NEXT: FileSize: 8
+# CHECK-NEXT: MemSize: 8
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: PF_W
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 16384
+# CHECK-NEXT: }
+
+# RUN: echo "SECTIONS { \
+# RUN: symbol = CONSTANT(MAXPAGESIZE); \
+# RUN: }" > %t.script
+# RUN: ld.lld -z max-page-size=0x4000 -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck -check-prefix CHECK-SCRIPT %s
+
+# CHECK-SCRIPT: 0000000000004000 *ABS* 00000000 symbol
+
+# RUN: not ld.lld -z max-page-size=0x1001 -o %t1 --script %t.script %t 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR1 %s
+# ERR1: max-page-size: value isn't a power of 2
+
+# RUN: not ld.lld -z max-page-size=-0x1000 -o %t1 --script %t.script %t 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR2 %s
+# ERR2: invalid max-page-size: -0x1000
+
+.global _start
+_start:
+ nop
+
+.section .a, "aw"
+.quad 0
diff --git a/test/ELF/linkerscript-phdr-check.s b/test/ELF/linkerscript/phdr-check.s
index c7229ed3312c..c7229ed3312c 100644
--- a/test/ELF/linkerscript-phdr-check.s
+++ b/test/ELF/linkerscript/phdr-check.s
diff --git a/test/ELF/linkerscript/phdrs-flags.s b/test/ELF/linkerscript/phdrs-flags.s
new file mode 100644
index 000000000000..3cb6432db09c
--- /dev/null
+++ b/test/ELF/linkerscript/phdrs-flags.s
@@ -0,0 +1,58 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "PHDRS {all PT_LOAD FILEHDR PHDRS FLAGS (1 | 1 + 0x1);} \
+# RUN: SECTIONS { \
+# RUN: . = 0x10000200; \
+# RUN: .text : {*(.text*)} :all \
+# RUN: .foo : {*(.foo.*)} :all \
+# RUN: .data : {*(.data.*)} :all}" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-readobj -program-headers %t1 | FileCheck %s
+
+# RUN: echo "PHDRS {all PT_LOAD FILEHDR PHDRS FLAGS (0x1);} \
+# RUN: SECTIONS { \
+# RUN: . = 0x10000200; \
+# RUN: .text : {*(.text*)} :all \
+# RUN: .foo : {*(.foo.*)} \
+# RUN: .data : {*(.data.*)} }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-readobj -program-headers %t1 | FileCheck --check-prefix=DEFHDR %s
+
+# CHECK: ProgramHeaders [
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD (0x1)
+# CHECK-NEXT: Offset: 0x0
+# CHECK-NEXT: VirtualAddress: 0x10000000
+# CHECK-NEXT: PhysicalAddress: 0x10000000
+# CHECK-NEXT: FileSize: 521
+# CHECK-NEXT: MemSize: 521
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_W (0x2)
+# CHECK-NEXT: PF_X (0x1)
+# CHECK-NEXT: ]
+
+# DEFHDR: ProgramHeaders [
+# DEFHDR-NEXT: ProgramHeader {
+# DEFHDR-NEXT: Type: PT_LOAD (0x1)
+# DEFHDR-NEXT: Offset: 0x0
+# DEFHDR-NEXT: VirtualAddress: 0x10000000
+# DEFHDR-NEXT: PhysicalAddress: 0x10000000
+# DEFHDR-NEXT: FileSize: 521
+# DEFHDR-NEXT: MemSize: 521
+# DEFHDR-NEXT: Flags [ (0x1)
+# DEFHDR-NEXT: PF_X (0x1)
+# DEFHDR-NEXT: ]
+# DEFHDR-NEXT: Alignment: 4096
+# DEFHDR-NEXT: }
+
+.global _start
+_start:
+ nop
+
+.section .foo.1,"a"
+foo1:
+ .long 0
+
+.section .foo.2,"aw"
+foo2:
+ .long 0
diff --git a/test/ELF/linkerscript/phdrs.s b/test/ELF/linkerscript/phdrs.s
new file mode 100644
index 000000000000..a7476e802341
--- /dev/null
+++ b/test/ELF/linkerscript/phdrs.s
@@ -0,0 +1,143 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "PHDRS {all PT_LOAD FILEHDR PHDRS ;} \
+# RUN: SECTIONS { \
+# RUN: . = 0x10000200; \
+# RUN: .text : {*(.text*)} :all \
+# RUN: .foo : {*(.foo.*)} :all \
+# RUN: .data : {*(.data.*)} :all}" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-readobj -program-headers %t1 | FileCheck %s
+
+## Check that program headers are not written, unless we explicitly tell
+## lld to do this.
+# RUN: echo "PHDRS {all PT_LOAD;} \
+# RUN: SECTIONS { \
+# RUN: . = 0x10000200; \
+# RUN: /DISCARD/ : {*(.text*)} \
+# RUN: .foo : {*(.foo.*)} :all \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-readobj -program-headers %t1 | FileCheck --check-prefix=NOPHDR %s
+
+## Check the AT(expr)
+# RUN: echo "PHDRS {all PT_LOAD FILEHDR PHDRS AT(0x500 + 0x500) ;} \
+# RUN: SECTIONS { \
+# RUN: . = 0x10000200; \
+# RUN: .text : {*(.text*)} :all \
+# RUN: .foo : {*(.foo.*)} :all \
+# RUN: .data : {*(.data.*)} :all}" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-readobj -program-headers %t1 | FileCheck --check-prefix=AT %s
+
+# RUN: echo "PHDRS {all PT_LOAD FILEHDR PHDRS ;} \
+# RUN: SECTIONS { \
+# RUN: . = 0x10000200; \
+# RUN: .text : {*(.text*)} :all \
+# RUN: .foo : {*(.foo.*)} \
+# RUN: .data : {*(.data.*)} }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-readobj -program-headers %t1 | FileCheck --check-prefix=DEFHDR %s
+
+## Check that error is reported when trying to use phdr which is not listed
+## inside PHDRS {} block
+## TODO: If script doesn't contain PHDRS {} block then default phdr is always
+## created and error is not reported.
+# RUN: echo "PHDRS { all PT_LOAD; } \
+# RUN: SECTIONS { .baz : {*(.foo.*)} :bar }" > %t.script
+# RUN: not ld.lld -o %t1 --script %t.script %t 2>&1 | FileCheck --check-prefix=BADHDR %s
+
+# CHECK: ProgramHeaders [
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD (0x1)
+# CHECK-NEXT: Offset: 0x0
+# CHECK-NEXT: VirtualAddress: 0x10000000
+# CHECK-NEXT: PhysicalAddress: 0x10000000
+# CHECK-NEXT: FileSize: 521
+# CHECK-NEXT: MemSize: 521
+# CHECK-NEXT: Flags [ (0x7)
+# CHECK-NEXT: PF_R (0x4)
+# CHECK-NEXT: PF_W (0x2)
+# CHECK-NEXT: PF_X (0x1)
+# CHECK-NEXT: ]
+
+# NOPHDR: ProgramHeaders [
+# NOPHDR-NEXT: ProgramHeader {
+# NOPHDR-NEXT: Type: PT_LOAD (0x1)
+# NOPHDR-NEXT: Offset: 0x200
+# NOPHDR-NEXT: VirtualAddress: 0x10000200
+# NOPHDR-NEXT: PhysicalAddress: 0x10000200
+# NOPHDR-NEXT: FileSize: 8
+# NOPHDR-NEXT: MemSize: 8
+# NOPHDR-NEXT: Flags [ (0x6)
+# NOPHDR-NEXT: PF_R (0x4)
+# NOPHDR-NEXT: PF_W (0x2)
+# NOPHDR-NEXT: ]
+# NOPHDR-NEXT: Alignment: 4096
+# NOPHDR-NEXT: }
+# NOPHDR-NEXT: ]
+
+# AT: ProgramHeaders [
+# AT-NEXT: ProgramHeader {
+# AT-NEXT: Type: PT_LOAD (0x1)
+# AT-NEXT: Offset: 0x0
+# AT-NEXT: VirtualAddress: 0x10000000
+# AT-NEXT: PhysicalAddress: 0xA00
+# AT-NEXT: FileSize: 521
+# AT-NEXT: MemSize: 521
+# AT-NEXT: Flags [ (0x7)
+# AT-NEXT: PF_R (0x4)
+# AT-NEXT: PF_W (0x2)
+# AT-NEXT: PF_X (0x1)
+# AT-NEXT: ]
+
+## Check the numetic values for PHDRS.
+# RUN: echo "PHDRS {text PT_LOAD FILEHDR PHDRS; foo 0x11223344; } \
+# RUN: SECTIONS { . = SIZEOF_HEADERS; .foo : { *(.*) } : text : foo}" > %t1.script
+# RUN: ld.lld -o %t2 --script %t1.script %t
+# RUN: llvm-readobj -program-headers %t2 | FileCheck --check-prefix=INT-PHDRS %s
+
+# INT-PHDRS: ProgramHeaders [
+# INT-PHDRS: ProgramHeader {
+# INT-PHDRS: Type: (0x11223344)
+# INT-PHDRS-NEXT: Offset: 0xB0
+# INT-PHDRS-NEXT: VirtualAddress: 0xB0
+# INT-PHDRS-NEXT: PhysicalAddress: 0xB0
+# INT-PHDRS-NEXT: FileSize:
+# INT-PHDRS-NEXT: MemSize:
+# INT-PHDRS-NEXT: Flags [
+# INT-PHDRS-NEXT: PF_R
+# INT-PHDRS-NEXT: PF_W
+# INT-PHDRS-NEXT: PF_X
+# INT-PHDRS-NEXT: ]
+# INT-PHDRS-NEXT: Alignment:
+# INT-PHDRS-NEXT: }
+# INT-PHDRS-NEXT: ]
+
+# DEFHDR: ProgramHeaders [
+# DEFHDR-NEXT: ProgramHeader {
+# DEFHDR-NEXT: Type: PT_LOAD (0x1)
+# DEFHDR-NEXT: Offset: 0x0
+# DEFHDR-NEXT: VirtualAddress: 0x10000000
+# DEFHDR-NEXT: PhysicalAddress: 0x10000000
+# DEFHDR-NEXT: FileSize: 521
+# DEFHDR-NEXT: MemSize: 521
+# DEFHDR-NEXT: Flags [ (0x7)
+# DEFHDR-NEXT: PF_R (0x4)
+# DEFHDR-NEXT: PF_W (0x2)
+# DEFHDR-NEXT: PF_X (0x1)
+# DEFHDR-NEXT: ]
+
+# BADHDR: {{.*}}.script:1: section header 'bar' is not listed in PHDRS
+
+.global _start
+_start:
+ nop
+
+.section .foo.1,"a"
+foo1:
+ .long 0
+
+.section .foo.2,"aw"
+foo2:
+ .long 0
diff --git a/test/ELF/linkerscript/repsection-symbol.s b/test/ELF/linkerscript/repsection-symbol.s
new file mode 100644
index 000000000000..6786ac47255c
--- /dev/null
+++ b/test/ELF/linkerscript/repsection-symbol.s
@@ -0,0 +1,28 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# RUN: echo "SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
+# RUN: .text : { *(.text) } \
+# RUN: .foo : {foo1 = .; *(.foo.*) foo2 = .; *(.bar) foo3 = .;} \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t -shared
+# RUN: llvm-readobj -t %t1 | FileCheck %s
+
+# CHECK: Name: foo1
+# CHECK-NEXT: Value: 0x288
+
+# CHECK: Name: foo2
+# CHECK-NEXT: Value: 0x290
+
+# CHECK: Name: foo3
+# CHECK-NEXT: Value: 0x294
+
+.section .foo.1,"a"
+ .long 1
+
+.section .foo.2,"aw"
+ .long 2
+
+ .section .bar,"aw"
+ .long 3
diff --git a/test/ELF/linkerscript-repsection-va.s b/test/ELF/linkerscript/repsection-va.s
index 4feeaa0e1c38..8a50fc0998f9 100644
--- a/test/ELF/linkerscript-repsection-va.s
+++ b/test/ELF/linkerscript/repsection-va.s
@@ -6,10 +6,10 @@
# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
# CHECK: Sections:
# CHECK-NEXT: Idx Name Size Address Type
-# CHECK-NEXT: 0 00000000 0000000000000000
-# CHECK-NEXT: 1 .foo 00000004 0000000000000158 DATA
-# CHECK-NEXT: 2 .foo 00000004 000000000000015c DATA
-# CHECK-NEXT: 3 .text 00000001 0000000000000160 TEXT DATA
+# CHECK-NOT: .foo
+# CHECK: .foo 00000008 {{.*}} DATA
+# CHECK-NOT: .foo
+
.global _start
_start:
diff --git a/test/ELF/linkerscript/rosegment.s b/test/ELF/linkerscript/rosegment.s
new file mode 100644
index 000000000000..3201b8bda9fb
--- /dev/null
+++ b/test/ELF/linkerscript/rosegment.s
@@ -0,0 +1,24 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# Test that with linker scripts we don't create a RO PT_LOAD.
+
+# RUN: echo "SECTIONS {}" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t -shared
+# RUN: llvm-readobj -l %t1 | FileCheck %s
+
+# CHECK-NOT: Type: PT_LOAD
+
+# CHECK: Type: PT_LOAD
+# CHECK: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: PF_X
+# CHECK-NEXT: ]
+
+# CHECK: Type: PT_LOAD
+# CHECK: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: PF_W
+# CHECK-NEXT: ]
+
+# CHECK-NOT: Type: PT_LOAD
diff --git a/test/ELF/linkerscript2.s b/test/ELF/linkerscript/searchdir.s
index 6ecd9e7ea975..9fc1a6a49a90 100644
--- a/test/ELF/linkerscript2.s
+++ b/test/ELF/linkerscript/searchdir.s
@@ -1,10 +1,5 @@
-# There is some bad quoting interaction between lit's internal shell, which is
-# implemented in Python, and the Cygwin implementations of the Unix utilities.
-# Avoid running these tests on Windows for now by requiring a real shell.
-
-# REQUIRES: shell
-
# REQUIRES: x86
+
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-freebsd %s -o %t
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-freebsd \
# RUN: %p/Inputs/libsearch-dyn.s -o %tdyn.o
diff --git a/test/ELF/linkerscript/sections-constraint.s b/test/ELF/linkerscript/sections-constraint.s
new file mode 100644
index 000000000000..4d95ec18336c
--- /dev/null
+++ b/test/ELF/linkerscript/sections-constraint.s
@@ -0,0 +1,46 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { \
+# RUN: .writable : ONLY_IF_RW { *(.writable) } \
+# RUN: .readable : ONLY_IF_RO { *(.readable) }}" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -section-headers %t1 | \
+# RUN: FileCheck -check-prefix=BASE %s
+# BASE: Sections:
+# BASE-NEXT: Idx Name Size
+# BASE-NEXT: 0 00000000
+# BASE: .writable 00000004
+# BASE: .readable 00000004
+
+# RUN: echo "SECTIONS { \
+# RUN: .foo : ONLY_IF_RO { *(.foo.*) } \
+# RUN: .writable : ONLY_IF_RW { *(.writable) } \
+# RUN: .readable : ONLY_IF_RO { *(.readable) }}" > %t2.script
+# RUN: ld.lld -o %t2 --script %t2.script %t
+# RUN: llvm-objdump -section-headers %t2 | \
+# RUN: FileCheck -check-prefix=NO1 %s
+# NO1: Sections:
+# NO1-NEXT: Idx Name Size
+# NO1-NEXT: 0 00000000
+# NO1: .writable 00000004
+# NO1: .foo.2 00000004
+# NO1: .readable 00000004
+# NO1: .foo.1 00000004
+
+.global _start
+_start:
+ nop
+
+.section .writable, "aw"
+writable:
+ .long 1
+
+.section .readable, "a"
+readable:
+ .long 2
+
+.section .foo.1, "awx"
+ .long 0
+
+.section .foo.2, "aw"
+ .long 0
diff --git a/test/ELF/linkerscript/sections-constraint2.s b/test/ELF/linkerscript/sections-constraint2.s
new file mode 100644
index 000000000000..e726365582d8
--- /dev/null
+++ b/test/ELF/linkerscript/sections-constraint2.s
@@ -0,0 +1,14 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { zed : ONLY_IF_RO { *(foo) *(bar) } }" > %t.script
+# RUN: ld.lld -T %t.script %t.o -o %t.so -shared
+# RUN: llvm-readobj -s %t.so | FileCheck %s
+
+# CHECK: Sections [
+# CHECK-NOT: zed
+
+.section foo,"aw"
+.quad 1
+
+.section bar, "a"
+.quad 2
diff --git a/test/ELF/linkerscript/sections-constraint3.s b/test/ELF/linkerscript/sections-constraint3.s
new file mode 100644
index 000000000000..259f11e91dd1
--- /dev/null
+++ b/test/ELF/linkerscript/sections-constraint3.s
@@ -0,0 +1,11 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { zed : ONLY_IF_RO { abc = 1; *(foo) } }" > %t.script
+# RUN: ld.lld -T %t.script %t.o -o %t.so -shared
+# RUN: llvm-readobj -t %t.so | FileCheck %s
+
+# CHECK: Symbols [
+# CHECK-NOT: abc
+
+.section foo,"aw"
+.quad 1
diff --git a/test/ELF/linkerscript/sections-constraint4.s b/test/ELF/linkerscript/sections-constraint4.s
new file mode 100644
index 000000000000..6c2d4c8e1ee4
--- /dev/null
+++ b/test/ELF/linkerscript/sections-constraint4.s
@@ -0,0 +1,20 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "SECTIONS { \
+# RUN: .foo : ONLY_IF_RO { *(.foo) } \
+# RUN: .bar : {bar1 = .; *(.bar) } }" > %t1.script
+# RUN: ld.lld -o %t1 --script %t1.script %t
+# RUN: llvm-readobj -t %t1 | FileCheck %s
+
+# CHECK: Name: bar1
+
+.global _start
+_start:
+ nop
+
+.section .bar, "aw"
+bar:
+ .long 1
+
+.section .foo, "aw"
+ .long 0
diff --git a/test/ELF/linkerscript/sections-constraint5.s b/test/ELF/linkerscript/sections-constraint5.s
new file mode 100644
index 000000000000..ea14aad5247e
--- /dev/null
+++ b/test/ELF/linkerscript/sections-constraint5.s
@@ -0,0 +1,32 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS { \
+# RUN: bar : ONLY_IF_RO { sym1 = .; *(foo*) } \
+# RUN: bar : ONLY_IF_RW { sym2 = .; *(foo*) } \
+# RUN: }" > %t.script
+
+# RUN: ld.lld -o %t -T %t.script %t.o
+# RUN: llvm-readobj -s -t %t | FileCheck %s
+
+# CHECK: Sections [
+# CHECK: Name: bar
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 2
+
+# CHECK: Symbols [
+# CHECK-NOT: sym1
+# CHECK: sym2
+# CHECK-NOT: sym1
+
+.section foo1,"a"
+.byte 0
+
+.section foo2,"aw"
+.byte 0
+
diff --git a/test/ELF/linkerscript-sections-keep.s b/test/ELF/linkerscript/sections-keep.s
index fae6383ca499..feb0baca9c3d 100644
--- a/test/ELF/linkerscript-sections-keep.s
+++ b/test/ELF/linkerscript/sections-keep.s
@@ -1,5 +1,6 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/keep.s -o %t2.o
## First check that section "keep" is garbage collected without using KEEP
# RUN: echo "SECTIONS { \
@@ -10,10 +11,10 @@
# RUN: llvm-objdump -section-headers %t1 | \
# RUN: FileCheck -check-prefix=SECGC %s
# SECGC: Sections:
-# SECGC-NEXT: Idx Name Size Address Type
-# SECGC-NEXT: 0 00000000 0000000000000000
-# SECGC-NEXT: 1 .text 00000007 0000000000000158 TEXT DATA
-# SECGC-NEXT: 2 .temp 00000004 000000000000015f DATA
+# SECGC-NEXT: Idx Name Size
+# SECGC-NEXT: 0 00000000
+# SECGC-NEXT: 1 .text 00000007
+# SECGC-NEXT: 2 .temp 00000004
## Now apply KEEP command to preserve the section.
# RUN: echo "SECTIONS { \
@@ -24,48 +25,62 @@
# RUN: llvm-objdump -section-headers %t1 | \
# RUN: FileCheck -check-prefix=SECNOGC %s
# SECNOGC: Sections:
-# SECNOGC-NEXT: Idx Name Size Address Type
-# SECNOGC-NEXT: 0 00000000 0000000000000000
-# SECNOGC-NEXT: 1 .text 00000007 0000000000000158 TEXT DATA
-# SECNOGC-NEXT: 2 .keep 00000004 000000000000015f DATA
-# SECNOGC-NEXT: 3 .temp 00000004 0000000000000163 DATA
+# SECNOGC-NEXT: Idx Name Size
+# SECNOGC-NEXT: 0 00000000
+# SECNOGC-NEXT: 1 .text 00000007
+# SECNOGC-NEXT: 2 .keep 00000004
+# SECNOGC-NEXT: 3 .temp 00000004
## A section name matches two entries in the SECTIONS directive. The
## first one doesn't have KEEP, the second one does. If section that have
## KEEP is the first in order then section is NOT collected.
# RUN: echo "SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
# RUN: .keep : { KEEP(*(.keep)) } \
# RUN: .nokeep : { *(.keep) }}" > %t.script
# RUN: ld.lld --gc-sections -o %t1 --script %t.script %t
# RUN: llvm-objdump -section-headers %t1 | FileCheck -check-prefix=MIXED1 %s
# MIXED1: Sections:
-# MIXED1-NEXT: Idx Name Size Address Type
-# MIXED1-NEXT: 0 00000000 0000000000000000
-# MIXED1-NEXT: 1 .keep 00000004 0000000000000120 DATA
-# MIXED1-NEXT: 2 .temp 00000004 0000000000000124 DATA
-# MIXED1-NEXT: 3 .text 00000007 0000000000000128 TEXT DATA
-# MIXED1-NEXT: 4 .symtab 00000060 0000000000000000
-# MIXED1-NEXT: 5 .shstrtab 0000002d 0000000000000000
-# MIXED1-NEXT: 6 .strtab 00000012 0000000000000000
+# MIXED1-NEXT: Idx Name Size
+# MIXED1-NEXT: 0 00000000
+# MIXED1-NEXT: 1 .keep 00000004
+# MIXED1-NEXT: 2 .text 00000007 00000000000000ec TEXT DATA
+# MIXED1-NEXT: 3 .temp 00000004 00000000000000f3 DATA
+# MIXED1-NEXT: 4 .comment 00000008 0000000000000000
+# MIXED1-NEXT: 5 .symtab 00000060 0000000000000000
+# MIXED1-NEXT: 6 .shstrtab 00000036 0000000000000000
+# MIXED1-NEXT: 7 .strtab 00000012 0000000000000000
## The same, but now section without KEEP is at first place.
## gold and bfd linkers disagree here. gold collects .keep while
## bfd keeps it. Our current behavior is compatible with bfd although
## we can choose either way.
# RUN: echo "SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
# RUN: .nokeep : { *(.keep) } \
# RUN: .keep : { KEEP(*(.keep)) }}" > %t.script
# RUN: ld.lld --gc-sections -o %t1 --script %t.script %t
# RUN: llvm-objdump -section-headers %t1 | FileCheck -check-prefix=MIXED2 %s
# MIXED2: Sections:
-# MIXED2-NEXT: Idx Name Size Address Type
-# MIXED2-NEXT: 0 00000000 0000000000000000
-# MIXED2-NEXT: 1 .nokeep 00000004 0000000000000120 DATA
-# MIXED2-NEXT: 2 .temp 00000004 0000000000000124 DATA
-# MIXED2-NEXT: 3 .text 00000007 0000000000000128 TEXT DATA
-# MIXED2-NEXT: 4 .symtab 00000060 0000000000000000
-# MIXED2-NEXT: 5 .shstrtab 0000002f 0000000000000000
-# MIXED2-NEXT: 6 .strtab 00000012 0000000000000000
+# MIXED2-NEXT: Idx Name Size
+# MIXED2-NEXT: 0 00000000
+# MIXED2-NEXT: 1 .nokeep 00000004 00000000000000e8 DATA
+# MIXED2-NEXT: 2 .text 00000007 00000000000000ec TEXT DATA
+# MIXED2-NEXT: 3 .temp 00000004 00000000000000f3 DATA
+# MIXED2-NEXT: 4 .comment 00000008 0000000000000000
+# MIXED2-NEXT: 5 .symtab 00000060 0000000000000000
+# MIXED2-NEXT: 6 .shstrtab 00000038 0000000000000000
+# MIXED2-NEXT: 7 .strtab 00000012 0000000000000000
+
+# Check file pattern for kept sections.
+# RUN: echo "SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
+# RUN: .keep : { KEEP(*2.o(.keep)) } \
+# RUN: }" > %t.script
+# RUN: ld.lld --gc-sections -o %t1 --script %t.script %t2.o %t
+# RUN: llvm-objdump -s %t1 | FileCheck -check-prefix=FILEMATCH %s
+# FILEMATCH: Contents of section .keep:
+# FILEMATCH-NEXT: 00e8 41414141 AAAA
.global _start
_start:
diff --git a/test/ELF/linkerscript/sections-padding.s b/test/ELF/linkerscript/sections-padding.s
new file mode 100644
index 000000000000..51d3a88fadf6
--- /dev/null
+++ b/test/ELF/linkerscript/sections-padding.s
@@ -0,0 +1,49 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+## Check that padding value works:
+# RUN: echo "SECTIONS { .mysec : { *(.mysec*) } =0x1122 }" > %t.script
+# RUN: ld.lld -o %t.out --script %t.script %t
+# RUN: llvm-objdump -s %t.out | FileCheck -check-prefix=YES %s
+# YES: 66001122 00001122 00001122 00001122
+
+## Confirming that address was correct:
+# RUN: echo "SECTIONS { .mysec : { *(.mysec*) } =0x99887766 }" > %t.script
+# RUN: ld.lld -o %t.out --script %t.script %t
+# RUN: llvm-objdump -s %t.out | FileCheck -check-prefix=YES2 %s
+# YES2: 66887766 99887766 99887766 99887766
+
+## Default padding value is 0x00:
+# RUN: echo "SECTIONS { .mysec : { *(.mysec*) } }" > %t.script
+# RUN: ld.lld -o %t.out --script %t.script %t
+# RUN: llvm-objdump -s %t.out | FileCheck -check-prefix=NO %s
+# NO: 66000000 00000000 00000000 00000000
+
+## Decimal value.
+# RUN: echo "SECTIONS { .mysec : { *(.mysec*) } =777 }" > %t.script
+# RUN: ld.lld -o %t.out --script %t.script %t
+# RUN: llvm-objdump -s %t.out | FileCheck -check-prefix=DEC %s
+# DEC: 66000309 00000309 00000309 00000309
+
+## Invalid hex value:
+# RUN: echo "SECTIONS { .mysec : { *(.mysec*) } =0x99XX }" > %t.script
+# RUN: not ld.lld -o %t.out --script %t.script %t 2>&1 \
+# RUN: | FileCheck --check-prefix=ERR2 %s
+# ERR2: invalid filler expression: 0x99XX
+
+## Check case with space between '=' and expression:
+# RUN: echo "SECTIONS { .mysec : { *(.mysec*) } = 0x1122 }" > %t.script
+# RUN: ld.lld -o %t.out --script %t.script %t
+# RUN: llvm-objdump -s %t.out | FileCheck -check-prefix=YES %s
+
+.section .mysec.1,"a"
+.align 16
+.byte 0x66
+
+.section .mysec.2,"a"
+.align 16
+.byte 0x66
+
+.globl _start
+_start:
+ nop
diff --git a/test/ELF/linkerscript/sections-sort.s b/test/ELF/linkerscript/sections-sort.s
new file mode 100644
index 000000000000..cf1fd3e57827
--- /dev/null
+++ b/test/ELF/linkerscript/sections-sort.s
@@ -0,0 +1,30 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+
+# RUN: echo "SECTIONS { \
+# RUN: .text : { *(.text) } \
+# RUN: foo : { *(foo) } \
+# RUN: } " > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o -shared
+# RUN: llvm-objdump --section-headers %t | FileCheck %s
+
+# Test the section order. This is a case where at least with libstdc++'s
+# stable_sort we used to get a different result.
+
+nop
+
+.section foo, "a"
+.byte 0
+
+# CHECK: Id
+# CHECK-NEXT: 0
+# CHECK-NEXT: 1 .text
+# CHECK-NEXT: 2 foo
+# CHECK-NEXT: 3 .dynsym
+# CHECK-NEXT: 4 .hash
+# CHECK-NEXT: 5 .dynstr
+# CHECK-NEXT: 6 .dynamic
+# CHECK-NEXT: 7 .comment
+# CHECK-NEXT: 8 .symtab
+# CHECK-NEXT: 9 .shstrtab
+# CHECK-NEXT: 10 .strtab
diff --git a/test/ELF/linkerscript-sections.s b/test/ELF/linkerscript/sections.s
index b68dac765a77..416e77346a59 100644
--- a/test/ELF/linkerscript-sections.s
+++ b/test/ELF/linkerscript/sections.s
@@ -21,11 +21,13 @@
# SEC-DEFAULT: 3 other 00000003 {{[0-9a-f]*}} DATA
# SEC-DEFAULT: 4 .bss 00000002 {{[0-9a-f]*}} BSS
# SEC-DEFAULT: 5 .shstrtab 00000002 {{[0-9a-f]*}}
-# SEC-DEFAULT: 6 .symtab 00000030 {{[0-9a-f]*}}
-# SEC-DEFAULT: 7 .shstrtab 00000032 {{[0-9a-f]*}}
-# SEC-DEFAULT: 8 .strtab 00000008 {{[0-9a-f]*}}
+# SEC-DEFAULT: 6 .comment 00000008 {{[0-9a-f]*}}
+# SEC-DEFAULT: 7 .symtab 00000030 {{[0-9a-f]*}}
+# SEC-DEFAULT: 8 .shstrtab 0000003b {{[0-9a-f]*}}
+# SEC-DEFAULT: 9 .strtab 00000008 {{[0-9a-f]*}}
-# Sections are put in order specified in linker script.
+# Sections are put in order specified in linker script, other than alloc
+# sections going first.
# RUN: echo "SECTIONS { \
# RUN: .bss : { *(.bss) } \
# RUN: other : { *(other) } \
@@ -42,7 +44,7 @@
# SEC-ORDER: 1 .bss 00000002 {{[0-9a-f]*}} BSS
# SEC-ORDER: 2 other 00000003 {{[0-9a-f]*}} DATA
# SEC-ORDER: 3 .shstrtab 00000002 {{[0-9a-f]*}}
-# SEC-ORDER: 4 .shstrtab 00000032 {{[0-9a-f]*}}
+# SEC-ORDER: 4 .shstrtab 0000003b {{[0-9a-f]*}}
# SEC-ORDER: 5 .symtab 00000030 {{[0-9a-f]*}}
# SEC-ORDER: 6 .strtab 00000008 {{[0-9a-f]*}}
# SEC-ORDER: 7 .data 00000020 {{[0-9a-f]*}} DATA
@@ -62,9 +64,10 @@
# SEC-SWAP-NAMES: 3 other 00000003 {{[0-9a-f]*}} DATA
# SEC-SWAP-NAMES: 4 .bss 00000002 {{[0-9a-f]*}} BSS
# SEC-SWAP-NAMES: 5 .shstrtab 00000002 {{[0-9a-f]*}}
-# SEC-SWAP-NAMES: 6 .symtab 00000030 {{[0-9a-f]*}}
-# SEC-SWAP-NAMES: 7 .shstrtab 00000032 {{[0-9a-f]*}}
-# SEC-SWAP-NAMES: 8 .strtab 00000008 {{[0-9a-f]*}}
+# SEC-SWAP-NAMES: 6 .comment 00000008 {{[0-9a-f]*}}
+# SEC-SWAP-NAMES: 7 .symtab 00000030 {{[0-9a-f]*}}
+# SEC-SWAP-NAMES: 8 .shstrtab 0000003b {{[0-9a-f]*}}
+# SEC-SWAP-NAMES: 9 .strtab 00000008 {{[0-9a-f]*}}
# .shstrtab from the input object file is discarded.
# RUN: echo "SECTIONS { \
@@ -78,9 +81,10 @@
# SEC-DISCARD: 2 .data 00000020 {{[0-9a-f]*}} DATA
# SEC-DISCARD: 3 other 00000003 {{[0-9a-f]*}} DATA
# SEC-DISCARD: 4 .bss 00000002 {{[0-9a-f]*}} BSS
-# SEC-DISCARD: 5 .symtab 00000030 {{[0-9a-f]*}}
-# SEC-DISCARD: 6 .shstrtab 00000032 {{[0-9a-f]*}}
-# SEC-DISCARD: 7 .strtab 00000008 {{[0-9a-f]*}}
+# SEC-DISCARD: 5 .comment 00000008 {{[0-9a-f]*}}
+# SEC-DISCARD: 6 .symtab 00000030 {{[0-9a-f]*}}
+# SEC-DISCARD: 7 .shstrtab 0000003b {{[0-9a-f]*}}
+# SEC-DISCARD: 8 .strtab 00000008 {{[0-9a-f]*}}
# Multiple SECTIONS command specifying additional input section descriptions
# for the same output section description - input sections are merged into
@@ -99,9 +103,10 @@
# SEC-MULTI: 2 .data 00000023 {{[0-9a-f]*}} DATA
# SEC-MULTI: 3 .bss 00000002 {{[0-9a-f]*}} BSS
# SEC-MULTI: 4 .shstrtab 00000002 {{[0-9a-f]*}}
-# SEC-MULTI: 5 .symtab 00000030 {{[0-9a-f]*}}
-# SEC-MULTI: 6 .shstrtab 0000002c {{[0-9a-f]*}}
-# SEC-MULTI: 7 .strtab 00000008 {{[0-9a-f]*}}
+# SEC-MULTI: 5 .comment 00000008 {{[0-9a-f]*}}
+# SEC-MULTI: 6 .symtab 00000030 {{[0-9a-f]*}}
+# SEC-MULTI: 7 .shstrtab 00000035 {{[0-9a-f]*}}
+# SEC-MULTI: 8 .strtab 00000008 {{[0-9a-f]*}}
.globl _start
_start:
diff --git a/test/ELF/linkerscript/segment-start.s b/test/ELF/linkerscript/segment-start.s
new file mode 100644
index 000000000000..e46c398f63f9
--- /dev/null
+++ b/test/ELF/linkerscript/segment-start.s
@@ -0,0 +1,27 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: ld.lld %t.o %S/Inputs/segment-start.script -shared -o %t.so
+// RUN: llvm-readobj --dyn-symbols %t.so | FileCheck %s
+
+// CHECK: Name: foobar1
+// CHECK-NEXT: Value: 0x8001
+
+// CHECK: Name: foobar2
+// CHECK-NEXT: Value: 0x8002
+
+// CHECK: Name: foobar3
+// CHECK-NEXT: Value: 0x8003
+
+// CHECK: Name: foobar4
+// CHECK-NEXT: Value: 0x8004
+
+.data
+.quad foobar1
+.quad foobar2
+.quad foobar3
+.quad foobar4
+
+// RUN: echo "SECTIONS { . = SEGMENT_START(\"foobar\", foo); }" > %t.script
+// RUN: not ld.lld %t.o %t.script -shared -o %t2.so 2>&1 \
+// RUN: | FileCheck --check-prefix=ERR %s
+// ERR: {{.*}}.script:1: symbol not found: foo
diff --git a/test/ELF/linkerscript/sizeof.s b/test/ELF/linkerscript/sizeof.s
new file mode 100644
index 000000000000..4618f79d3db6
--- /dev/null
+++ b/test/ELF/linkerscript/sizeof.s
@@ -0,0 +1,53 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# RUN: echo "SECTIONS { \
+# RUN: .aaa : { *(.aaa) } \
+# RUN: .bbb : { *(.bbb) } \
+# RUN: .ccc : { *(.ccc) } \
+# RUN: _aaa = SIZEOF(.aaa); \
+# RUN: _bbb = SIZEOF(.bbb); \
+# RUN: _ccc = SIZEOF(.ccc); \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t -section-headers %t1 | FileCheck %s
+# CHECK: Sections:
+# CHECK-NEXT: Idx Name Size
+# CHECK-NEXT: 0 00000000
+# CHECK-NEXT: 1 .aaa 00000008
+# CHECK-NEXT: 2 .bbb 00000010
+# CHECK-NEXT: 3 .ccc 00000018
+# CHECK: SYMBOL TABLE:
+# CHECK-NEXT: 0000000000000000 *UND* 00000000
+# CHECK-NEXT: .text 00000000 _start
+# CHECK-NEXT: 0000000000000008 *ABS* 00000000 _aaa
+# CHECK-NEXT: 0000000000000010 *ABS* 00000000 _bbb
+# CHECK-NEXT: 0000000000000018 *ABS* 00000000 _ccc
+
+## SIZEOF(.nonexistent_section) should return 0.
+# RUN: echo "SECTIONS { \
+# RUN: .aaa : { *(.aaa) } \
+# RUN: .bbb : { *(.bbb) } \
+# RUN: .ccc : { *(.ccc) } \
+# RUN: _aaa = SIZEOF(.foo); \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t -section-headers %t1 | FileCheck -check-prefix=CHECK2 %s
+
+# CHECK2: 0000000000000000 *ABS* 00000000 _aaa
+
+.global _start
+_start:
+ nop
+
+.section .aaa,"a"
+ .quad 0
+
+.section .bbb,"a"
+ .quad 0
+ .quad 0
+
+.section .ccc,"a"
+ .quad 0
+ .quad 0
+ .quad 0
diff --git a/test/ELF/linkerscript/sizeofheaders.s b/test/ELF/linkerscript/sizeofheaders.s
new file mode 100644
index 000000000000..3cc70747280c
--- /dev/null
+++ b/test/ELF/linkerscript/sizeofheaders.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo " SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
+# RUN: _size = SIZEOF_HEADERS; \
+# RUN: .text : {*(.text*)} \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck %s
+
+#CHECK: SYMBOL TABLE:
+#CHECK-NEXT: 0000000000000000 *UND* 00000000
+#CHECK-NEXT: 00000000000000e8 .text 00000000 _start
+#CHECK-NEXT: 00000000000000e8 *ABS* 00000000 _size
+
+.global _start
+_start:
+ nop
diff --git a/test/ELF/linkerscript/sort-constructors.s b/test/ELF/linkerscript/sort-constructors.s
new file mode 100644
index 000000000000..a0c23af6de79
--- /dev/null
+++ b/test/ELF/linkerscript/sort-constructors.s
@@ -0,0 +1,5 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
+# RUN: echo "SECTIONS { .aaa : { SORT(CONSTRUCTORS) } }" > %t1.script
+# RUN: ld.lld -shared -o %t1 --script %t1.script %t1.o
+# RUN: llvm-readobj %t1 > /dev/null
diff --git a/test/ELF/linkerscript/sort-init.s b/test/ELF/linkerscript/sort-init.s
new file mode 100644
index 000000000000..894b8ae882b7
--- /dev/null
+++ b/test/ELF/linkerscript/sort-init.s
@@ -0,0 +1,24 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
+# RUN: echo "SECTIONS { .init_array : { *(SORT_BY_INIT_PRIORITY(.init_array.*)) } }" > %t1.script
+# RUN: ld.lld --script %t1.script %t1.o -o %t2
+# RUN: llvm-objdump -s %t2 | FileCheck %s
+
+# CHECK: Contents of section .init_array:
+# CHECK-NEXT: 03020000 00000000 010405
+
+.globl _start
+_start:
+ nop
+
+.section .init_array, "aw", @init_array
+ .align 8
+ .byte 1
+.section .init_array.100, "aw", @init_array
+ .long 2
+.section .init_array.5, "aw", @init_array
+ .byte 3
+.section .init_array, "aw", @init_array
+ .byte 4
+.section .init_array, "aw", @init_array
+ .byte 5
diff --git a/test/ELF/linkerscript/sort-nested.s b/test/ELF/linkerscript/sort-nested.s
new file mode 100644
index 000000000000..fb1d1a3dd070
--- /dev/null
+++ b/test/ELF/linkerscript/sort-nested.s
@@ -0,0 +1,50 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \
+# RUN: %p/Inputs/sort-nested.s -o %t2.o
+
+## Check sorting first by alignment and then by name.
+# RUN: echo "SECTIONS { .aaa : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.aaa.*))) } }" > %t1.script
+# RUN: ld.lld -o %t1 --script %t1.script %t1.o %t2.o
+# RUN: llvm-objdump -s %t1 | FileCheck -check-prefix=SORTED_AN %s
+# SORTED_AN: Contents of section .aaa:
+# SORTED_AN-NEXT: 01000000 00000000 00000000 00000000
+# SORTED_AN-NEXT: 11000000 00000000 00000000 00000000
+# SORTED_AN-NEXT: 55000000 00000000 22000000 00000000
+# SORTED_AN-NEXT: 02000000 00000000
+
+## Check sorting first by name and then by alignment.
+# RUN: echo "SECTIONS { .aaa : { *(SORT_BY_NAME(SORT_BY_ALIGNMENT(.aaa.*))) } }" > %t2.script
+# RUN: ld.lld -o %t2 --script %t2.script %t1.o %t2.o
+# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=SORTED_NA %s
+# SORTED_NA: Contents of section .aaa:
+# SORTED_NA: 01000000 00000000 00000000 00000000
+# SORTED_NA: 11000000 00000000 22000000 00000000
+# SORTED_NA: 02000000 00000000 00000000 00000000
+# SORTED_NA: 55000000 00000000
+
+## If the section sorting command in linker script isn't nested, the
+## command line option will make the section sorting command to be treated
+## as nested sorting command.
+# RUN: echo "SECTIONS { .aaa : { *(SORT_BY_ALIGNMENT(.aaa.*)) } }" > %t3.script
+# RUN: ld.lld --sort-section name -o %t3 --script %t3.script %t1.o %t2.o
+# RUN: llvm-objdump -s %t3 | FileCheck -check-prefix=SORTED_AN %s
+# RUN: echo "SECTIONS { .aaa : { *(SORT_BY_NAME(.aaa.*)) } }" > %t4.script
+# RUN: ld.lld --sort-section alignment -o %t4 --script %t4.script %t1.o %t2.o
+# RUN: llvm-objdump -s %t4 | FileCheck -check-prefix=SORTED_NA %s
+
+.global _start
+_start:
+ nop
+
+.section .aaa.1, "a"
+.align 32
+.quad 1
+
+.section .aaa.2, "a"
+.align 2
+.quad 2
+
+.section .aaa.5, "a"
+.align 16
+.quad 0x55
diff --git a/test/ELF/linkerscript/sort-non-script.s b/test/ELF/linkerscript/sort-non-script.s
new file mode 100644
index 000000000000..75a2d450adcc
--- /dev/null
+++ b/test/ELF/linkerscript/sort-non-script.s
@@ -0,0 +1,16 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
+
+# RUN: echo "SECTIONS { foo : {*(foo)} }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t -shared
+# RUN: llvm-readobj -elf-output-style=GNU -s %t1 | FileCheck %s
+
+# CHECK: .text {{.*}} AX
+# CHECK-NEXT: .dynsym {{.*}} A
+# CHECK-NEXT: .hash {{.*}} A
+# CHECK-NEXT: .dynstr {{.*}} A
+# CHECK-NEXT: .dynamic {{.*}} WA
+# CHECK-NEXT: foo {{.*}} WA
+
+.section foo, "aw"
+.byte 0
diff --git a/test/ELF/linkerscript/sort.s b/test/ELF/linkerscript/sort.s
new file mode 100644
index 000000000000..6390ced12289
--- /dev/null
+++ b/test/ELF/linkerscript/sort.s
@@ -0,0 +1,120 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \
+# RUN: %p/Inputs/sort.s -o %t2.o
+
+# RUN: echo "SECTIONS { .aaa : { *(.aaa.*) } }" > %t1.script
+# RUN: ld.lld -o %t1 --script %t1.script %t2.o %t1.o
+# RUN: llvm-objdump -s %t1 | FileCheck -check-prefix=UNSORTED %s
+# UNSORTED: Contents of section .aaa:
+# UNSORTED-NEXT: 55000000 00000000 00000000 00000000
+# UNSORTED-NEXT: 00000000 00000000 00000000 00000000
+# UNSORTED-NEXT: 11000000 00000000 33000000 00000000
+# UNSORTED-NEXT: 22000000 00000000 44000000 00000000
+# UNSORTED-NEXT: 05000000 00000000 01000000 00000000
+# UNSORTED-NEXT: 03000000 00000000 02000000 00000000
+# UNSORTED-NEXT: 04000000 00000000
+
+## Check that SORT works (sorted by name of section).
+# RUN: echo "SECTIONS { .aaa : { *(SORT(.aaa.*)) } }" > %t2.script
+# RUN: ld.lld -o %t2 --script %t2.script %t2.o %t1.o
+# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=SORTED_A %s
+# SORTED_A: Contents of section .aaa:
+# SORTED_A-NEXT: 11000000 00000000 01000000 00000000
+# SORTED_A-NEXT: 22000000 00000000 02000000 00000000
+# SORTED_A-NEXT: 33000000 00000000 03000000 00000000
+# SORTED_A-NEXT: 44000000 00000000 00000000 00000000
+# SORTED_A-NEXT: 04000000 00000000 55000000 00000000
+# SORTED_A-NEXT: 00000000 00000000 00000000 00000000
+# SORTED_A-NEXT: 05000000 00000000
+
+## When we switch the order of files, check that sorting by
+## section names is stable.
+# RUN: echo "SECTIONS { .aaa : { *(SORT(.aaa.*)) } }" > %t3.script
+# RUN: ld.lld -o %t3 --script %t3.script %t1.o %t2.o
+# RUN: llvm-objdump -s %t3 | FileCheck -check-prefix=SORTED_B %s
+# SORTED_B: Contents of section .aaa:
+# SORTED_B-NEXT: 01000000 00000000 00000000 00000000
+# SORTED_B-NEXT: 00000000 00000000 00000000 00000000
+# SORTED_B-NEXT: 11000000 00000000 02000000 00000000
+# SORTED_B-NEXT: 22000000 00000000 03000000 00000000
+# SORTED_B-NEXT: 33000000 00000000 00000000 00000000
+# SORTED_B-NEXT: 04000000 00000000 44000000 00000000
+# SORTED_B-NEXT: 05000000 00000000 55000000 00000000
+
+## Check that SORT surrounded with KEEP also works.
+# RUN: echo "SECTIONS { .aaa : { KEEP (*(SORT(.aaa.*))) } }" > %t3.script
+# RUN: ld.lld -o %t3 --script %t3.script %t2.o %t1.o
+# RUN: llvm-objdump -s %t3 | FileCheck -check-prefix=SORTED_A %s
+
+## Check that SORT_BY_NAME works (SORT is alias).
+# RUN: echo "SECTIONS { .aaa : { *(SORT_BY_NAME(.aaa.*)) } }" > %t4.script
+# RUN: ld.lld -o %t4 --script %t4.script %t2.o %t1.o
+# RUN: llvm-objdump -s %t4 | FileCheck -check-prefix=SORTED_A %s
+
+## Check that sections ordered by alignment.
+# RUN: echo "SECTIONS { .aaa : { *(SORT_BY_ALIGNMENT(.aaa.*)) } }" > %t5.script
+# RUN: ld.lld -o %t5 --script %t5.script %t1.o %t2.o
+# RUN: llvm-objdump -s %t5 | FileCheck -check-prefix=SORTED_ALIGNMENT %s
+# SORTED_ALIGNMENT: Contents of section .aaa:
+# SORTED_ALIGNMENT-NEXT: 05000000 00000000 00000000 00000000
+# SORTED_ALIGNMENT-NEXT: 00000000 00000000 00000000 00000000
+# SORTED_ALIGNMENT-NEXT: 11000000 00000000 00000000 00000000
+# SORTED_ALIGNMENT-NEXT: 04000000 00000000 00000000 00000000
+# SORTED_ALIGNMENT-NEXT: 22000000 00000000 03000000 00000000
+# SORTED_ALIGNMENT-NEXT: 33000000 00000000 02000000 00000000
+# SORTED_ALIGNMENT-NEXT: 44000000 00000000 01000000 00000000
+# SORTED_ALIGNMENT-NEXT: 55000000 00000000
+
+## SORT_NONE itself does not sort anything.
+# RUN: echo "SECTIONS { .aaa : { *(SORT_NONE(.aaa.*)) } }" > %t6.script
+# RUN: ld.lld -o %t7 --script %t6.script %t2.o %t1.o
+# RUN: llvm-objdump -s %t7 | FileCheck -check-prefix=UNSORTED %s
+
+## Check --sort-section alignment option.
+# RUN: echo "SECTIONS { .aaa : { *(.aaa.*) } }" > %t7.script
+# RUN: ld.lld --sort-section alignment -o %t8 --script %t7.script %t1.o %t2.o
+# RUN: llvm-objdump -s %t8 | FileCheck -check-prefix=SORTED_ALIGNMENT %s
+
+## Check --sort-section= form.
+# RUN: ld.lld --sort-section=alignment -o %t8_1 --script %t7.script %t1.o %t2.o
+# RUN: llvm-objdump -s %t8_1 | FileCheck -check-prefix=SORTED_ALIGNMENT %s
+
+## Check --sort-section name option.
+# RUN: echo "SECTIONS { .aaa : { *(.aaa.*) } }" > %t8.script
+# RUN: ld.lld --sort-section name -o %t9 --script %t8.script %t1.o %t2.o
+# RUN: llvm-objdump -s %t9 | FileCheck -check-prefix=SORTED_B %s
+
+## SORT_NONE disables the --sort-section.
+# RUN: echo "SECTIONS { .aaa : { *(SORT_NONE(.aaa.*)) } }" > %t9.script
+# RUN: ld.lld --sort-section name -o %t10 --script %t9.script %t2.o %t1.o
+# RUN: llvm-objdump -s %t10 | FileCheck -check-prefix=UNSORTED %s
+
+## SORT_NONE as a inner sort directive.
+# RUN: echo "SECTIONS { .aaa : { *(SORT_BY_NAME(SORT_NONE(.aaa.*))) } }" > %t10.script
+# RUN: ld.lld -o %t11 --script %t10.script %t2.o %t1.o
+# RUN: llvm-objdump -s %t11 | FileCheck -check-prefix=SORTED_A %s
+
+.global _start
+_start:
+ nop
+
+.section .aaa.5, "a"
+.align 32
+.quad 5
+
+.section .aaa.1, "a"
+.align 2
+.quad 1
+
+.section .aaa.3, "a"
+.align 8
+.quad 3
+
+.section .aaa.2, "a"
+.align 4
+.quad 2
+
+.section .aaa.4, "a"
+.align 16
+.quad 4
diff --git a/test/ELF/linkerscript/sort2.s b/test/ELF/linkerscript/sort2.s
new file mode 100644
index 000000000000..1d42093d5aa0
--- /dev/null
+++ b/test/ELF/linkerscript/sort2.s
@@ -0,0 +1,39 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %tfile1.o
+
+# RUN: echo "SECTIONS { .abc : { *(SORT(.foo.*) .bar.*) } }" > %t1.script
+# RUN: ld.lld -o %t1 --script %t1.script %tfile1.o
+# RUN: llvm-objdump -s %t1 | FileCheck %s
+
+# CHECK: Contents of section .abc:
+# CHECK: 01000000 00000000 02000000 00000000
+# CHECK: 03000000 00000000 04000000 00000000
+# CHECK: 06000000 00000000 05000000 00000000
+
+# RUN: echo "SECTIONS { \
+# RUN: .abc : { *(SORT(.foo.* EXCLUDE_FILE (*file1.o) .bar.*) .bar.*) } \
+# RUN: }" > %t2.script
+# RUN: ld.lld -o %t2 --script %t2.script %tfile1.o
+# RUN: llvm-objdump -s %t2 | FileCheck %s
+
+.text
+.globl _start
+_start:
+
+.section .foo.2,"a"
+ .quad 2
+
+.section .foo.3,"a"
+ .quad 3
+
+.section .foo.1,"a"
+ .quad 1
+
+.section .bar.4,"a"
+ .quad 4
+
+.section .bar.6,"a"
+ .quad 6
+
+.section .bar.5,"a"
+ .quad 5
diff --git a/test/ELF/linkerscript/start-end.s b/test/ELF/linkerscript/start-end.s
new file mode 100644
index 000000000000..b68606abc181
--- /dev/null
+++ b/test/ELF/linkerscript/start-end.s
@@ -0,0 +1,16 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { \
+# RUN: .init_array : { \
+# RUN: __init_array_start = .; \
+# RUN: *(.init_array) \
+# RUN: __init_array_end = .; } }" > %t.script
+# RUN: ld.lld %t.o -script %t.script -o %t 2>&1
+
+.globl _start
+.text
+_start:
+ nop
+
+.section .init_array, "aw"
+ .quad 0
diff --git a/test/ELF/linkerscript/subalign.s b/test/ELF/linkerscript/subalign.s
new file mode 100644
index 000000000000..8b441d440b0e
--- /dev/null
+++ b/test/ELF/linkerscript/subalign.s
@@ -0,0 +1,43 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
+
+# RUN: echo "SECTIONS { .aaa : { *(.aaa.*) } }" > %t1.script
+# RUN: ld.lld -o %t1 --script %t1.script %t1.o
+# RUN: llvm-objdump -s %t1 | FileCheck -check-prefix=NOALIGN %s
+# NOALIGN: Contents of section .aaa:
+# NOALIGN-NEXT: 01000000 00000000 00000000 00000000
+# NOALIGN-NEXT: 00000000 00000000 00000000 00000000
+# NOALIGN-NEXT: 02000000 00000000 00000000 00000000
+# NOALIGN-NEXT: 00000000 00000000 00000000 00000000
+# NOALIGN-NEXT: 03000000 00000000 00000000 00000000
+# NOALIGN-NEXT: 00000000 00000000 00000000 00000000
+# NOALIGN-NEXT: 00000000 00000000 00000000 00000000
+# NOALIGN-NEXT: 00000000 00000000 00000000 00000000
+# NOALIGN-NEXT: 04000000 00000000
+
+# RUN: echo "SECTIONS { .aaa : SUBALIGN(1) { *(.aaa.*) } }" > %t2.script
+# RUN: ld.lld -o %t2 --script %t2.script %t1.o
+# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=SUBALIGN %s
+# SUBALIGN: Contents of section .aaa:
+# SUBALIGN: 01000000 00000000 02000000 00000000
+# SUBALIGN: 03000000 00000000 04000000 00000000
+
+.global _start
+_start:
+ nop
+
+.section .aaa.1, "a"
+.align 16
+.quad 1
+
+.section .aaa.2, "a"
+.align 32
+.quad 2
+
+.section .aaa.3, "a"
+.align 64
+.quad 3
+
+.section .aaa.4, "a"
+.align 128
+.quad 4
diff --git a/test/ELF/linkerscript/symbol-assignexpr.s b/test/ELF/linkerscript/symbol-assignexpr.s
new file mode 100644
index 000000000000..b988abcdfe98
--- /dev/null
+++ b/test/ELF/linkerscript/symbol-assignexpr.s
@@ -0,0 +1,48 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# RUN: echo "SECTIONS { \
+# RUN: symbol = CONSTANT(MAXPAGESIZE); \
+# RUN: symbol2 = symbol + 0x1234; \
+# RUN: symbol3 = symbol2; \
+# RUN: symbol4 = symbol + -4; \
+# RUN: symbol5 = symbol - ~ 0xfffb; \
+# RUN: symbol6 = symbol - ~(0xfff0 + 0xb); \
+# RUN: symbol7 = symbol - ~ 0xfffb + 4; \
+# RUN: symbol8 = ~ 0xffff + 4; \
+# RUN: symbol9 = - 4; \
+# RUN: symbol10 = 0xfedcba9876543210; \
+# RUN: symbol11 = ((0x28000 + 0x1fff) & ~(0x1000 + -1)); \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck %s
+
+# CHECK: SYMBOL TABLE:
+# CHECK-NEXT: 0000000000000000 *UND* 00000000
+# CHECK-NEXT: .text 00000000 _start
+# CHECK-NEXT: .text 00000000 foo
+# CHECK-NEXT: 0000000000001000 *ABS* 00000000 symbol
+# CHECK-NEXT: 0000000000002234 *ABS* 00000000 symbol2
+# CHECK-NEXT: 0000000000002234 *ABS* 00000000 symbol3
+# CHECK-NEXT: 0000000000000ffc *ABS* 00000000 symbol4
+# CHECK-NEXT: 0000000000010ffc *ABS* 00000000 symbol5
+# CHECK-NEXT: 0000000000010ffc *ABS* 00000000 symbol6
+# CHECK-NEXT: 0000000000011000 *ABS* 00000000 symbol7
+# CHECK-NEXT: ffffffffffff0004 *ABS* 00000000 symbol8
+# CHECK-NEXT: fffffffffffffffc *ABS* 00000000 symbol9
+# CHECK-NEXT: fedcba9876543210 *ABS* 00000000 symbol10
+# CHECK-NEXT: 0000000000029000 *ABS* 00000000 symbol11
+
+# RUN: echo "SECTIONS { \
+# RUN: symbol2 = symbol; \
+# RUN: }" > %t2.script
+# RUN: not ld.lld -o %t2 --script %t2.script %t 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR %s
+# ERR: {{.*}}.script:1: symbol not found: symbol
+
+.global _start
+_start:
+ nop
+
+.global foo
+foo:
diff --git a/test/ELF/linkerscript-symbols.s b/test/ELF/linkerscript/symbol-conflict.s
index 1fcd4e8a8481..dcca7c13de6e 100644
--- a/test/ELF/linkerscript-symbols.s
+++ b/test/ELF/linkerscript/symbol-conflict.s
@@ -1,10 +1,10 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: echo "SECTIONS {.text : {*(.text.*)} text_end = .;}" > %t.script
+# RUN: echo "SECTIONS { . = SIZEOF_HEADERS; .text : {*(.text.*)} end = .;}" > %t.script
# RUN: ld.lld -o %t1 --script %t.script %t
# RUN: llvm-objdump -t %t1 | FileCheck %s
-# CHECK: 0000000000000121 *ABS* 00000000 text_end
+# CHECK: 00000000000000e9 .text 00000000 end
.global _start
_start:
diff --git a/test/ELF/linkerscript/symbol-only.s b/test/ELF/linkerscript/symbol-only.s
new file mode 100644
index 000000000000..76d54f01cdc7
--- /dev/null
+++ b/test/ELF/linkerscript/symbol-only.s
@@ -0,0 +1,21 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# RUN: echo "SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
+# RUN: abc : { foo = .; } \
+# RUN: . = ALIGN(0x1000); \
+# RUN: bar : { *(bar) } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t -shared
+# RUN: llvm-objdump -section-headers -t %t1 | FileCheck %s
+# CHECK: Sections:
+# CHECK-NEXT: Idx Name Size Address
+# CHECK-NEXT: 0 00000000 0000000000000000
+# CHECK: abc 00000000 [[ADDR:[0-9a-f]*]] BSS
+# CHECK-NEXT: bar 00000000 0000000000001000 DATA
+
+# CHECK: SYMBOL TABLE:
+# CHECK: [[ADDR]] abc 00000000 foo
+
+.section bar, "a"
diff --git a/test/ELF/linkerscript/symbolreferenced.s b/test/ELF/linkerscript/symbolreferenced.s
new file mode 100644
index 000000000000..c2925b179ad5
--- /dev/null
+++ b/test/ELF/linkerscript/symbolreferenced.s
@@ -0,0 +1,22 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# Provide new symbol. The value should be 1, like set in PROVIDE()
+# RUN: echo "SECTIONS { PROVIDE(newsym = 1);}" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=PROVIDE1 %s
+# PROVIDE1: 0000000000000001 *ABS* 00000000 newsym
+
+# Provide new symbol (hidden). The value should be 1
+# RUN: echo "SECTIONS { PROVIDE_HIDDEN(newsym = 1);}" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=HIDDEN1 %s
+# HIDDEN1: 0000000000000001 *ABS* 00000000 .hidden newsym
+
+.global _start
+_start:
+ nop
+
+.globl patatino
+patatino:
+ movl newsym, %eax
diff --git a/test/ELF/linkerscript/symbols-synthetic.s b/test/ELF/linkerscript/symbols-synthetic.s
new file mode 100644
index 000000000000..95cdae9a929e
--- /dev/null
+++ b/test/ELF/linkerscript/symbols-synthetic.s
@@ -0,0 +1,98 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# Simple symbol assignment within input section list. The '.' symbol
+# is not location counter but offset from the beginning of output
+# section .foo
+# RUN: echo "SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
+# RUN: .foo : { \
+# RUN: begin_foo = .; \
+# RUN: PROVIDE(_begin_sec = .); \
+# RUN: *(.foo) \
+# RUN: end_foo = .; \
+# RUN: PROVIDE_HIDDEN(_end_sec = .); \
+# RUN: PROVIDE(_end_sec_abs = ABSOLUTE(.)); \
+# RUN: size_foo_1 = SIZEOF(.foo); \
+# RUN: size_foo_1_abs = ABSOLUTE(SIZEOF(.foo)); \
+# RUN: . = ALIGN(0x1000); \
+# RUN: begin_bar = .; \
+# RUN: *(.bar) \
+# RUN: end_bar = .; \
+# RUN: size_foo_2 = SIZEOF(.foo); } \
+# RUN: size_foo_3 = SIZEOF(.foo); \
+# RUN: .eh_frame_hdr : { \
+# RUN: __eh_frame_hdr_start = .; \
+# RUN: __eh_frame_hdr_start2 = ABSOLUTE(ALIGN(0x10)); \
+# RUN: *(.eh_frame_hdr) \
+# RUN: __eh_frame_hdr_end = .; \
+# RUN: __eh_frame_hdr_end2 = ABSOLUTE(ALIGN(0x10)); } \
+# RUN: .eh_frame : { } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --eh-frame-hdr --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=SIMPLE %s
+
+# Check that the following script is processed without errors
+# RUN: echo "SECTIONS { \
+# RUN: .eh_frame_hdr : { \
+# RUN: PROVIDE_HIDDEN(_begin_sec = .); \
+# RUN: *(.eh_frame_hdr) \
+# RUN: *(.eh_frame_hdr) \
+# RUN: PROVIDE_HIDDEN(_end_sec_abs = ABSOLUTE(.)); \
+# RUN: PROVIDE_HIDDEN(_end_sec = .); } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --eh-frame-hdr --script %t.script %t
+
+# Check that we can specify synthetic symbols without defining SECTIONS.
+# RUN: echo "PROVIDE_HIDDEN(_begin_sec = _start); \
+# RUN: PROVIDE_HIDDEN(_end_sec = ADDR(.text) + SIZEOF(.text));" > %t.script
+# RUN: ld.lld -o %t1 --eh-frame-hdr --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=NO-SEC %s
+
+# Check that we can do the same as above inside SECTIONS block.
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x201000; \
+# RUN: .text : { *(.text) } \
+# RUN: PROVIDE_HIDDEN(_begin_sec = ADDR(.text)); \
+# RUN: PROVIDE_HIDDEN(_end_sec = ADDR(.text) + SIZEOF(.text)); }" > %t.script
+# RUN: ld.lld -o %t1 --eh-frame-hdr --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=IN-SEC %s
+
+# SIMPLE: 0000000000000128 .foo 00000000 .hidden _end_sec
+# SIMPLE-NEXT: 0000000000000120 .foo 00000000 _begin_sec
+# SIMPLE-NEXT: 0000000000000128 *ABS* 00000000 _end_sec_abs
+# SIMPLE-NEXT: 0000000000001048 .text 00000000 _start
+# SIMPLE-NEXT: 0000000000000120 .foo 00000000 begin_foo
+# SIMPLE-NEXT: 0000000000000128 .foo 00000000 end_foo
+# SIMPLE-NEXT: 0000000000000008 *ABS* 00000000 size_foo_1
+# SIMPLE-NEXT: 0000000000000008 *ABS* 00000000 size_foo_1_abs
+# SIMPLE-NEXT: 0000000000001000 .foo 00000000 begin_bar
+# SIMPLE-NEXT: 0000000000001004 .foo 00000000 end_bar
+# SIMPLE-NEXT: 0000000000000ee4 *ABS* 00000000 size_foo_2
+# SIMPLE-NEXT: 0000000000000ee4 *ABS* 00000000 size_foo_3
+# SIMPLE-NEXT: 0000000000001004 .eh_frame_hdr 00000000 __eh_frame_hdr_start
+# SIMPLE-NEXT: 0000000000001010 *ABS* 00000000 __eh_frame_hdr_start2
+# SIMPLE-NEXT: 0000000000001018 .eh_frame_hdr 00000000 __eh_frame_hdr_end
+# SIMPLE-NEXT: 0000000000001020 *ABS* 00000000 __eh_frame_hdr_end2
+
+# NO-SEC: 0000000000201000 .text 00000000 .hidden _begin_sec
+# NO-SEC-NEXT: 0000000000201001 .text 00000000 .hidden _end_sec
+
+# IN-SEC: 0000000000201000 .text 00000000 .hidden _begin_sec
+# IN-SEC-NEXT: 0000000000201001 .text 00000000 .hidden _end_sec
+
+.global _start
+_start:
+ nop
+
+.section .foo,"a"
+ .quad 0
+
+.section .bar,"a"
+ .long 0
+
+.section .dah,"ax",@progbits
+ .cfi_startproc
+ nop
+ .cfi_endproc
+
+.global _begin_sec, _end_sec, _end_sec_abs
diff --git a/test/ELF/linkerscript/symbols.s b/test/ELF/linkerscript/symbols.s
new file mode 100644
index 000000000000..4656635171c8
--- /dev/null
+++ b/test/ELF/linkerscript/symbols.s
@@ -0,0 +1,84 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# Simple symbol assignment. Should raise conflict in case we
+# have duplicates in any input section, but currently simply
+# replaces the value.
+# RUN: echo "SECTIONS {.text : {*(.text.*)} text_end = .;}" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=SIMPLE %s
+# SIMPLE: .text 00000000 text_end
+
+# The symbol is not referenced. Don't provide it.
+# RUN: echo "SECTIONS { PROVIDE(newsym = 1);}" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=PROVIDE1 %s
+# PROVIDE1-NOT: 0000000000000001 *ABS* 00000000 newsym
+
+# The symbol is not referenced. Don't provide it.
+# RUN: echo "SECTIONS { PROVIDE_HIDDEN(newsym = 1);}" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=HIDDEN1 %s
+# HIDDEN1-NOT: 0000000000000001 *ABS* 00000000 .hidden newsym
+
+# Provide existing symbol. The value should be 0, even though we
+# have value of 1 in PROVIDE()
+# RUN: echo "SECTIONS { PROVIDE(somesym = 1);}" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=PROVIDE2 %s
+# PROVIDE2: 0000000000000000 *ABS* 00000000 somesym
+
+# Provide existing symbol. The value should be 0, even though we
+# have value of 1 in PROVIDE_HIDDEN(). Visibility should not change
+# RUN: echo "SECTIONS { PROVIDE_HIDDEN(somesym = 1);}" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=HIDDEN2 %s
+# HIDDEN2: 0000000000000000 *ABS* 00000000 somesym
+
+# Hidden symbol assignment.
+# RUN: echo "SECTIONS { HIDDEN(newsym = 1);}" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=HIDDEN3 %s
+# HIDDEN3: 0000000000000001 *ABS* 00000000 .hidden newsym
+
+# The symbol is not referenced. Don't provide it.
+# RUN: echo "PROVIDE(newsym = 1);" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=PROVIDE4 %s
+# PROVIDE4-NOT: 0000000000000001 *ABS* 00000000 newsym
+
+# The symbol is not referenced. Don't provide it.
+# RUN: echo "PROVIDE_HIDDEN(newsym = 1);" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=HIDDEN4 %s
+# HIDDEN4-NOT: 0000000000000001 *ABS* 00000000 .hidden newsym
+
+# Provide existing symbol. The value should be 0, even though we
+# have value of 1 in PROVIDE()
+# RUN: echo "PROVIDE(somesym = 1);" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=PROVIDE5 %s
+# PROVIDE5: 0000000000000000 *ABS* 00000000 somesym
+
+# Provide existing symbol. The value should be 0, even though we
+# have value of 1 in PROVIDE_HIDDEN(). Visibility should not change
+# RUN: echo "PROVIDE_HIDDEN(somesym = 1);" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=HIDDEN5 %s
+# HIDDEN5: 0000000000000000 *ABS* 00000000 somesym
+
+# Simple symbol assignment. All three symbols should have the
+# same value.
+# RUN: echo "foo = 0x100; SECTIONS { bar = foo; } baz = bar;" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=SIMPLE2 %s
+# SIMPLE2: 0000000000000100 *ABS* 00000000 foo
+# SIMPLE2: 0000000000000100 *ABS* 00000000 bar
+# SIMPLE2: 0000000000000100 *ABS* 00000000 baz
+
+.global _start
+_start:
+ nop
+
+.global somesym
+somesym = 0
diff --git a/test/ELF/linkerscript/tbss.s b/test/ELF/linkerscript/tbss.s
new file mode 100644
index 000000000000..de8ed50cb4a4
--- /dev/null
+++ b/test/ELF/linkerscript/tbss.s
@@ -0,0 +1,42 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
+# RUN: .text : { *(.text) } \
+# RUN: foo : { *(foo) } \
+# RUN: bar : { *(bar) } \
+# RUN: }" > %t.script
+# RUN: ld.lld -T %t.script %t.o -o %t
+# RUN: llvm-readobj -s %t | FileCheck %s
+
+# test that a tbss section doesn't use address space.
+
+# CHECK: Name: foo
+# CHECK-NEXT: Type: SHT_NOBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_TLS
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x[[ADDR:.*]]
+# CHECK-NEXT: Offset: 0x[[ADDR]]
+# CHECK-NEXT: Size: 4
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 1
+# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: }
+# CHECK-NEXT: Section {
+# CHECK-NEXT: Index:
+# CHECK-NEXT: Name: bar
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x[[ADDR]]
+
+ .section foo,"awT",@nobits
+ .long 0
+ .section bar, "aw"
+ .long 0
diff --git a/test/ELF/linkerscript-symbol-conflict.s b/test/ELF/linkerscript/undef.s
index 30186ed5cb87..85ad0767b5ed 100644
--- a/test/ELF/linkerscript-symbol-conflict.s
+++ b/test/ELF/linkerscript/undef.s
@@ -1,11 +1,11 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: echo "SECTIONS {.text : {*(.text.*)} end = .;}" > %t.script
+# RUN: echo "SECTIONS { patatino = 0x1234; }" > %t.script
# RUN: ld.lld -o %t1 --script %t.script %t
# RUN: llvm-objdump -t %t1 | FileCheck %s
-# CHECK: 0000000000000121 *ABS* 00000000 end
+# CHECK: 0000000000001234 *ABS* 00000000 patatino
.global _start
_start:
- nop
+ call patatino
diff --git a/test/ELF/linkerscript-va.s b/test/ELF/linkerscript/va.s
index 25d0bd2268c0..854ebcef0146 100644
--- a/test/ELF/linkerscript-va.s
+++ b/test/ELF/linkerscript/va.s
@@ -7,9 +7,9 @@
# CHECK: Sections:
# CHECK-NEXT: Idx Name Size Address Type
# CHECK-NEXT: 0 00000000 0000000000000000
-# CHECK-NEXT: 1 .foo 00000004 0000000000000120 DATA
-# CHECK-NEXT: 2 .boo 00000004 0000000000000124 DATA
-# CHECK-NEXT: 3 .text 00000001 0000000000000128 TEXT DATA
+# CHECK-NEXT: 1 .text 00000001 0000000000000000 TEXT DATA
+# CHECK-NEXT: 2 .foo 00000004 0000000000000001 DATA
+# CHECK-NEXT: 3 .boo 00000004 0000000000000005 DATA
.global _start
_start:
diff --git a/test/ELF/linkerscript/visibility.s b/test/ELF/linkerscript/visibility.s
new file mode 100644
index 000000000000..9d9fcf0c5cdc
--- /dev/null
+++ b/test/ELF/linkerscript/visibility.s
@@ -0,0 +1,22 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+
+# RUN: echo "SECTIONS { foo = .; }" > %t1.script
+# RUN: ld.lld -o %t1 --script %t1.script %t.o -shared
+# RUN: llvm-readobj -t %t1 | FileCheck %s
+
+# CHECK: Symbol {
+# CHECK: Name: foo
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Local
+# CHECK-NEXT: Type:
+# CHECK-NEXT: Other [
+# CHECK-NEXT: STV_HIDDEN
+# CHECK-NEXT: ]
+# CHECK-NEXT: Section:
+# CHECK-NEXT: }
+
+ .data
+ .hidden foo
+ .long foo
diff --git a/test/ELF/linkerscript/wildcards.s b/test/ELF/linkerscript/wildcards.s
new file mode 100644
index 000000000000..4e9469e40773
--- /dev/null
+++ b/test/ELF/linkerscript/wildcards.s
@@ -0,0 +1,83 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+## Default case: abc and abx included in text.
+# RUN: echo "SECTIONS { \
+# RUN: .text : { *(.abc .abx) } }" > %t.script
+# RUN: ld.lld -o %t.out --script %t.script %t
+# RUN: llvm-objdump -section-headers %t.out | \
+# RUN: FileCheck -check-prefix=SEC-DEFAULT %s
+# SEC-DEFAULT: Sections:
+# SEC-DEFAULT-NEXT: Idx Name Size
+# SEC-DEFAULT-NEXT: 0 00000000
+# SEC-DEFAULT-NEXT: 1 .text 00000008
+# SEC-DEFAULT-NEXT: 2 .abcd 00000004
+# SEC-DEFAULT-NEXT: 3 .ad 00000004
+# SEC-DEFAULT-NEXT: 4 .ag 00000004
+# SEC-DEFAULT-NEXT: 5 .comment 00000008 {{[0-9a-f]*}}
+# SEC-DEFAULT-NEXT: 6 .symtab 00000030
+# SEC-DEFAULT-NEXT: 7 .shstrtab 00000038
+# SEC-DEFAULT-NEXT: 8 .strtab 00000008
+
+## Now replace the symbol with '?' and check that results are the same.
+# RUN: echo "SECTIONS { \
+# RUN: .text : { *(.abc .ab?) } }" > %t.script
+# RUN: ld.lld -o %t.out --script %t.script %t
+# RUN: llvm-objdump -section-headers %t.out | \
+# RUN: FileCheck -check-prefix=SEC-DEFAULT %s
+
+## Now see how replacing '?' with '*' will consume whole abcd.
+# RUN: echo "SECTIONS { \
+# RUN: .text : { *(.abc .ab*) } }" > %t.script
+# RUN: ld.lld -o %t.out --script %t.script %t
+# RUN: llvm-objdump -section-headers %t.out | \
+# RUN: FileCheck -check-prefix=SEC-ALL %s
+# SEC-ALL: Sections:
+# SEC-ALL-NEXT: Idx Name Size
+# SEC-ALL-NEXT: 0 00000000
+# SEC-ALL-NEXT: 1 .text 0000000c
+# SEC-ALL-NEXT: 2 .ad 00000004
+# SEC-ALL-NEXT: 3 .ag 00000004
+# SEC-ALL-NEXT: 4 .comment 00000008
+# SEC-ALL-NEXT: 5 .symtab 00000030
+# SEC-ALL-NEXT: 6 .shstrtab 00000032
+# SEC-ALL-NEXT: 7 .strtab 00000008
+
+## All sections started with .a are merged.
+# RUN: echo "SECTIONS { \
+# RUN: .text : { *(.a*) } }" > %t.script
+# RUN: ld.lld -o %t.out --script %t.script %t
+# RUN: llvm-objdump -section-headers %t.out | \
+# RUN: FileCheck -check-prefix=SEC-NO %s
+# SEC-NO: Sections:
+# SEC-NO-NEXT: Idx Name Size
+# SEC-NO-NEXT: 0 00000000
+# SEC-NO-NEXT: 1 .text 00000014
+# SEC-NO-NEXT: 2 .comment 00000008
+# SEC-NO-NEXT: 3 .symtab 00000030
+# SEC-NO-NEXT: 4 .shstrtab 0000002a
+# SEC-NO-NEXT: 5 .strtab 00000008
+
+.text
+.section .abc,"ax",@progbits
+.long 0
+
+.text
+.section .abx,"ax",@progbits
+.long 0
+
+.text
+.section .abcd,"ax",@progbits
+.long 0
+
+.text
+.section .ad,"ax",@progbits
+.long 0
+
+.text
+.section .ag,"ax",@progbits
+.long 0
+
+
+.globl _start
+_start:
diff --git a/test/ELF/linkerscript/wildcards2.s b/test/ELF/linkerscript/wildcards2.s
new file mode 100644
index 000000000000..5e8d6a3d309a
--- /dev/null
+++ b/test/ELF/linkerscript/wildcards2.s
@@ -0,0 +1,25 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+## Check that aabc is not included in text.
+# RUN: echo "SECTIONS { \
+# RUN: .text : { *(.abc) } }" > %t.script
+# RUN: ld.lld -o %t.out --script %t.script %t
+# RUN: llvm-objdump -section-headers %t.out | \
+# RUN: FileCheck %s
+# CHECK: Sections:
+# CHECK-NEXT: Idx Name Size
+# CHECK-NEXT: 0 00000000
+# CHECK-NEXT: 1 .text 00000004
+# CHECK-NEXT: 2 aabc 00000004
+
+.text
+.section .abc,"ax",@progbits
+.long 0
+
+.text
+.section aabc,"ax",@progbits
+.long 0
+
+.globl _start
+_start:
diff --git a/test/ELF/local-dynamic.s b/test/ELF/local-dynamic.s
index 436516a91190..797a1071311c 100644
--- a/test/ELF/local-dynamic.s
+++ b/test/ELF/local-dynamic.s
@@ -16,7 +16,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: blah
-// CHECK-NEXT: Value: 0x1000
+// CHECK-NEXT: Value:
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -25,7 +25,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: foo
-// CHECK-NEXT: Value: 0x1000
+// CHECK-NEXT: Value:
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -34,7 +34,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: goo
-// CHECK-NEXT: Value: 0x1000
+// CHECK-NEXT: Value:
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -43,7 +43,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: _DYNAMIC
-// CHECK-NEXT: Value: 0x1000
+// CHECK-NEXT: Value:
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -54,7 +54,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: _start
-// CHECK-NEXT: Value: 0x1000
+// CHECK-NEXT: Value:
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: None
@@ -75,7 +75,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: _start@
-// CHECK-NEXT: Value: 0x1000
+// CHECK-NEXT: Value:
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: None
diff --git a/test/ELF/local-got-pie.s b/test/ELF/local-got-pie.s
index e846bd454445..417f9d002034 100644
--- a/test/ELF/local-got-pie.s
+++ b/test/ELF/local-got-pie.s
@@ -1,6 +1,6 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
// RUN: ld.lld %t.o -o %t -pie
-// RUN: llvm-readobj -s -r %t | FileCheck %s
+// RUN: llvm-readobj -s -r -d %t | FileCheck %s
// RUN: llvm-objdump -d %t | FileCheck --check-prefix=DISASM %s
.globl _start
@@ -12,10 +12,10 @@ _start:
foo:
nop
-// 0x20A0 - 1001 - 5 = 4250
+// 0x20B0 - 1001 - 5 = 4266
// DISASM: Disassembly of section .text:
// DISASM-NEXT: _start:
-// DISASM-NEXT: 1000: {{.*}} callq 4251
+// DISASM-NEXT: 1000: {{.*}} callq 4267
// DISASM: foo:
// DISASM-NEXT: 1005: {{.*}} nop
@@ -25,12 +25,13 @@ foo:
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x20A0
+// CHECK-NEXT: Address: 0x20B0
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 8
// CHECK: Relocations [
// CHECK-NEXT: Section ({{.*}}) .rela.dyn {
-// CHECK-NEXT: 0x20A0 R_X86_64_RELATIVE - 0x1005
+// CHECK-NEXT: 0x20B0 R_X86_64_RELATIVE - 0x1005
// CHECK-NEXT: }
// CHECK-NEXT: ]
+// CHECK: 0x000000006FFFFFF9 RELACOUNT 1
diff --git a/test/ELF/local-got-shared.s b/test/ELF/local-got-shared.s
index 08e6583f481f..e4fd46997517 100644
--- a/test/ELF/local-got-shared.s
+++ b/test/ELF/local-got-shared.s
@@ -1,6 +1,6 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
// RUN: ld.lld %t.o -o %t -shared
-// RUN: llvm-readobj -s -r %t | FileCheck %s
+// RUN: llvm-readobj -s -r -d %t | FileCheck %s
// RUN: llvm-objdump -d %t | FileCheck --check-prefix=DISASM %s
bar:
@@ -11,9 +11,9 @@ bar:
foo:
nop
-// 0x2090 - 0x1000 - 5 = 4235
+// 0x20A0 - 0x1000 - 5 = 4251
// DISASM: bar:
-// DISASM-NEXT: 1000: {{.*}} callq 4235
+// DISASM-NEXT: 1000: {{.*}} callq 4251
// DISASM: foo:
// DISASM-NEXT: 1005: {{.*}} nop
@@ -24,12 +24,13 @@ foo:
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x2090
+// CHECK-NEXT: Address: 0x20A0
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 8
// CHECK: Relocations [
// CHECK-NEXT: Section ({{.*}}) .rela.dyn {
-// CHECK-NEXT: 0x2090 R_X86_64_RELATIVE - 0x1005
+// CHECK-NEXT: 0x20A0 R_X86_64_RELATIVE - 0x1005
// CHECK-NEXT: }
// CHECK-NEXT: ]
+// CHECK: 0x000000006FFFFFF9 RELACOUNT 1
diff --git a/test/ELF/local-got.s b/test/ELF/local-got.s
index 1a8fa0dfd253..7e6ef9e0be7e 100644
--- a/test/ELF/local-got.s
+++ b/test/ELF/local-got.s
@@ -14,14 +14,14 @@ _start:
foo:
nop
-// 0x120B0 - 0x11000 - 5 = 4251
-// 0x120B8 - 0x11005 - 5 = 4254
+// 0x2020B0 - 0x201000 - 5 = 4251
+// 0x2020B8 - 0x201005 - 5 = 4254
// DISASM: _start:
-// DISASM-NEXT: 11000: {{.*}} callq 4267
-// DISASM-NEXT: 11005: {{.*}} callq 4270
+// DISASM-NEXT: 201000: {{.*}} callq 4267
+// DISASM-NEXT: 201005: {{.*}} callq 4270
// DISASM: foo:
-// DISASM-NEXT: 1100a: {{.*}} nop
+// DISASM-NEXT: 20100a: {{.*}} nop
// CHECK: Name: .got
// CHECK-NEXT: Type: SHT_PROGBITS
@@ -29,7 +29,7 @@ foo:
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x120B0
+// CHECK-NEXT: Address: 0x2020B0
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 16
// CHECK-NEXT: Link: 0
@@ -37,12 +37,12 @@ foo:
// CHECK-NEXT: AddressAlignment: 8
// CHECK-NEXT: EntrySize: 0
// CHECK-NEXT: SectionData (
-// 0x1200a in little endian
-// CHECK-NEXT: 0000: 00000000 00000000 0A100100 00000000
+// 0x20200a in little endian
+// CHECK-NEXT: 0000: 00000000 00000000 0A102000 00000000
// CHECK-NEXT: )
// CHECK: Relocations [
// CHECK-NEXT: Section ({{.*}}) .rela.dyn {
-// CHECK-NEXT: 0x120B0 R_X86_64_GLOB_DAT bar 0x0
+// CHECK-NEXT: 0x2020B0 R_X86_64_GLOB_DAT bar 0x0
// CHECK-NEXT: }
// CHECK-NEXT: ]
diff --git a/test/ELF/local.s b/test/ELF/local.s
index cc879e15c0db..983d7ff7ba63 100644
--- a/test/ELF/local.s
+++ b/test/ELF/local.s
@@ -1,5 +1,5 @@
// Check that symbol table is correctly populated with local symbols.
-// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
+// RUN: llvm-mc -save-temp-labels -filetype=obj -triple=x86_64-pc-linux %s -o %t
// RUN: ld.lld %t -o %t1
// RUN: llvm-readobj -t -s %t1 | FileCheck %s
// REQUIRES: x86
@@ -14,7 +14,7 @@
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size:
// CHECK-NEXT: Link:
-// CHECK-NEXT: Info: 5
+// CHECK-NEXT: Info: 6
// CHECK: Symbols [
// CHECK-NEXT: Symbol {
@@ -27,8 +27,17 @@
// CHECK-NEXT: Section: Undefined
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
+// CHECK-NEXT: Name: .Labs
+// CHECK-NEXT: Value:
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Local
+// CHECK-NEXT: Type: None
+// CHECK-NEXT: Other: 0
+// CHECK-NEXT: Section: Absolute
+// CHECK-NEXT: }
+// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: abs
-// CHECK-NEXT: Value: 0x2A
+// CHECK-NEXT: Value:
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -37,7 +46,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: blah
-// CHECK-NEXT: Value: 0x11000
+// CHECK-NEXT: Value:
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -46,7 +55,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: foo
-// CHECK-NEXT: Value: 0x11000
+// CHECK-NEXT: Value:
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -55,7 +64,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: goo
-// CHECK-NEXT: Value: 0x11000
+// CHECK-NEXT: Value:
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -64,7 +73,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: _start
-// CHECK-NEXT: Value: 0x11000
+// CHECK-NEXT: Value:
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: None
@@ -80,3 +89,4 @@ blah:
foo:
goo:
abs = 42
+.Labs = 43
diff --git a/test/ELF/lto/Inputs/common3.ll b/test/ELF/lto/Inputs/common3.ll
new file mode 100644
index 000000000000..a4efc6591570
--- /dev/null
+++ b/test/ELF/lto/Inputs/common3.ll
@@ -0,0 +1,3 @@
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+@a = common hidden global i64 0, align 4
diff --git a/test/ELF/lto/Inputs/thin1.ll b/test/ELF/lto/Inputs/thin1.ll
new file mode 100644
index 000000000000..93029837714b
--- /dev/null
+++ b/test/ELF/lto/Inputs/thin1.ll
@@ -0,0 +1,12 @@
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-scei-ps4"
+
+define i32 @foo(i32 %goo) {
+entry:
+ %goo.addr = alloca i32, align 4
+ store i32 %goo, i32* %goo.addr, align 4
+ %0 = load i32, i32* %goo.addr, align 4
+ %1 = load i32, i32* %goo.addr, align 4
+ %mul = mul nsw i32 %0, %1
+ ret i32 %mul
+}
diff --git a/test/ELF/lto/Inputs/thin2.ll b/test/ELF/lto/Inputs/thin2.ll
new file mode 100644
index 000000000000..6cffbdcc53f2
--- /dev/null
+++ b/test/ELF/lto/Inputs/thin2.ll
@@ -0,0 +1,11 @@
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-scei-ps4"
+
+define i32 @blah(i32 %meh) #0 {
+entry:
+ %meh.addr = alloca i32, align 4
+ store i32 %meh, i32* %meh.addr, align 4
+ %0 = load i32, i32* %meh.addr, align 4
+ %sub = sub nsw i32 %0, 48
+ ret i32 %sub
+}
diff --git a/test/ELF/lto/Inputs/thinlto.ll b/test/ELF/lto/Inputs/thinlto.ll
new file mode 100644
index 000000000000..31c72ec4653a
--- /dev/null
+++ b/test/ELF/lto/Inputs/thinlto.ll
@@ -0,0 +1,7 @@
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define void @g() {
+entry:
+ ret void
+}
diff --git a/test/ELF/lto/Inputs/unnamed-addr-drop.ll b/test/ELF/lto/Inputs/unnamed-addr-drop.ll
new file mode 100644
index 000000000000..b91bc8e5a891
--- /dev/null
+++ b/test/ELF/lto/Inputs/unnamed-addr-drop.ll
@@ -0,0 +1,4 @@
+target triple = "x86_64-unknown-linux-gnu"
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+@foo = unnamed_addr constant i32 42
diff --git a/test/ELF/lto/archive-3.ll b/test/ELF/lto/archive-3.ll
index 350c8929c9df..0322e412539a 100644
--- a/test/ELF/lto/archive-3.ll
+++ b/test/ELF/lto/archive-3.ll
@@ -3,12 +3,12 @@
; RUN: llvm-as %s -o %t2.o
; RUN: ld.lld -m elf_x86_64 %t1.o %t2.o -o %t3 -save-temps
-; RUN: llvm-dis %t3.lto.bc -o - | FileCheck %s
+; RUN: llvm-dis %t3.0.2.internalize.bc -o - | FileCheck %s
; RUN: rm -f %t.a
; RUN: llvm-ar rcs %t.a %t1.o
; RUN: ld.lld -m elf_x86_64 %t.a %t1.o %t2.o -o %t3 -save-temps
-; RUN: llvm-dis %t3.lto.bc -o - | FileCheck %s
+; RUN: llvm-dis %t3.0.2.internalize.bc -o - | FileCheck %s
; CHECK: define internal void @foo() {
diff --git a/test/ELF/lto/archive.ll b/test/ELF/lto/archive.ll
index b3f69fb9920f..b4d011fdb888 100644
--- a/test/ELF/lto/archive.ll
+++ b/test/ELF/lto/archive.ll
@@ -8,7 +8,6 @@
; RUN: ld.lld -m elf_x86_64 %t2.o --whole-archive %t.a -o %t3 -shared
; RUN: llvm-readobj -t %t3 | FileCheck %s
-
; CHECK: Name: g (
; CHECK-NEXT: Value:
; CHECK-NEXT: Size:
diff --git a/test/ELF/lto/asmundef.ll b/test/ELF/lto/asmundef.ll
index d76e418fce81..1c87cd01fad8 100644
--- a/test/ELF/lto/asmundef.ll
+++ b/test/ELF/lto/asmundef.ll
@@ -1,7 +1,7 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 %t.o -o %t -save-temps
-; RUN: llvm-dis %t.lto.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.0.4.opt.bc -o - | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
diff --git a/test/ELF/lto/available-externally.ll b/test/ELF/lto/available-externally.ll
index 74aa86002c9e..181042b9d1e1 100644
--- a/test/ELF/lto/available-externally.ll
+++ b/test/ELF/lto/available-externally.ll
@@ -1,7 +1,7 @@
; RUN: llvm-as %s -o %t1.o
; RUN: llvm-as %p/Inputs/available-externally.ll -o %t2.o
; RUN: ld.lld %t1.o %t2.o -m elf_x86_64 -o %t.so -shared -save-temps
-; RUN: llvm-dis < %t.so.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t.so.0.2.internalize.bc | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
diff --git a/test/ELF/lto/invalid-bitcode.ll b/test/ELF/lto/bitcode-nodatalayout.ll
index eceffb10d8ad..5c4883a42444 100644
--- a/test/ELF/lto/invalid-bitcode.ll
+++ b/test/ELF/lto/bitcode-nodatalayout.ll
@@ -1,10 +1,11 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
-; RUN: not ld.lld -m elf_x86_64 %t.o 2>&1 | FileCheck %s
+; RUN: not ld.lld -m elf_x86_64 %t.o -o %t 2>&1 | FileCheck %s
-; CHECK: invalid bitcode file:
+; CHECK: input module has no datalayout
; This bitcode file has no datalayout.
+; Check that we error out producing a reasonable diagnostic.
target triple = "x86_64-unknown-linux-gnu"
define void @_start() {
diff --git a/test/ELF/lto/combined-lto-object-name.ll b/test/ELF/lto/combined-lto-object-name.ll
index f5b7e3ae40e6..f46efc4f7387 100644
--- a/test/ELF/lto/combined-lto-object-name.ll
+++ b/test/ELF/lto/combined-lto-object-name.ll
@@ -11,4 +11,4 @@ define void @_start() {
ret void
}
-; CHECK: undefined symbol: foo in {{.*}}combined-lto-object-name.ll.tmp.o
+; CHECK: error: ld-temp.o:(function _start): undefined symbol 'foo'
diff --git a/test/ELF/lto/common2.ll b/test/ELF/lto/common2.ll
index 59a2676e4fc9..b44bbac4fda3 100644
--- a/test/ELF/lto/common2.ll
+++ b/test/ELF/lto/common2.ll
@@ -1,21 +1,24 @@
; RUN: llvm-as %s -o %t1.o
; RUN: ld.lld -m elf_x86_64 %t1.o -o %t -shared -save-temps
-; RUN: llvm-dis < %t.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t.0.2.internalize.bc | FileCheck %s
; RUN: llvm-readobj -t %t | FileCheck %s --check-prefix=SHARED
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@a = common global i8 0, align 8
+; CHECK-DAG: @a = common global i8 0, align 8
-; Shared library case, we ensure that the bitcode generated file
-; has not the a symbol but is appears in the final shared library
-; produced.
-; CHECK-NOT: @a = common global i8 0, align 8
+@b = common hidden global i32 0, align 4
+define i32 @f() {
+ %t = load i32, i32* @b, align 4
+ ret i32 %t
+}
+; CHECK-DAG: @b = internal global i32 0, align 4
; SHARED: Symbol {
; SHARED: Name: a
-; SHARED-NEXT: Value: 0x2000
+; SHARED-NEXT: Value:
; SHARED-NEXT: Size: 1
; SHARED-NEXT: Binding: Global
; SHARED-NEXT: Type: Object
diff --git a/test/ELF/lto/common3.ll b/test/ELF/lto/common3.ll
new file mode 100644
index 000000000000..6d40de547fcd
--- /dev/null
+++ b/test/ELF/lto/common3.ll
@@ -0,0 +1,14 @@
+; RUN: llvm-as %s -o %t1.o
+; RUN: llvm-as %S/Inputs/common3.ll -o %t2.o
+; RUN: ld.lld -m elf_x86_64 %t1.o %t2.o -o %t -shared -save-temps
+; RUN: llvm-dis < %t.0.2.internalize.bc | FileCheck %s
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+@a = common hidden global i32 0, align 8
+define i32 @f() {
+ %t = load i32, i32* @a, align 4
+ ret i32 %t
+}
+
+; CHECK: @a = internal global i64 0, align 8
diff --git a/test/ELF/lto/discard-value-names.ll b/test/ELF/lto/discard-value-names.ll
index c6cd94fd2e18..c71dc386113d 100644
--- a/test/ELF/lto/discard-value-names.ll
+++ b/test/ELF/lto/discard-value-names.ll
@@ -1,7 +1,7 @@
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 -shared -save-temps %t.o -o %t2.o
-; RUN: llvm-dis < %t2.o.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t2.o.0.0.preopt.bc | FileCheck %s
; CHECK: @GlobalValueName
; CHECK: @foo(i32 %in)
diff --git a/test/ELF/lto/drop-debug-info.ll b/test/ELF/lto/drop-debug-info.ll
index 7a7ed5ea41d1..27c0260080eb 100644
--- a/test/ELF/lto/drop-debug-info.ll
+++ b/test/ELF/lto/drop-debug-info.ll
@@ -5,5 +5,5 @@
;
; RUN: ld.lld -m elf_x86_64 -shared %p/Inputs/drop-debug-info.bc \
; RUN: -disable-verify 2>&1 | FileCheck %s
-; CHECK: warning: ignoring debug info with an invalid version (1) in {{.*}}drop-debug-info.bc
+; CHECK: ignoring debug info with an invalid version (1) in {{.*}}drop-debug-info.bc
diff --git a/test/ELF/lto/drop-linkage.ll b/test/ELF/lto/drop-linkage.ll
index fd111c18bd13..1ff179666f0e 100644
--- a/test/ELF/lto/drop-linkage.ll
+++ b/test/ELF/lto/drop-linkage.ll
@@ -5,7 +5,7 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
; RUN: llc %s -o %t.o -filetype=obj
; RUN: llvm-as %p/Inputs/drop-linkage.ll -o %t2.o
; RUN: ld.lld %t.o %t2.o -o %t.so -save-temps -shared
-; RUN: llvm-dis %t.so.lto.opt.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.4.opt.bc -o - | FileCheck %s
define void @foo() {
ret void
diff --git a/test/ELF/lto/duplicated.ll b/test/ELF/lto/duplicated.ll
index 6ef6772c5f20..903592c46067 100644
--- a/test/ELF/lto/duplicated.ll
+++ b/test/ELF/lto/duplicated.ll
@@ -1,7 +1,7 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: not ld.lld -m elf_x86_64 %t.o %t.o -o %t.so -shared 2>&1 | FileCheck %s
-; CHECK: duplicate symbol: f in {{.*}}.o and {{.*}}.o
+; CHECK: duplicate symbol 'f' in {{.*}}.o and {{.*}}.o
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
diff --git a/test/ELF/lto/dynsym.ll b/test/ELF/lto/dynsym.ll
index 5885960c7bc3..b2b4157820b5 100644
--- a/test/ELF/lto/dynsym.ll
+++ b/test/ELF/lto/dynsym.ll
@@ -5,6 +5,11 @@
; RUN: ld.lld -m elf_x86_64 %t2.o %t.so -o %t
; RUN: llvm-readobj -dyn-symbols %t | FileCheck %s
+; Check that we don't crash when gc'ing sections and printing the result.
+; RUN: ld.lld -m elf_x86_64 %t2.o %t.so --gc-sections --print-gc-sections \
+; RUN: -o %t
+; RUN: llvm-readobj -dyn-symbols %t | FileCheck %s
+
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
diff --git a/test/ELF/lto/internalize-basic.ll b/test/ELF/lto/internalize-basic.ll
index 396b9cb60f17..43c1837528f5 100644
--- a/test/ELF/lto/internalize-basic.ll
+++ b/test/ELF/lto/internalize-basic.ll
@@ -1,7 +1,7 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 -save-temps
-; RUN: llvm-dis < %t2.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t2.0.2.internalize.bc | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
diff --git a/test/ELF/lto/internalize-exportdyn.ll b/test/ELF/lto/internalize-exportdyn.ll
index bee9a1e6a1ee..2034a2b3ab72 100644
--- a/test/ELF/lto/internalize-exportdyn.ll
+++ b/test/ELF/lto/internalize-exportdyn.ll
@@ -2,7 +2,7 @@
; RUN: llvm-as %s -o %t.o
; RUN: llvm-as %p/Inputs/internalize-exportdyn.ll -o %t2.o
; RUN: ld.lld -m elf_x86_64 %t.o %t2.o -o %t2 --export-dynamic -save-temps
-; RUN: llvm-dis < %t2.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t2.0.2.internalize.bc | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
diff --git a/test/ELF/lto/internalize-llvmused.ll b/test/ELF/lto/internalize-llvmused.ll
index 46c90a65ff72..253dcb26d042 100644
--- a/test/ELF/lto/internalize-llvmused.ll
+++ b/test/ELF/lto/internalize-llvmused.ll
@@ -1,7 +1,7 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 -save-temps
-; RUN: llvm-dis < %t2.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t2.0.2.internalize.bc | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
diff --git a/test/ELF/lto/internalize-undef.ll b/test/ELF/lto/internalize-undef.ll
index 5d74c31eee8b..f76528bda3b0 100644
--- a/test/ELF/lto/internalize-undef.ll
+++ b/test/ELF/lto/internalize-undef.ll
@@ -2,7 +2,7 @@
; RUN: llvm-as %s -o %t.o
; RUN: llvm-as %p/Inputs/internalize-undef.ll -o %t2.o
; RUN: ld.lld -m elf_x86_64 %t.o %t2.o -o %t -save-temps
-; RUN: llvm-dis < %t.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t.0.2.internalize.bc | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
diff --git a/test/ELF/lto/internalize-version-script.ll b/test/ELF/lto/internalize-version-script.ll
index c25328fb56e2..c577e43b50e4 100644
--- a/test/ELF/lto/internalize-version-script.ll
+++ b/test/ELF/lto/internalize-version-script.ll
@@ -2,7 +2,7 @@
; RUN: llvm-as %s -o %t.o
; RUN: echo "{ global: foo; local: *; };" > %t.script
; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 -shared --version-script %t.script -save-temps
-; RUN: llvm-dis < %t2.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t2.0.2.internalize.bc | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
diff --git a/test/ELF/lto/irmover-error.ll b/test/ELF/lto/irmover-error.ll
index aee411441c47..8b9836d23ca4 100644
--- a/test/ELF/lto/irmover-error.ll
+++ b/test/ELF/lto/irmover-error.ll
@@ -2,7 +2,7 @@
; RUN: llvm-as -o %t2.bc %S/Inputs/irmover-error.ll
; RUN: not ld.lld -m elf_x86_64 %t1.bc %t2.bc -o %t 2>&1 | FileCheck %s
-; CHECK: failed to link module {{.*}}2.bc: linking module flags 'foo': IDs have conflicting values
+; CHECK: linking module flags 'foo': IDs have conflicting values
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
diff --git a/test/ELF/lto/linkonce-odr.ll b/test/ELF/lto/linkonce-odr.ll
index 569e27154545..44233513d1fb 100644
--- a/test/ELF/lto/linkonce-odr.ll
+++ b/test/ELF/lto/linkonce-odr.ll
@@ -2,7 +2,7 @@
; RUN: llvm-as %p/Inputs/linkonce-odr.ll -o %t1.o
; RUN: llc -relocation-model=pic %s -o %t2.o -filetype=obj
; RUN: ld.lld %t1.o %t2.o -o %t.so -shared -save-temps
-; RUN: llvm-dis %t.so.lto.opt.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.4.opt.bc -o - | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
diff --git a/test/ELF/lto/linkonce.ll b/test/ELF/lto/linkonce.ll
index f980eff887b1..6dba6a3fded7 100644
--- a/test/ELF/lto/linkonce.ll
+++ b/test/ELF/lto/linkonce.ll
@@ -2,7 +2,7 @@
; RUN: llvm-as %p/Inputs/linkonce.ll -o %t1.o
; RUN: llc -relocation-model=pic %s -o %t2.o -filetype=obj
; RUN: ld.lld %t1.o %t2.o -o %t.so -shared -save-temps
-; RUN: llvm-dis %t.so.lto.opt.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.4.opt.bc -o - | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
diff --git a/test/ELF/lto/ltopasses-basic.ll b/test/ELF/lto/ltopasses-basic.ll
index 5bd5f4122fd0..0c4ad8b9f17c 100644
--- a/test/ELF/lto/ltopasses-basic.ll
+++ b/test/ELF/lto/ltopasses-basic.ll
@@ -1,8 +1,7 @@
; REQUIRES: x86
-; RUN: rm -f %t.so.lto.bc %t.so.lto.opt.bc %t.so.lto.o
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 %t.o -o %t.so -save-temps -mllvm -debug-pass=Arguments -shared 2>&1 | FileCheck %s --check-prefix=MLLVM
-; RUN: llvm-dis %t.so.lto.opt.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.4.opt.bc -o - | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
diff --git a/test/ELF/lto/ltopasses-custom.ll b/test/ELF/lto/ltopasses-custom.ll
index 3e982e079fb1..a48959a32991 100644
--- a/test/ELF/lto/ltopasses-custom.ll
+++ b/test/ELF/lto/ltopasses-custom.ll
@@ -3,8 +3,8 @@
; RUN: ld.lld -m elf_x86_64 %t.o -o %t.so -save-temps --lto-aa-pipeline=basic-aa \
; RUN: --lto-newpm-passes=ipsccp -shared
; RUN: ld.lld -m elf_x86_64 %t.o -o %t2.so -save-temps --lto-newpm-passes=loweratomic -shared
-; RUN: llvm-dis %t.so.lto.opt.bc -o - | FileCheck %s
-; RUN: llvm-dis %t2.so.lto.opt.bc -o - | FileCheck %s --check-prefix=ATOMIC
+; RUN: llvm-dis %t.so.0.4.opt.bc -o - | FileCheck %s
+; RUN: llvm-dis %t2.so.0.4.opt.bc -o - | FileCheck %s --check-prefix=ATOMIC
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@@ -28,3 +28,10 @@ define void @barrier() {
; RUN: --lto-newpm-passes=iamnotapass -shared 2>&1 | \
; RUN: FileCheck %s --check-prefix=INVALID
; INVALID: unable to parse pass pipeline description: iamnotapass
+
+; Check that invalid AA pipelines are rejected gracefully.
+; RUN: not ld.lld -m elf_x86_64 %t.o -o %t2.so \
+; RUN: --lto-newpm-passes=globaldce --lto-aa-pipeline=patatino \
+; RUN: -shared 2>&1 | \
+; RUN: FileCheck %s --check-prefix=INVALIDAA
+; INVALIDAA: unable to parse AA pipeline description: patatino
diff --git a/test/ELF/lto/metadata.ll b/test/ELF/lto/metadata.ll
index 3e73de5aab47..2eaacaae2726 100644
--- a/test/ELF/lto/metadata.ll
+++ b/test/ELF/lto/metadata.ll
@@ -10,4 +10,6 @@ define weak void @foo(i32* %p) {
ret void
}
-!0 = !{!"Simple C/C++ TBAA"}
+!0 = !{!1, !1, i64 0}
+!1 = !{!"int", !2}
+!2 = !{!"Simple C/C++ TBAA"}
diff --git a/test/ELF/lto/parallel-internalize.ll b/test/ELF/lto/parallel-internalize.ll
index 58ed50eab894..a7ee4b35877e 100644
--- a/test/ELF/lto/parallel-internalize.ll
+++ b/test/ELF/lto/parallel-internalize.ll
@@ -1,6 +1,7 @@
; REQUIRES: x86
; RUN: llvm-as -o %t.bc %s
-; RUN: ld.lld -m elf_x86_64 --lto-jobs=2 -save-temps -o %t %t.bc -e foo --lto-O0
+; RUN: ld.lld -m elf_x86_64 --lto-partitions=2 -save-temps -o %t %t.bc \
+; RUN: -e foo --lto-O0
; RUN: llvm-readobj -t -dyn-symbols %t | FileCheck %s
; RUN: llvm-nm %t0.lto.o | FileCheck --check-prefix=CHECK0 %s
; RUN: llvm-nm %t1.lto.o | FileCheck --check-prefix=CHECK1 %s
@@ -16,8 +17,8 @@
; CHECK-NEXT: Section: Undefined (0x0)
; CHECK-NEXT: }
; CHECK-NEXT: Symbol {
-; CHECK-NEXT: Name: bar (5)
-; CHECK-NEXT: Value: 0x11010
+; CHECK-NEXT: Name: bar
+; CHECK-NEXT: Value: 0x201010
; CHECK-NEXT: Size: 8
; CHECK-NEXT: Binding: Local (0x0)
; CHECK-NEXT: Type: Function (0x2)
@@ -27,8 +28,8 @@
; CHECK-NEXT: Section: .text (0x2)
; CHECK-NEXT: }
; CHECK-NEXT: Symbol {
-; CHECK-NEXT: Name: foo (1)
-; CHECK-NEXT: Value: 0x11000
+; CHECK-NEXT: Name: foo
+; CHECK-NEXT: Value: 0x201000
; CHECK-NEXT: Size: 8
; CHECK-NEXT: Binding: Global (0x1)
; CHECK-NEXT: Type: Function (0x2)
diff --git a/test/ELF/lto/parallel.ll b/test/ELF/lto/parallel.ll
index 8ea62ef3ae08..c5d7ca4e2cf1 100644
--- a/test/ELF/lto/parallel.ll
+++ b/test/ELF/lto/parallel.ll
@@ -1,6 +1,6 @@
; REQUIRES: x86
; RUN: llvm-as -o %t.bc %s
-; RUN: ld.lld -m elf_x86_64 --lto-jobs=2 -save-temps -o %t %t.bc -shared
+; RUN: ld.lld -m elf_x86_64 --lto-partitions=2 -save-temps -o %t %t.bc -shared
; RUN: llvm-nm %t0.lto.o | FileCheck --check-prefix=CHECK0 %s
; RUN: llvm-nm %t1.lto.o | FileCheck --check-prefix=CHECK1 %s
diff --git a/test/ELF/lto/save-temps.ll b/test/ELF/lto/save-temps.ll
index 0b0f939c53f1..f7af99ed40af 100644
--- a/test/ELF/lto/save-temps.ll
+++ b/test/ELF/lto/save-temps.ll
@@ -5,9 +5,9 @@
; RUN: llvm-as %p/Inputs/save-temps.ll -o %t2.o
; RUN: ld.lld -shared -m elf_x86_64 %t.o %t2.o -save-temps
; RUN: llvm-nm a.out | FileCheck %s
-; RUN: llvm-nm a.out.lto.bc | FileCheck %s
+; RUN: llvm-nm a.out.0.0.preopt.bc | FileCheck %s
; RUN: llvm-nm a.out.lto.o | FileCheck %s
-; RUN: llvm-dis a.out.lto.bc
+; RUN: llvm-dis a.out.0.0.preopt.bc
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
diff --git a/test/ELF/lto/shlib-undefined.ll b/test/ELF/lto/shlib-undefined.ll
index db60de8e21b2..0250ed761927 100644
--- a/test/ELF/lto/shlib-undefined.ll
+++ b/test/ELF/lto/shlib-undefined.ll
@@ -7,7 +7,7 @@
; RUN: llvm-readobj -dyn-symbols %t | FileCheck %s
; CHECK: Name: __progname@
-; CHECK-NEXT: Value: 0x11010
+; CHECK-NEXT: Value: 0x201010
; CHECK-NEXT: Size: 1
; CHECK-NEXT: Binding: Global (0x1)
; CHECK-NEXT: Type: Function
diff --git a/test/ELF/lto/thin-archivecollision.ll b/test/ELF/lto/thin-archivecollision.ll
new file mode 100644
index 000000000000..4e07187f36a0
--- /dev/null
+++ b/test/ELF/lto/thin-archivecollision.ll
@@ -0,0 +1,29 @@
+; RUN: opt -module-summary %s -o %t.o
+; RUN: opt -module-summary %p/Inputs/thin1.ll -o %t.coll.o
+; RUN: llvm-ar rcs %t1.a %t.coll.o
+; RUN: opt -module-summary %p/Inputs/thin2.ll -o %t.coll.o
+; RUN: llvm-ar rcsc %t2.a %t.coll.o
+
+; RUN: ld.lld %t.o %t1.a %t2.a -o %t
+; RUN: llvm-nm %t | FileCheck %s
+
+; Check we handle this case correctly even in presence of --whole-archive.
+; RUN: ld.lld %t.o --whole-archive %t1.a %t2.a -o %t
+; RUN: llvm-nm %t | FileCheck %s
+
+; CHECK: T _start
+; CHECK: T blah
+; CHECK: T foo
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-scei-ps4"
+
+define i32 @_start() {
+entry:
+ %call = call i32 @foo(i32 23)
+ %call1 = call i32 @blah(i32 37)
+ ret i32 0
+}
+
+declare i32 @foo(i32) #1
+declare i32 @blah(i32) #1
diff --git a/test/ELF/lto/thinlto.ll b/test/ELF/lto/thinlto.ll
new file mode 100644
index 000000000000..160c83a18abf
--- /dev/null
+++ b/test/ELF/lto/thinlto.ll
@@ -0,0 +1,35 @@
+; Basic ThinLTO tests.
+; RUN: opt -module-summary %s -o %t.o
+; RUN: opt -module-summary %p/Inputs/thinlto.ll -o %t2.o
+
+; First force single-threaded mode
+; RUN: ld.lld -save-temps --thinlto-jobs=1 -shared %t.o %t2.o -o %t
+; RUN: llvm-nm %t0.lto.o | FileCheck %s --check-prefix=NM1-SINGLE
+; RUN: llvm-nm %t1.lto.o | FileCheck %s --check-prefix=NM2-SINGLE
+
+; NM1-SINGLE: T f
+; NM2-SINGLE: T g
+
+; Next force multi-threaded mode
+; RUN: ld.lld -save-temps --thinlto-jobs=2 -shared %t.o %t2.o -o %t2
+; RUN: llvm-nm %t20.lto.o | FileCheck %s --check-prefix=NM1
+; RUN: llvm-nm %t21.lto.o | FileCheck %s --check-prefix=NM2
+
+; NM1: T f
+; NM2: T g
+
+; Then check without --thinlto-jobs (which currently default to hardware_concurrency)
+; We just check that we don't crash or fail (as it's not sure which tests are
+; stable on the final output file itself.
+; RUN: ld.lld -shared %t.o %t2.o -o %t2
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare void @g(...)
+
+define void @f() {
+entry:
+ call void (...) @g()
+ ret void
+}
diff --git a/test/ELF/lto/timepasses.ll b/test/ELF/lto/timepasses.ll
new file mode 100644
index 000000000000..5c893e661945
--- /dev/null
+++ b/test/ELF/lto/timepasses.ll
@@ -0,0 +1,15 @@
+; We use lld -flavor gnu because llvm-lit will append --full-shutdown to
+; the ld.lld invocation.
+; REQUIRES: x86
+; RUN: llvm-as %s -o %t.o
+; RUN: lld -flavor gnu %t.o -o %t.so -shared -mllvm -time-passes 2>&1 | FileCheck %s
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define void @patatino() {
+ ret void
+}
+
+; We should get the output of -time-passes even when --full-shutdown is not specified.
+; CHECK: Total Execution Time
diff --git a/test/ELF/lto/type-merge.ll b/test/ELF/lto/type-merge.ll
index 98db53970b0c..d6f196d7c3ba 100644
--- a/test/ELF/lto/type-merge.ll
+++ b/test/ELF/lto/type-merge.ll
@@ -2,7 +2,7 @@
; RUN: llvm-as %s -o %t.o
; RUN: llvm-as %p/Inputs/type-merge.ll -o %t2.o
; RUN: ld.lld -m elf_x86_64 %t.o %t2.o -o %t -shared -save-temps
-; RUN: llvm-dis < %t.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t.0.0.preopt.bc | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
diff --git a/test/ELF/lto/type-merge2.ll b/test/ELF/lto/type-merge2.ll
index f0931ddc9d5e..45777a7e6a48 100644
--- a/test/ELF/lto/type-merge2.ll
+++ b/test/ELF/lto/type-merge2.ll
@@ -1,7 +1,7 @@
; RUN: llvm-as %s -o %t.o
; RUN: llvm-as %p/Inputs/type-merge2.ll -o %t2.o
; RUN: ld.lld -m elf_x86_64 %t.o %t2.o -o %t.so -shared -save-temps
-; RUN: llvm-dis %t.so.lto.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.0.preopt.bc -o - | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
diff --git a/test/ELF/lto/undefined-puts.ll b/test/ELF/lto/undefined-puts.ll
index 54fb32cef4a2..07b2b4ef6414 100644
--- a/test/ELF/lto/undefined-puts.ll
+++ b/test/ELF/lto/undefined-puts.ll
@@ -20,7 +20,7 @@ declare i32 @printf(i8*, ...)
; Check that puts symbol is present in the dynamic symbol table and
; there's a relocation for it.
; CHECK: Dynamic Relocations {
-; CHECK-NEXT: 0x13018 R_X86_64_JUMP_SLOT puts 0x0
+; CHECK-NEXT: 0x203018 R_X86_64_JUMP_SLOT puts 0x0
; CHECK-NEXT: }
; CHECK: DynamicSymbols [
diff --git a/test/ELF/lto/unnamed-addr-comdat.ll b/test/ELF/lto/unnamed-addr-comdat.ll
index c8c36de88d4c..ed48f3ba5e04 100644
--- a/test/ELF/lto/unnamed-addr-comdat.ll
+++ b/test/ELF/lto/unnamed-addr-comdat.ll
@@ -1,6 +1,6 @@
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 %t.o %t.o -o %t.so -save-temps -shared
-; RUN: llvm-dis %t.so.lto.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.2.internalize.bc -o - | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
diff --git a/test/ELF/lto/unnamed-addr-drop.ll b/test/ELF/lto/unnamed-addr-drop.ll
new file mode 100644
index 000000000000..9142537fe57d
--- /dev/null
+++ b/test/ELF/lto/unnamed-addr-drop.ll
@@ -0,0 +1,12 @@
+; RUN: llvm-as %s -o %t1.o
+; RUN: llvm-as %S/Inputs/unnamed-addr-drop.ll -o %t2.o
+; RUN: ld.lld -m elf_x86_64 %t1.o %t2.o -o %t.so -save-temps -shared
+; RUN: llvm-dis %t.so.0.2.internalize.bc -o - | FileCheck %s
+
+target triple = "x86_64-unknown-linux-gnu"
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+@foo = weak constant i32 41
+
+; Check that unnamed_addr is dropped during the merge.
+; CHECK: @foo = constant i32 42
diff --git a/test/ELF/lto/unnamed-addr-lib.ll b/test/ELF/lto/unnamed-addr-lib.ll
index c1c31f84bc8e..c2bc6016efd5 100644
--- a/test/ELF/lto/unnamed-addr-lib.ll
+++ b/test/ELF/lto/unnamed-addr-lib.ll
@@ -3,7 +3,7 @@
; RUN: llvm-mc %p/Inputs/unnamed-addr-lib.s -o %t2.o -filetype=obj -triple=x86_64-pc-linux
; RUN: ld.lld %t2.o -shared -o %t2.so
; RUN: ld.lld -m elf_x86_64 %t.o %t2.so -o %t.so -save-temps -shared
-; RUN: llvm-dis %t.so.lto.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.2.internalize.bc -o - | FileCheck %s
; This documents a small limitation of lld's internalization logic. We decide
; that bar should be in the symbol table because if it is it will preempt the
diff --git a/test/ELF/lto/unnamed-addr.ll b/test/ELF/lto/unnamed-addr.ll
index a2c0105fd85f..6a6dd73dad86 100644
--- a/test/ELF/lto/unnamed-addr.ll
+++ b/test/ELF/lto/unnamed-addr.ll
@@ -1,6 +1,6 @@
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 %t.o -o %t.so -save-temps -shared
-; RUN: llvm-dis %t.so.lto.opt.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.4.opt.bc -o - | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
diff --git a/test/ELF/lto/version-script.ll b/test/ELF/lto/version-script.ll
index 11a7f073ab51..c43b443ff749 100644
--- a/test/ELF/lto/version-script.ll
+++ b/test/ELF/lto/version-script.ll
@@ -2,7 +2,7 @@
; RUN: llvm-as %s -o %t.o
; RUN: echo "VERSION_1.0{ global: foo; local: *; }; VERSION_2.0{ global: bar; local: *; };" > %t.script
; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 -shared --version-script %t.script -save-temps
-; RUN: llvm-dis < %t2.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t2.0.0.preopt.bc | FileCheck %s
; RUN: llvm-readobj -V -dyn-symbols %t2 | FileCheck --check-prefix=DSO %s
target triple = "x86_64-unknown-linux-gnu"
diff --git a/test/ELF/merge-reloc.s b/test/ELF/merge-reloc.s
new file mode 100644
index 000000000000..2447e3e9444f
--- /dev/null
+++ b/test/ELF/merge-reloc.s
@@ -0,0 +1,92 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -r -o %t-rel
+# RUN: llvm-readobj -s -section-data %t-rel | FileCheck %s
+
+# When linker generates a relocatable object it should keep "merge"
+# sections as-is: do not merge content, do not join regular and
+# "merge" sections, do not joint "merge" sections with different
+# entry size.
+
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .data
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_MERGE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 12
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 4
+# CHECK-NEXT: EntrySize: 4
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 42000000 42000000 42000000
+# CHECK-NEXT: )
+# CHECK-NEXT: }
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .data
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_MERGE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 16
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 8
+# CHECK-NEXT: EntrySize: 8
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 42000000 42000000 42000000 42000000
+# CHECK-NEXT: )
+# CHECK-NEXT: }
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .data
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 16
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 1
+# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 42000000 42000000 42000000 42000000
+# CHECK-NEXT: )
+# CHECK-NEXT: }
+
+ .section .data.1,"aM",@progbits,4
+ .align 4
+ .global foo
+foo:
+ .long 0x42
+ .long 0x42
+ .long 0x42
+
+ .section .data.2,"aM",@progbits,8
+ .align 8
+ .global bar
+bar:
+ .long 0x42
+ .long 0x42
+ .long 0x42
+ .long 0x42
+
+ .data
+ .global gar
+zed:
+ .long 0x42
+ .long 0x42
+ .long 0x42
+ .long 0x42
diff --git a/test/ELF/merge-string-empty.s b/test/ELF/merge-string-empty.s
new file mode 100644
index 000000000000..0b82ce700a2c
--- /dev/null
+++ b/test/ELF/merge-string-empty.s
@@ -0,0 +1,12 @@
+// Ensure that a mergeable string with size 0 does not cause any issue.
+
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: ld.lld %t.o -o %t
+
+.globl _start, s
+.section .rodata.str1.1,"aMS",@progbits,1
+s:
+.text
+_start:
+ .quad s
diff --git a/test/ELF/merge-string-error.s b/test/ELF/merge-string-error.s
index c5088acf63b6..78895cecca9c 100644
--- a/test/ELF/merge-string-error.s
+++ b/test/ELF/merge-string-error.s
@@ -8,4 +8,4 @@
.data
.long .rodata.str1.1 + 4
-// CHECK: entry is past the end of the section
+// CHECK: merge-string-error.s.tmp.o:(.rodata.str1.1): entry is past the end of the section
diff --git a/test/ELF/merge-string.s b/test/ELF/merge-string.s
index 2ad8afa53d6e..ffcafdea28a4 100644
--- a/test/ELF/merge-string.s
+++ b/test/ELF/merge-string.s
@@ -61,8 +61,6 @@ zed:
// NOMERGE-NEXT: Type: SHT_PROGBITS
// NOMERGE-NEXT: Flags [
// NOMERGE-NEXT: SHF_ALLOC
-// NOMERGE-NEXT: SHF_MERGE
-// NOMERGE-NEXT: SHF_STRINGS
// NOMERGE-NEXT: ]
// NOMERGE-NEXT: Address: 0x1C8
// NOMERGE-NEXT: Offset: 0x1C8
diff --git a/test/ELF/merge.s b/test/ELF/merge.s
index 5039ec2251c0..b84d33a3411e 100644
--- a/test/ELF/merge.s
+++ b/test/ELF/merge.s
@@ -23,7 +23,7 @@ zed:
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_MERGE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x10120
+// CHECK-NEXT: Address: 0x200120
// CHECK-NEXT: Offset: 0x120
// CHECK-NEXT: Size: 8
// CHECK-NEXT: Link: 0
@@ -35,13 +35,13 @@ zed:
// CHECK-NEXT: )
-// Address of the constant 0x10 = 0x10120 = 65824
-// Address of the constant 0x42 = 0x10124 = 65828
+// Address of the constant 0x10 = 0x200120 = 2097440
+// Address of the constant 0x42 = 0x200124 = 2097444
// CHECK: Symbols [
// CHECK: Name: bar
-// CHECK-NEXT: Value: 0x10124
+// CHECK-NEXT: Value: 0x200124
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Loca
// CHECK-NEXT: Type: None
@@ -49,7 +49,7 @@ zed:
// CHECK-NEXT: Section: .mysec
// CHECK: Name: zed
-// CHECK-NEXT: Value: 0x10124
+// CHECK-NEXT: Value: 0x200124
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -57,7 +57,7 @@ zed:
// CHECK-NEXT: Section: .mysec
// CHECK: Name: foo
-// CHECK-NEXT: Value: 0x10124
+// CHECK-NEXT: Value: 0x200124
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -75,37 +75,37 @@ _start:
// DISASM-NEXT: _start:
movl .mysec, %eax
-// addr(0x10) = 65824
-// DISASM-NEXT: movl 65824, %eax
+// addr(0x10) = 2097440
+// DISASM-NEXT: movl 2097440, %eax
movl .mysec+7, %eax
-// addr(0x42) + 3 = 65828 + 3 = 65831
-// DISASM-NEXT: movl 65831, %eax
+// addr(0x42) + 3 = 2097444 + 3 = 2097447
+// DISASM-NEXT: movl 2097447, %eax
movl .mysec+8, %eax
-// addr(0x42) = 65828
-// DISASM-NEXT: movl 65828, %eax
+// addr(0x42) = 2097444
+// DISASM-NEXT: movl 2097444, %eax
movl bar+7, %eax
-// addr(0x42) + 7 = 65828 + 7 = 65835
-// DISASM-NEXT: movl 65835, %eax
+// addr(0x42) + 7 = 2097444 + 7 = 2097451
+// DISASM-NEXT: movl 2097451, %eax
movl bar+8, %eax
-// addr(0x42) + 8 = 65828 + 8 = 65836
-// DISASM-NEXT: movl 65836, %eax
+// addr(0x42) + 8 = 2097444 + 8 = 2097452
+// DISASM-NEXT: movl 2097452, %eax
movl foo, %eax
-// addr(0x42) = 65828
-// DISASM-NEXT: movl 65828, %eax
+// addr(0x42) = 2097444
+// DISASM-NEXT: movl 2097444, %eax
movl foo+7, %eax
-// addr(0x42) + 7 = = 65828 + 7 = 65835
-// DISASM-NEXT: movl 65835, %eax
+// addr(0x42) + 7 = = 2097444 + 7 = 2097451
+// DISASM-NEXT: movl 2097451, %eax
movl foo+8, %eax
-// addr(0x42) + 8 = = 65828 + 8 = 65836
-// DISASM-NEXT: movl 65836, %eax
+// addr(0x42) + 8 = = 2097444 + 8 = 2097452
+// DISASM-NEXT: movl 2097452, %eax
// From the other file: movl .mysec, %eax
-// addr(0x42) = 65828
-// DISASM-NEXT: movl 65828, %eax
+// addr(0x42) = 2097444
+// DISASM-NEXT: movl 2097444, %eax
diff --git a/test/ELF/mips-26-mask.s b/test/ELF/mips-26-mask.s
new file mode 100644
index 000000000000..4cf56cfe338c
--- /dev/null
+++ b/test/ELF/mips-26-mask.s
@@ -0,0 +1,16 @@
+# Check reading/writing implicit addend for R_MIPS_26 relocation.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t.exe
+# RUN: llvm-objdump -d %t.exe | FileCheck %s
+
+# REQUIRES: mips
+
+# CHECK: Disassembly of section .text:
+# CHECK: __start:
+# CHECK-NEXT: 20000: 0e 00 80 00 jal 134348800
+
+ .text
+ .global __start
+__start:
+ jal __start+0x8000000
diff --git a/test/ELF/mips-32.s b/test/ELF/mips-32.s
index 7875c48d64d7..8801bcf91f48 100644
--- a/test/ELF/mips-32.s
+++ b/test/ELF/mips-32.s
@@ -52,10 +52,11 @@ v2:
# REL-NEXT: }
# REL-NEXT: ]
-# REL: DynamicSection [
-# REL: Tag Type Name/Value
-# REL: 0x00000012 RELSZ 16 (bytes)
-# REL: 0x00000013 RELENT 8 (bytes)
+# REL: DynamicSection [
+# REL: Tag Type Name/Value
+# REL: 0x00000012 RELSZ 16 (bytes)
+# REL: 0x00000013 RELENT 8 (bytes)
+# REL-NOT: 0x6FFFFFFA RELCOUNT
# REL: Primary GOT {
# REL-NEXT: Canonical gp value:
diff --git a/test/ELF/mips-64-disp.s b/test/ELF/mips-64-disp.s
index 1c66ba4fb9a1..95568071a07f 100644
--- a/test/ELF/mips-64-disp.s
+++ b/test/ELF/mips-64-disp.s
@@ -18,7 +18,7 @@
# CHECK-NEXT: 20010: 24 42 80 38 addiu $2, $2, -32712
# CHECK: 0000000000020014 .text 00000000 foo
-# CHECK: 0000000000037ff0 .got 00000000 .hidden _gp
+# CHECK: 0000000000037ff0 *ABS* 00000000 .hidden _gp
# CHECK: 0000000000020000 .text 00000000 __start
# CHECK: 0000000000000000 g F *UND* 00000000 foo1a
diff --git a/test/ELF/mips-64-got.s b/test/ELF/mips-64-got.s
index 52ce6fb4d351..f489b4451b61 100644
--- a/test/ELF/mips-64-got.s
+++ b/test/ELF/mips-64-got.s
@@ -14,12 +14,12 @@
# CHECK-NEXT: 20000: df 82 80 20 ld $2, -32736($gp)
# CHECK-NEXT: 20004: 64 42 00 18 daddiu $2, $2, 24
-# CHECK-NEXT: 20008: 24 42 80 38 addiu $2, $2, -32712
-# CHECK-NEXT: 2000c: 24 42 80 28 addiu $2, $2, -32728
-# CHECK-NEXT: 20010: 24 42 80 30 addiu $2, $2, -32720
+# CHECK-NEXT: 20008: 24 42 80 40 addiu $2, $2, -32704
+# CHECK-NEXT: 2000c: 24 42 80 30 addiu $2, $2, -32720
+# CHECK-NEXT: 20010: 24 42 80 38 addiu $2, $2, -32712
# CHECK: 0000000000020018 .text 00000000 foo
-# CHECK: 0000000000037ff0 .got 00000000 .hidden _gp
+# CHECK: 0000000000037ff0 *ABS* 00000000 .hidden _gp
# CHECK: 0000000000020000 .text 00000000 __start
# CHECK: 0000000000020014 .text 00000000 bar
@@ -50,18 +50,23 @@
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x30018
# GOT-NEXT: Access: -32728
-# GOT-NEXT: Initial: 0x20014
+# GOT-NEXT: Initial: 0x30000
# GOT-NEXT: }
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x30020
# GOT-NEXT: Access: -32720
+# GOT-NEXT: Initial: 0x20014
+# GOT-NEXT: }
+# GOT-NEXT: Entry {
+# GOT-NEXT: Address: 0x30028
+# GOT-NEXT: Access: -32712
# GOT-NEXT: Initial: 0x20018
# GOT-NEXT: }
# GOT-NEXT: ]
# GOT-NEXT: Global entries [
# GOT-NEXT: Entry {
-# GOT-NEXT: Address: 0x30028
-# GOT-NEXT: Access: -32712
+# GOT-NEXT: Address: 0x30030
+# GOT-NEXT: Access: -32704
# GOT-NEXT: Initial: 0x0
# GOT-NEXT: Value: 0x0
# GOT-NEXT: Type: Function
diff --git a/test/ELF/mips-64-gprel-so.s b/test/ELF/mips-64-gprel-so.s
index 437238ef5f26..a390ec082b1b 100644
--- a/test/ELF/mips-64-gprel-so.s
+++ b/test/ELF/mips-64-gprel-so.s
@@ -12,7 +12,7 @@
# CHECK-NEXT: 10004: 03 99 e0 2d daddu $gp, $gp, $25
# CHECK-NEXT: 10008: 67 9c 7f f0 daddiu $gp, $gp, 32752
-# CHECK: 0000000000027ff0 .got 00000000 .hidden _gp
+# CHECK: 0000000000027ff0 *ABS* 00000000 .hidden _gp
# CHECK: 0000000000010000 .text 00000000 foo
.text
diff --git a/test/ELF/mips-64-rels.s b/test/ELF/mips-64-rels.s
index 7126afc1e595..b9a3ee738485 100644
--- a/test/ELF/mips-64-rels.s
+++ b/test/ELF/mips-64-rels.s
@@ -24,7 +24,7 @@
# ^-- 0x20004 - 0x37ff0 = 0xfffffffffffe8014
# CHECK: 0000000000020004 .text 00000000 loc
-# CHECK: 0000000000037ff0 .got 00000000 .hidden _gp
+# CHECK: 0000000000037ff0 *ABS* 00000000 .hidden _gp
# CHECK: 0000000000020000 .text 00000000 __start
# REL: Relocations [
diff --git a/test/ELF/mips-align-err.s b/test/ELF/mips-align-err.s
index 28b192ac29f3..0c71ffba8cb9 100644
--- a/test/ELF/mips-align-err.s
+++ b/test/ELF/mips-align-err.s
@@ -4,7 +4,7 @@
# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
# RUN: -mcpu=mips32r6 %S/Inputs/mips-align-err.s -o %t2.o
# RUN: not ld.lld %t.o %t2.o -o %t.exe 2>&1 | FileCheck %s
-# CHECK: improper alignment for relocation R_MIPS_PC16
+# CHECK: {{.*}}:(.text+0x1): improper alignment for relocation R_MIPS_PC16
.globl __start
__start:
diff --git a/test/ELF/mips-call-hilo.s b/test/ELF/mips-call-hilo.s
new file mode 100644
index 000000000000..2504612f9e34
--- /dev/null
+++ b/test/ELF/mips-call-hilo.s
@@ -0,0 +1,62 @@
+# Check R_MIPS_CALL_HI16 / R_MIPS_CALL_LO16 relocations calculation.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -shared -o %t.so
+# RUN: llvm-objdump -d %t.so | FileCheck %s
+# RUN: llvm-readobj -r -mips-plt-got %t.so | FileCheck -check-prefix=GOT %s
+
+# REQUIRES: mips
+
+# CHECK: Disassembly of section .text:
+# CHECK-NEXT: foo:
+# CHECK-NEXT: 10000: 3c 02 00 00 lui $2, 0
+# CHECK-NEXT: 10004: 8c 42 80 20 lw $2, -32736($2)
+# CHECK-NEXT: 10008: 3c 02 00 00 lui $2, 0
+# CHECK-NEXT: 1000c: 8c 42 80 18 lw $2, -32744($2)
+# CHECK-NEXT: 10010: 3c 02 00 00 lui $2, 0
+# CHECK-NEXT: 10014: 8c 42 80 1c lw $2, -32740($2)
+
+# GOT: Relocations [
+# GOT-NEXT: ]
+
+# GOT: Primary GOT {
+# GOT-NEXT: Canonical gp value: 0x27FF0
+# GOT: Local entries [
+# GOT-NEXT: Entry {
+# GOT-NEXT: Address: 0x20008
+# GOT-NEXT: Access: -32744
+# GOT-NEXT: Initial: 0x10018
+# GOT-NEXT: }
+# GOT-NEXT: Entry {
+# GOT-NEXT: Address: 0x2000C
+# GOT-NEXT: Access: -32740
+# GOT-NEXT: Initial: 0x1001C
+# GOT-NEXT: }
+# GOT-NEXT: ]
+# GOT-NEXT: Global entries [
+# GOT-NEXT: Entry {
+# GOT-NEXT: Address: 0x20010
+# GOT-NEXT: Access: -32736
+# GOT-NEXT: Initial: 0x0
+# GOT-NEXT: Value: 0x0
+# GOT-NEXT: Type: None
+# GOT-NEXT: Section: Undefined
+# GOT-NEXT: Name: bar
+# GOT-NEXT: }
+# GOT-NEXT: ]
+# GOT-NEXT: Number of TLS and multi-GOT entries: 0
+# GOT-NEXT: }
+
+ .text
+ .global foo
+foo:
+ lui $2, %call_hi(bar)
+ lw $2, %call_lo(bar)($2)
+ lui $2, %call_hi(loc1)
+ lw $2, %call_lo(loc1)($2)
+ lui $2, %call_hi(loc2)
+ lw $2, %call_lo(loc2)($2)
+loc1:
+ nop
+loc2:
+ nop
diff --git a/test/ELF/mips-elf-flags-err.s b/test/ELF/mips-elf-flags-err.s
new file mode 100644
index 000000000000..eab8377b17e2
--- /dev/null
+++ b/test/ELF/mips-elf-flags-err.s
@@ -0,0 +1,87 @@
+# Check MIPS ELF ISA flag calculation if input files have different ISAs.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mcpu=mips32 %S/Inputs/mips-dynamic.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mcpu=mips32r2 %s -o %t2.o
+# RUN: ld.lld %t1.o %t2.o -o %t.exe
+# RUN: llvm-readobj -h %t.exe | FileCheck -check-prefix=R1R2 %s
+
+# Check that lld does not allow to link incompatible ISAs.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mcpu=mips3 %S/Inputs/mips-dynamic.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mcpu=mips32 -mattr=+fp64 %s -o %t2.o
+# RUN: not ld.lld %t1.o %t2.o -o %t.exe 2>&1 | FileCheck -check-prefix=R3R32 %s
+
+# Check that lld does not allow to link incompatible ISAs.
+
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux \
+# RUN: -mcpu=mips64r6 %S/Inputs/mips-dynamic.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux \
+# RUN: -mcpu=octeon %s -o %t2.o
+# RUN: not ld.lld %t1.o %t2.o -o %t.exe 2>&1 \
+# RUN: | FileCheck -check-prefix=R6OCTEON %s
+
+# Check that lld does not allow to link incompatible floating point ABI.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mcpu=mips32 %S/Inputs/mips-dynamic.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mcpu=mips32 -mattr=+fp64 %s -o %t2.o
+# RUN: not ld.lld %t1.o %t2.o -o %t.exe 2>&1 | FileCheck -check-prefix=FPABI %s
+
+# Check that lld take in account EF_MIPS_MACH_XXX ISA flags
+
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux \
+# RUN: -mcpu=mips64 %S/Inputs/mips-dynamic.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux \
+# RUN: -mcpu=octeon %s -o %t2.o
+# RUN: ld.lld %t1.o %t2.o -o %t.exe
+# RUN: llvm-readobj -h %t.exe | FileCheck -check-prefix=OCTEON %s
+
+# Check that lld does not allow to link incompatible ABIs.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -target-abi n32 %S/Inputs/mips-dynamic.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -target-abi o32 %s -o %t2.o
+# RUN: not ld.lld %t1.o %t2.o -o %t.exe 2>&1 | FileCheck -check-prefix=N32O32 %s
+
+# Check that lld does not allow to link modules with incompatible NAN flags.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mattr=+nan2008 %S/Inputs/mips-dynamic.s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: %s -o %t2.o
+# RUN: not ld.lld %t1.o %t2.o -o %t.exe 2>&1 | FileCheck -check-prefix=NAN %s
+
+# REQUIRES: mips
+
+ .option pic0
+ .text
+ .global __start
+__start:
+ nop
+
+# R1R2: Flags [
+# R1R2-NEXT: EF_MIPS_ABI_O32
+# R1R2-NEXT: EF_MIPS_ARCH_32R2
+# R1R2-NEXT: EF_MIPS_CPIC
+# R1R2-NEXT: ]
+
+# R3R32: target ISA 'mips3' is incompatible with 'mips32': {{.*}}mips-elf-flags-err.s.tmp2.o
+# R6OCTEON: target ISA 'mips64r6' is incompatible with 'octeon': {{.*}}mips-elf-flags-err.s.tmp2.o
+# FPABI: target floating point ABI '-mdouble-float' is incompatible with '-mgp32 -mfp64': {{.*}}mips-elf-flags-err.s.tmp2.o
+
+# OCTEON: Flags [
+# OCTEON-NEXT: EF_MIPS_ARCH_64R2
+# OCTEON-NEXT: EF_MIPS_CPIC
+# OCTEON-NEXT: EF_MIPS_MACH_OCTEON
+# OCTEON-NEXT: EF_MIPS_PIC
+# OCTEON-NEXT: ]
+
+# N32O32: error: {{.*}}mips-elf-flags-err.s.tmp2.o is incompatible with {{.*}}mips-elf-flags-err.s.tmp1.o
+
+# NAN: target -mnan=2008 is incompatible with -mnan=legacy: {{.*}}mips-elf-flags-err.s.tmp2.o
diff --git a/test/ELF/mips-elf-flags.s b/test/ELF/mips-elf-flags.s
index 7817e58b5383..26dc42ded546 100644
--- a/test/ELF/mips-elf-flags.s
+++ b/test/ELF/mips-elf-flags.s
@@ -1,10 +1,39 @@
# Check generation of MIPS specific ELF header flags.
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: %S/Inputs/mips-dynamic.s -o %t-so.o
+# RUN: ld.lld %t-so.o -shared -o %t.so
+# RUN: llvm-readobj -h -mips-abi-flags %t.so | FileCheck -check-prefix=SO %s
+
# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
-# RUN: ld.lld %t.o -shared -o %t.so
-# RUN: llvm-readobj -h %t.so | FileCheck -check-prefix=SO %s
# RUN: ld.lld %t.o -o %t.exe
-# RUN: llvm-readobj -h %t.exe | FileCheck -check-prefix=EXE %s
+# RUN: llvm-readobj -h -mips-abi-flags %t.exe | FileCheck -check-prefix=EXE %s
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mcpu=mips32r2 %s -o %t-r2.o
+# RUN: ld.lld %t-r2.o -o %t-r2.exe
+# RUN: llvm-readobj -h -mips-abi-flags %t-r2.exe \
+# RUN: | FileCheck -check-prefix=EXE-R2 %s
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mcpu=mips32r2 %s -o %t-r2.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mcpu=mips32r5 %S/Inputs/mips-dynamic.s -o %t-r5.o
+# RUN: ld.lld %t-r2.o %t-r5.o -o %t-r5.exe
+# RUN: llvm-readobj -h -mips-abi-flags %t-r5.exe \
+# RUN: | FileCheck -check-prefix=EXE-R5 %s
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mcpu=mips32r6 %s -o %t-r6.o
+# RUN: ld.lld %t-r6.o -o %t-r6.exe
+# RUN: llvm-readobj -h -mips-abi-flags %t-r6.exe \
+# RUN: | FileCheck -check-prefix=EXE-R6 %s
+
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux \
+# RUN: -mcpu=octeon %s -o %t.o
+# RUN: ld.lld %t.o -o %t.exe
+# RUN: llvm-readobj -h -mips-abi-flags %t.exe \
+# RUN: | FileCheck -check-prefix=OCTEON %s
# REQUIRES: mips
@@ -15,13 +44,129 @@ __start:
# SO: Flags [
# SO-NEXT: EF_MIPS_ABI_O32
-# SO-NEXT: EF_MIPS_ARCH_32R2
+# SO-NEXT: EF_MIPS_ARCH_32
# SO-NEXT: EF_MIPS_CPIC
# SO-NEXT: EF_MIPS_PIC
# SO-NEXT: ]
+# SO: MIPS ABI Flags {
+# SO-NEXT: Version: 0
+# SO-NEXT: ISA: MIPS32
+# SO-NEXT: ISA Extension: None
+# SO-NEXT: ASEs [
+# SO-NEXT: ]
+# SO-NEXT: FP ABI: Hard float (double precision)
+# SO-NEXT: GPR size: 32
+# SO-NEXT: CPR1 size: 32
+# SO-NEXT: CPR2 size: 0
+# SO-NEXT: Flags 1 [
+# SO-NEXT: ODDSPREG
+# SO-NEXT: ]
+# SO-NEXT: Flags 2: 0x0
+# SO-NEXT: }
# EXE: Flags [
# EXE-NEXT: EF_MIPS_ABI_O32
-# EXE-NEXT: EF_MIPS_ARCH_32R2
+# EXE-NEXT: EF_MIPS_ARCH_32
# EXE-NEXT: EF_MIPS_CPIC
# EXE-NEXT: ]
+# EXE: MIPS ABI Flags {
+# EXE-NEXT: Version: 0
+# EXE-NEXT: ISA: MIPS32
+# EXE-NEXT: ISA Extension: None
+# EXE-NEXT: ASEs [
+# EXE-NEXT: ]
+# EXE-NEXT: FP ABI: Hard float (double precision)
+# EXE-NEXT: GPR size: 32
+# EXE-NEXT: CPR1 size: 32
+# EXE-NEXT: CPR2 size: 0
+# EXE-NEXT: Flags 1 [
+# EXE-NEXT: ODDSPREG
+# EXE-NEXT: ]
+# EXE-NEXT: Flags 2: 0x0
+# EXE-NEXT: }
+
+# EXE-R2: Flags [
+# EXE-R2-NEXT: EF_MIPS_ABI_O32
+# EXE-R2-NEXT: EF_MIPS_ARCH_32R2
+# EXE-R2-NEXT: EF_MIPS_CPIC
+# EXE-R2-NEXT: ]
+# EXE-R2: MIPS ABI Flags {
+# EXE-R2-NEXT: Version: 0
+# EXE-R2-NEXT: ISA: MIPS32r2
+# EXE-R2-NEXT: ISA Extension: None
+# EXE-R2-NEXT: ASEs [
+# EXE-R2-NEXT: ]
+# EXE-R2-NEXT: FP ABI: Hard float (double precision)
+# EXE-R2-NEXT: GPR size: 32
+# EXE-R2-NEXT: CPR1 size: 32
+# EXE-R2-NEXT: CPR2 size: 0
+# EXE-R2-NEXT: Flags 1 [
+# EXE-R2-NEXT: ODDSPREG
+# EXE-R2-NEXT: ]
+# EXE-R2-NEXT: Flags 2: 0x0
+# EXE-R2-NEXT: }
+
+# EXE-R5: Flags [
+# EXE-R5-NEXT: EF_MIPS_ABI_O32
+# EXE-R5-NEXT: EF_MIPS_ARCH_32R2
+# EXE-R5-NEXT: EF_MIPS_CPIC
+# EXE-R5-NEXT: ]
+# EXE-R5: MIPS ABI Flags {
+# EXE-R5-NEXT: Version: 0
+# EXE-R5-NEXT: ISA: MIPS32r5
+# EXE-R5-NEXT: ISA Extension: None
+# EXE-R5-NEXT: ASEs [
+# EXE-R5-NEXT: ]
+# EXE-R5-NEXT: FP ABI: Hard float (double precision)
+# EXE-R5-NEXT: GPR size: 32
+# EXE-R5-NEXT: CPR1 size: 32
+# EXE-R5-NEXT: CPR2 size: 0
+# EXE-R5-NEXT: Flags 1 [
+# EXE-R5-NEXT: ODDSPREG
+# EXE-R5-NEXT: ]
+# EXE-R5-NEXT: Flags 2: 0x0
+# EXE-R5-NEXT: }
+
+# EXE-R6: Flags [
+# EXE-R6-NEXT: EF_MIPS_ABI_O32
+# EXE-R6-NEXT: EF_MIPS_ARCH_32R6
+# EXE-R6-NEXT: EF_MIPS_CPIC
+# EXE-R6-NEXT: EF_MIPS_NAN2008
+# EXE-R6-NEXT: ]
+# EXE-R6: MIPS ABI Flags {
+# EXE-R6-NEXT: Version: 0
+# EXE-R6-NEXT: ISA: MIPS32
+# EXE-R6-NEXT: ISA Extension: None
+# EXE-R6-NEXT: ASEs [
+# EXE-R6-NEXT: ]
+# EXE-R6-NEXT: FP ABI: Hard float (32-bit CPU, 64-bit FPU)
+# EXE-R6-NEXT: GPR size: 32
+# EXE-R6-NEXT: CPR1 size: 64
+# EXE-R6-NEXT: CPR2 size: 0
+# EXE-R6-NEXT: Flags 1 [
+# EXE-R6-NEXT: ODDSPREG
+# EXE-R6-NEXT: ]
+# EXE-R6-NEXT: Flags 2: 0x0
+# EXE-R6-NEXT: }
+
+# OCTEON: Flags [
+# OCTEON-NEXT: EF_MIPS_ARCH_64R2
+# OCTEON-NEXT: EF_MIPS_CPIC
+# OCTEON-NEXT: EF_MIPS_MACH_OCTEON
+# OCTEON-NEXT: EF_MIPS_PIC
+# OCTEON-NEXT: ]
+# OCTEON: MIPS ABI Flags {
+# OCTEON-NEXT: Version: 0
+# OCTEON-NEXT: ISA: MIPS64r2
+# OCTEON-NEXT: ISA Extension: Cavium Networks Octeon
+# OCTEON-NEXT: ASEs [
+# OCTEON-NEXT: ]
+# OCTEON-NEXT: FP ABI: Hard float (double precision)
+# OCTEON-NEXT: GPR size: 64
+# OCTEON-NEXT: CPR1 size: 64
+# OCTEON-NEXT: CPR2 size: 0
+# OCTEON-NEXT: Flags 1 [
+# OCTEON-NEXT: ODDSPREG
+# OCTEON-NEXT: ]
+# OCTEON-NEXT: Flags 2: 0x0
+# OCTEON-NEXT: }
diff --git a/test/ELF/mips-got-and-copy.s b/test/ELF/mips-got-and-copy.s
index 453b8c957b56..f42b39c6d51d 100644
--- a/test/ELF/mips-got-and-copy.s
+++ b/test/ELF/mips-got-and-copy.s
@@ -37,7 +37,7 @@
# CHECK-NEXT: Value: 0x40014
# CHECK-NEXT: Type: Object (0x1)
# CHECK-NEXT: Section: .bss (0xD)
-# CHECK-NEXT: Name: data1@ (7)
+# CHECK-NEXT: Name: data1@
# CHECK-NEXT: }
# CHECK-NEXT: ]
# CHECK-NEXT: Number of TLS and multi-GOT entries: 0
diff --git a/test/ELF/mips-got-hilo.s b/test/ELF/mips-got-hilo.s
new file mode 100644
index 000000000000..7e6945fd2b62
--- /dev/null
+++ b/test/ELF/mips-got-hilo.s
@@ -0,0 +1,64 @@
+# Check R_MIPS_GOT_HI16 / R_MIPS_GOT_LO16 relocations calculation.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -shared -o %t.so
+# RUN: llvm-objdump -d %t.so | FileCheck %s
+# RUN: llvm-readobj -r -mips-plt-got %t.so | FileCheck -check-prefix=GOT %s
+
+# REQUIRES: mips
+
+# CHECK: Disassembly of section .text:
+# CHECK-NEXT: foo:
+# CHECK-NEXT: 10000: 3c 02 00 00 lui $2, 0
+# CHECK-NEXT: 10004: 8c 42 80 20 lw $2, -32736($2)
+# CHECK-NEXT: 10008: 3c 02 00 00 lui $2, 0
+# CHECK-NEXT: 1000c: 8c 42 80 18 lw $2, -32744($2)
+# CHECK-NEXT: 10010: 3c 02 00 00 lui $2, 0
+# CHECK-NEXT: 10014: 8c 42 80 1c lw $2, -32740($2)
+
+# GOT: Relocations [
+# GOT-NEXT: ]
+
+# GOT: Primary GOT {
+# GOT-NEXT: Canonical gp value: 0x27FF0
+# GOT: Local entries [
+# GOT-NEXT: Entry {
+# GOT-NEXT: Address: 0x20008
+# GOT-NEXT: Access: -32744
+# GOT-NEXT: Initial: 0x30000
+# GOT-NEXT: }
+# GOT-NEXT: Entry {
+# GOT-NEXT: Address: 0x2000C
+# GOT-NEXT: Access: -32740
+# GOT-NEXT: Initial: 0x30004
+# GOT-NEXT: }
+# GOT-NEXT: ]
+# GOT-NEXT: Global entries [
+# GOT-NEXT: Entry {
+# GOT-NEXT: Address: 0x20010
+# GOT-NEXT: Access: -32736
+# GOT-NEXT: Initial: 0x0
+# GOT-NEXT: Value: 0x0
+# GOT-NEXT: Type: None
+# GOT-NEXT: Section: Undefined
+# GOT-NEXT: Name: bar
+# GOT-NEXT: }
+# GOT-NEXT: ]
+# GOT-NEXT: Number of TLS and multi-GOT entries: 0
+# GOT-NEXT: }
+
+ .text
+ .global foo
+foo:
+ lui $2, %got_hi(bar)
+ lw $2, %got_lo(bar)($2)
+ lui $2, %got_hi(loc1)
+ lw $2, %got_lo(loc1)($2)
+ lui $2, %got_hi(loc2)
+ lw $2, %got_lo(loc2)($2)
+
+ .data
+loc1:
+ .word 0
+loc2:
+ .word 0
diff --git a/test/ELF/mips-got-page.s b/test/ELF/mips-got-page.s
new file mode 100644
index 000000000000..e2dc485ba661
--- /dev/null
+++ b/test/ELF/mips-got-page.s
@@ -0,0 +1,40 @@
+# Check the case when small section (less that 0x10000 bytes) occupies
+# two adjacent 0xffff-bytes pages. We need to create two GOT entries
+# for R_MIPS_GOT_PAGE relocations.
+
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux -o %t.o %s
+# RUN: ld.lld --section-start .rodata=0x27FFC -shared -o %t.so %t.o
+# RUN: llvm-readobj -t -mips-plt-got %t.so | FileCheck %s
+
+# REQUIRES: mips
+
+# CHECK: Name: bar
+# CHECK-NEXT: Value: 0x28000
+# ^ page-address = (0x28000 + 0x8000) & ~0xffff = 0x30000
+
+# CHECK: Name: foo
+# CHECK-NEXT: Value: 0x27FFC
+# ^ page-address = (0x27ffc + 0x8000) & ~0xffff = 0x20000
+
+# CHECK: Local entries [
+# CHECK-NEXT: Entry {
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Access: -32736
+# CHECK-NEXT: Initial: 0x20000
+# CHECK-NEXT: }
+# CHECK-NEXT: Entry {
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Access: -32728
+# CHECK-NEXT: Initial: 0x30000
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+ .text
+ ld $v0,%got_page(foo)($gp)
+ ld $v0,%got_page(bar)($gp)
+
+ .rodata
+foo:
+ .word 0
+bar:
+ .word 0
diff --git a/test/ELF/mips-got-redundant.s b/test/ELF/mips-got-redundant.s
index 07c3c249f4fa..115a49f357dd 100644
--- a/test/ELF/mips-got-redundant.s
+++ b/test/ELF/mips-got-redundant.s
@@ -22,6 +22,12 @@
# CHECK-NEXT: Entry {
# CHECK-NEXT: Address: 0x20010
# CHECK-NEXT: Access: -32736
+# CHECK-NEXT: Initial: 0x50000
+# ^-- redundant
+# CHECK-NEXT: }
+# CHECK-NEXT: Entry {
+# CHECK-NEXT: Address: 0x20014
+# CHECK-NEXT: Access: -32732
# CHECK-NEXT: Initial: 0x40008
# ^-- glb1
# CHECK-NEXT: }
diff --git a/test/ELF/mips-got-relocs.s b/test/ELF/mips-got-relocs.s
index c44cf90b7be3..19c0db49a440 100644
--- a/test/ELF/mips-got-relocs.s
+++ b/test/ELF/mips-got-relocs.s
@@ -47,7 +47,7 @@ v1:
# EXE_SYM: Sections:
# EXE_SYM: .got 0000000c 0000000000030000 DATA
# EXE_SYM: SYMBOL TABLE:
-# EXE_SYM: 00037ff0 .got 00000000 .hidden _gp
+# EXE_SYM: 00037ff0 *ABS* 00000000 .hidden _gp
# ^-- .got + GP offset (0x7ff0)
# EXE_SYM: 00040000 g .data 00000004 v1
@@ -71,7 +71,7 @@ v1:
# DSO_SYM: Sections:
# DSO_SYM: .got 0000000c 0000000000020000 DATA
# DSO_SYM: SYMBOL TABLE:
-# DSO_SYM: 00027ff0 .got 00000000 .hidden _gp
+# DSO_SYM: 00027ff0 *ABS* 00000000 .hidden _gp
# ^-- .got + GP offset (0x7ff0)
# DSO_SYM: 00030000 g .data 00000004 v1
diff --git a/test/ELF/mips-got16.s b/test/ELF/mips-got16.s
index ef80418ef032..c4c14e46607c 100644
--- a/test/ELF/mips-got16.s
+++ b/test/ELF/mips-got16.s
@@ -11,15 +11,15 @@
# CHECK-NEXT: __start:
# CHECK-NEXT: 10000: 8f 88 80 18 lw $8, -32744($gp)
# CHECK-NEXT: 10004: 21 08 00 2c addi $8, $8, 44
-# CHECK-NEXT: 10008: 8f 88 80 1c lw $8, -32740($gp)
+# CHECK-NEXT: 10008: 8f 88 80 24 lw $8, -32732($gp)
# CHECK-NEXT: 1000c: 21 08 90 00 addi $8, $8, -28672
-# CHECK-NEXT: 10010: 8f 88 80 20 lw $8, -32736($gp)
+# CHECK-NEXT: 10010: 8f 88 80 28 lw $8, -32728($gp)
# CHECK-NEXT: 10014: 21 08 90 04 addi $8, $8, -28668
-# CHECK-NEXT: 10018: 8f 88 80 20 lw $8, -32736($gp)
+# CHECK-NEXT: 10018: 8f 88 80 28 lw $8, -32728($gp)
# CHECK-NEXT: 1001c: 21 08 10 04 addi $8, $8, 4100
-# CHECK-NEXT: 10020: 8f 88 80 28 lw $8, -32728($gp)
+# CHECK-NEXT: 10020: 8f 88 80 30 lw $8, -32720($gp)
# CHECK-NEXT: 10024: 21 08 10 08 addi $8, $8, 4104
-# CHECK-NEXT: 10028: 8f 88 80 2c lw $8, -32724($gp)
+# CHECK-NEXT: 10028: 8f 88 80 34 lw $8, -32716($gp)
#
# CHECK: SYMBOL TABLE:
# CHECK: 00051008 .data 00000000 .hidden bar
@@ -54,33 +54,45 @@
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x2000C
# GOT-NEXT: Access: -32740
-# GOT-NEXT: Initial: 0x40000
-# ^-- (0x39000 + 0x8000) & ~0xffff
+# GOT-NEXT: Initial: 0x20000
+# ^-- redundant unused entry
# GOT-NEXT: }
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x20010
# GOT-NEXT: Access: -32736
+# GOT-NEXT: Initial: 0x30000
+# ^-- redundant unused entry
+# GOT-NEXT: }
+# GOT-NEXT: Entry {
+# GOT-NEXT: Address: 0x20014
+# GOT-NEXT: Access: -32732
+# GOT-NEXT: Initial: 0x40000
+# ^-- (0x39000 + 0x8000) & ~0xffff
+# GOT-NEXT: }
+# GOT-NEXT: Entry {
+# GOT-NEXT: Address: 0x20018
+# GOT-NEXT: Access: -32728
# GOT-NEXT: Initial: 0x50000
# ^-- (0x39000 + 0x10004 + 0x8000) & ~0xffff
# ^-- (0x39000 + 0x18004 + 0x8000) & ~0xffff
# GOT-NEXT: }
# GOT-NEXT: Entry {
-# GOT-NEXT: Address: 0x20014
-# GOT-NEXT: Access: -32732
-# GOT-NEXT: Initial: 0x0
+# GOT-NEXT: Address: 0x2001C
+# GOT-NEXT: Access: -32724
+# GOT-NEXT: Initial: 0x60000
# ^-- redundant unused entry
# GOT-NEXT: }
# GOT-NEXT: Entry {
-# GOT-NEXT: Address: 0x20018
-# GOT-NEXT: Access: -327
+# GOT-NEXT: Address: 0x20020
+# GOT-NEXT: Access: -32720
# GOT-NEXT: Initial: 0x51008
# ^-- 'bar' address
# GOT-NEXT: }
# GOT-NEXT: ]
# GOT-NEXT: Global entries [
# GOT-NEXT: Entry {
-# GOT-NEXT: Address: 0x2001C
-# GOT-NEXT: Access: -32724
+# GOT-NEXT: Address: 0x20024
+# GOT-NEXT: Access: -32716
# GOT-NEXT: Initial: 0x0
# GOT-NEXT: Value: 0x0
# GOT-NEXT: Type: None
diff --git a/test/ELF/mips-gp-disp.s b/test/ELF/mips-gp-disp.s
index 7a0fd6409d18..62a2b1084530 100644
--- a/test/ELF/mips-gp-disp.s
+++ b/test/ELF/mips-gp-disp.s
@@ -24,7 +24,7 @@
# DIS-NEXT: 10000: 3c 08 00 01 lui $8, 1
# DIS-NEXT: 10004: 21 08 7f f0 addi $8, $8, 32752
# ^-- 0x37ff0 & 0xffff
-# DIS: 00027ff0 .got 00000000 .hidden _gp
+# DIS: 00027ff0 *ABS* 00000000 .hidden _gp
# REL: Relocations [
# REL-NEXT: ]
diff --git a/test/ELF/mips-gp-ext.s b/test/ELF/mips-gp-ext.s
new file mode 100644
index 000000000000..b6c49e0fd422
--- /dev/null
+++ b/test/ELF/mips-gp-ext.s
@@ -0,0 +1,73 @@
+# Check that the linker use a value of _gp symbol defined
+# in a linker script to calculate GOT relocations.
+
+# FIXME: This test is xfailed because it depends on D27276 patch
+# that enables absolute symbols redefinition by a linker's script.
+# XFAIL: *
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
+
+# RUN: echo "SECTIONS { \
+# RUN: .text : { *(.text) } \
+# RUN: _gp = . + 0x100; \
+# RUN: .got : { *(.got) } }" > %t.rel.script
+# RUN: ld.lld -shared -o %t.rel.so --script %t.rel.script %t.o
+# RUN: llvm-objdump -s -t %t.rel.so | FileCheck --check-prefix=REL %s
+
+# RUN: echo "SECTIONS { \
+# RUN: .text : { *(.text) } \
+# RUN: _gp = 0x200; \
+# RUN: .got : { *(.got) } }" > %t.abs.script
+# RUN: ld.lld -shared -o %t.abs.so --script %t.abs.script %t.o
+# RUN: llvm-objdump -s -t %t.abs.so | FileCheck --check-prefix=ABS %s
+
+# REQUIRES: mips
+
+# REL: Contents of section .text:
+# REL-NEXT: 0000 3c080000 2108010c 8f82ffe4
+# ^-- %hi(_gp_disp)
+# ^-- %lo(_gp_disp)
+# ^-- 8 - (0x10c - 0xe8)
+# G - (GP - .got)
+
+# REL: Contents of section .reginfo:
+# REL-NEXT: 0028 10000104 00000000 00000000 00000000
+# REL-NEXT: 0038 00000000 0000010c
+# ^-- _gp
+
+# REL: Contents of section .data:
+# REL-NEXT: 0100 fffffef4
+# ^-- 0-0x10c
+
+# REL: 00000000 .text 00000000 foo
+# REL: 0000010c *ABS* 00000000 .hidden _gp_disp
+# REL: 0000010c *ABS* 00000000 .hidden _gp
+
+# ABS: Contents of section .text:
+# ABS-NEXT: 0000 3c080000 21080200 8f82fef0
+# ^-- %hi(_gp_disp)
+# ^-- %lo(_gp_disp)
+# ^-- 8 - (0x200 - 0xe8)
+# G - (GP - .got)
+
+# ABS: Contents of section .reginfo:
+# ABS-NEXT: 0028 10000104 00000000 00000000 00000000
+# ABS-NEXT: 0038 00000000 00000200
+# ^-- _gp
+
+# ABS: Contents of section .data:
+# ABS-NEXT: 0100 fffffe00
+# ^-- 0-0x200
+
+# ABS: 00000000 .text 00000000 foo
+# ABS: 00000200 *ABS* 00000000 .hidden _gp_disp
+# ABS: 00000200 *ABS* 00000000 .hidden _gp
+
+ .text
+foo:
+ lui $t0, %hi(_gp_disp)
+ addi $t0, $t0, %lo(_gp_disp)
+ lw $v0, %call16(bar)($gp)
+
+ .data
+ .gpword foo
diff --git a/test/ELF/mips-gp-local.s b/test/ELF/mips-gp-local.s
index 8bb3c236edf0..b77dbb8367ae 100644
--- a/test/ELF/mips-gp-local.s
+++ b/test/ELF/mips-gp-local.s
@@ -11,7 +11,7 @@
# CHECK-NEXT: 20000: 3c 08 00 03 lui $8, 3
# CHECK-NEXT: 20004: 21 08 7f f0 addi $8, $8, 32752
-# CHECK: 00037ff0 .got 00000000 .hidden _gp
+# CHECK: 00037ff0 *ABS* 00000000 .hidden _gp
.text
.globl __start
diff --git a/test/ELF/mips-gp-lowest.s b/test/ELF/mips-gp-lowest.s
new file mode 100644
index 000000000000..b3d30aa35903
--- /dev/null
+++ b/test/ELF/mips-gp-lowest.s
@@ -0,0 +1,44 @@
+# Check that default _gp value is calculated relative
+# to the GP-relative section with the lowest address.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS { \
+# RUN: .sdata : { *(.sdata) } \
+# RUN: .got : { *(.got) } }" > %t.rel.script
+# RUN: ld.lld %t.o --script %t.rel.script -shared -o %t.so
+# RUN: llvm-readobj -s -t %t.so | FileCheck %s
+
+# REQUIRES: mips
+
+ .text
+ .global foo
+foo:
+ lui $gp, %call16(foo)
+
+ .sdata
+ .word 0
+
+# CHECK: Section {
+# CHECK: Name: .sdata
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_MIPS_GPREL
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0xDD
+# CHECK: }
+# CHECK: Section {
+# CHECK: Name: .got
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_MIPS_GPREL
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0xE4
+# CHECK: }
+
+# CHECK: Name: _gp (5)
+# CHECK-NEXT: Value: 0x80CD
+# ^-- 0xDD + 0x7ff0
diff --git a/test/ELF/mips-gprel32-relocs-gp0.s b/test/ELF/mips-gprel32-relocs-gp0.s
new file mode 100644
index 000000000000..e71f8856afee
--- /dev/null
+++ b/test/ELF/mips-gprel32-relocs-gp0.s
@@ -0,0 +1,48 @@
+# Check that relocatable object produced by LLD has zero gp0 value.
+# Also check an error message if input object file has non-zero gp0 value
+# and the linker generates a relocatable object.
+# mips-gp0-non-zero.o is a relocatable object produced from the asm code
+# below and linked by GNU bfd linker.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
+# RUN: ld.lld -r -o %t-rel.o %t.o
+# RUN: llvm-readobj -mips-reginfo %t-rel.o | FileCheck --check-prefix=REL %s
+
+# RUN: ld.lld -shared -o %t.so %S/Inputs/mips-gp0-non-zero.o
+# RUN: llvm-readobj -mips-reginfo %t.so | FileCheck --check-prefix=DSO %s
+# RUN: llvm-objdump -s -t %t.so | FileCheck --check-prefix=DUMP %s
+
+# RUN: not ld.lld -r -o %t-rel.o %S/Inputs/mips-gp0-non-zero.o 2>&1 \
+# RUN: | FileCheck --check-prefix=ERR %s
+
+# REQUIRES: mips
+
+# REL: GP: 0x0
+
+# DSO: GP: 0x27FF0
+
+# DUMP: Contents of section .rodata:
+# DUMP: 0114 ffff0004 ffff0008
+# ^ 0x10004 + 0x7ff0 - 0x27ff0
+# ^ 0x10008 + 0x7ff0 - 0x27ff0
+
+# DUMP: SYMBOL TABLE:
+# DUMP: 00010008 .text 00000000 bar
+# DUMP: 00010004 .text 00000000 foo
+# DUMP: 00027ff0 *ABS* 00000000 .hidden _gp
+
+# ERR: error: {{.*}}mips-gp0-non-zero.o: unsupported non-zero ri_gp_value
+
+ .text
+ .global __start
+__start:
+ lw $t0,%call16(__start)($gp)
+foo:
+ nop
+bar:
+ nop
+
+ .section .rodata, "a"
+v:
+ .gpword foo
+ .gpword bar
diff --git a/test/ELF/mips-gprel32-relocs-gp0.test b/test/ELF/mips-gprel32-relocs-gp0.test
deleted file mode 100644
index 6cc837ef7889..000000000000
--- a/test/ELF/mips-gprel32-relocs-gp0.test
+++ /dev/null
@@ -1,31 +0,0 @@
-# Check R_MIPS_GPREL32 relocation calculation if input file conatins
-# non-zero GP0 value in the .reginfo section.
-# FIXME: The only way to get an object file with non-zero GP0 value
-# is to link multiple object files with GOT relocations using '-r'
-# option. LLD does not calculate and generate GP0 correctly so we
-# use a binary input in this test. The input object file is a result
-# of linking two object files with R_MIPS_GPREL32 relocations.
-
-# RUN: ld.lld -shared -o %t.so %S/Inputs/mips-gprel32-gp0.o
-# RUN: llvm-objdump -s %S/Inputs/mips-gprel32-gp0.o \
-# RUN: | FileCheck -check-prefix=OBJ %s
-# RUN: llvm-objdump -s -t %t.so | FileCheck %s
-
-# OBJ: Contents of section .reginfo:
-# OBJ-NEXT: 0000 00000001 00000000 00000000 00000000
-# OBJ-NEXT: 0010 00000000 00007fef
-# ^-- GP0 value
-# OBJ: Contents of section .rodata:
-# OBJ-NEXT: 0000 ffff8011 ffff8011
-# ^-- foo addend
-# ^-- bar addend
-
-# CHECK: Contents of section .rodata:
-# CHECK: 012c fffe8010 fffe8020
-# ^ 0x10000 + 0xffff8011 + 0x7fef - 0x27ff0
-# ^ 0x10010 + 0xffff8011 + 0x7fef - 0x27ff0
-
-# CHECK: SYMBOL TABLE:
-# CHECK: 00010000 .text 00000000 foo
-# CHECK: 00010010 .text 00000000 bar
-# CHECK: 00027ff0 .got 00000000 .hidden _gp
diff --git a/test/ELF/mips-gprel32-relocs.s b/test/ELF/mips-gprel32-relocs.s
index fa1c5cfe9956..993596deedb7 100644
--- a/test/ELF/mips-gprel32-relocs.s
+++ b/test/ELF/mips-gprel32-relocs.s
@@ -28,4 +28,4 @@ v1:
# CHECK: SYMBOL TABLE:
# CHECK: 00010008 .text 00000000 bar
# CHECK: 00010004 .text 00000000 foo
-# CHECK: 00027ff0 .got 00000000 .hidden _gp
+# CHECK: 00027ff0 *ABS* 00000000 .hidden _gp
diff --git a/test/ELF/mips-higher-highest.s b/test/ELF/mips-higher-highest.s
new file mode 100644
index 000000000000..123b51a65add
--- /dev/null
+++ b/test/ELF/mips-higher-highest.s
@@ -0,0 +1,21 @@
+# Check R_MIPS_HIGHER / R_MIPS_HIGHEST relocations calculation.
+
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux \
+# RUN: %S/Inputs/mips-dynamic.s -o %t2.o
+# RUN: ld.lld %t1.o %t2.o -o %t.exe
+# RUN: llvm-objdump -d %t.exe | FileCheck %s
+
+# REQUIRES: mips
+
+ .global __start
+__start:
+ lui $6, %highest(_foo+0x300047FFF7FF7)
+ daddiu $6, $6, %higher(_foo+0x300047FFF7FF7)
+ lui $7, %highest(_foo+0x300047FFF7FF8)
+ ld $7, %higher (_foo+0x300047FFF7FF8)($7)
+
+# CHECK: 20000: 3c 06 00 03 lui $6, 3
+# CHECK-NEXT: 20004: 64 c6 00 05 daddiu $6, $6, 5
+# CHECK-NEXT: 20008: 3c 07 00 03 lui $7, 3
+# CHECK-NEXT: 2000c: dc e7 00 05 ld $7, 5($7)
diff --git a/test/ELF/mips-hilo-gp-disp.s b/test/ELF/mips-hilo-gp-disp.s
index 37cf90d9728b..16eab06e7b88 100644
--- a/test/ELF/mips-hilo-gp-disp.s
+++ b/test/ELF/mips-hilo-gp-disp.s
@@ -34,7 +34,7 @@ bar:
# EXE: SYMBOL TABLE:
# EXE: 0002000c .text 00000000 bar
-# EXE: 00037ff0 .got 00000000 .hidden _gp
+# EXE: 00037ff0 *ABS* 00000000 .hidden _gp
# EXE: 00020000 .text 00000000 __start
# SO: Disassembly of section .text:
@@ -51,5 +51,5 @@ bar:
# SO: SYMBOL TABLE:
# SO: 0001000c .text 00000000 bar
-# SO: 00027ff0 .got 00000000 .hidden _gp
+# SO: 00027ff0 *ABS* 00000000 .hidden _gp
# SO: 00010000 .text 00000000 __start
diff --git a/test/ELF/mips-merge-abiflags.s b/test/ELF/mips-merge-abiflags.s
new file mode 100644
index 000000000000..2e8b43bcc3b8
--- /dev/null
+++ b/test/ELF/mips-merge-abiflags.s
@@ -0,0 +1,63 @@
+# Test that lld handles input files with concatenated .MIPS.abiflags sections
+# This happens e.g. with the FreeBSD BFD (BFD 2.17.50 [FreeBSD] 2007-07-03)
+
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-freebsd %s -o %t.o
+# RUN: ld.lld %t.o %p/Inputs/mips-concatenated-abiflags.o -o %t.exe
+# RUN: llvm-readobj -sections -mips-abi-flags %t.exe | FileCheck %s
+# RUN: llvm-readobj -sections -mips-abi-flags \
+# RUN: %p/Inputs/mips-concatenated-abiflags.o | \
+# RUN: FileCheck --check-prefix=INPUT-OBJECT %s
+
+# REQUIRES: mips
+ .globl __start
+__start:
+ nop
+
+# CHECK: Section {
+# CHECK: Index: 1
+# CHECK-NEXT: Name: .MIPS.abiflags
+# CHECK-NEXT: Type: SHT_MIPS_ABIFLAGS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 24
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 8
+# CHECK-NEXT: EntrySize: 24
+# CHECK-NEXT: }
+
+# CHECK: MIPS ABI Flags {
+# CHECK-NEXT: Version: 0
+# CHECK-NEXT: ISA: MIPS64
+# CHECK-NEXT: ISA Extension: None
+# CHECK-NEXT: ASEs [
+# CHECK-NEXT: ]
+# CHECK-NEXT: FP ABI: Hard float (double precision)
+# CHECK-NEXT: GPR size: 64
+# CHECK-NEXT: CPR1 size: 64
+# CHECK-NEXT: CPR2 size: 0
+# CHECK-NEXT: Flags 1 [
+# CHECK-NEXT: ODDSPREG
+# CHECK-NEXT: ]
+# CHECK-NEXT: Flags 2: 0x0
+# CHECK-NEXT: }
+
+# INPUT-OBJECT: Section {
+# INPUT-OBJECT: Index: 3
+# INPUT-OBJECT-NEXT: Name: .MIPS.abiflags
+# INPUT-OBJECT-NEXT: Type: SHT_MIPS_ABIFLAGS
+# INPUT-OBJECT-NEXT: Flags [
+# INPUT-OBJECT-NEXT: SHF_ALLOC
+# INPUT-OBJECT-NEXT: ]
+# INPUT-OBJECT-NEXT: Address:
+# INPUT-OBJECT-NEXT: Offset:
+# INPUT-OBJECT-NEXT: Size: 48
+# INPUT-OBJECT-NEXT: Link: 0
+# INPUT-OBJECT-NEXT: Info: 0
+# INPUT-OBJECT-NEXT: AddressAlignment: 8
+# INPUT-OBJECT-NEXT: EntrySize: 0
+# INPUT-OBJECT-NEXT: }
+# INPUT-OBJECT: The .MIPS.abiflags section has a wrong size.
diff --git a/test/ELF/mips-n32-emul.s b/test/ELF/mips-n32-emul.s
new file mode 100644
index 000000000000..d0d81cc1c95f
--- /dev/null
+++ b/test/ELF/mips-n32-emul.s
@@ -0,0 +1,14 @@
+# Check that LLD shows an error when N32 ABI emulation argument
+# is combined with non-N32 ABI object files.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
+# RUN: not ld.lld -m elf32btsmipn32 %t.o -o %t.exe 2>&1 | FileCheck %s
+
+# REQUIRES: mips
+
+ .text
+ .global __start
+__start:
+ nop
+
+# CHECK: error: {{.*}}mips-n32-emul.s.tmp.o is incompatible with elf32btsmipn32
diff --git a/test/ELF/mips-n32-rels.s b/test/ELF/mips-n32-rels.s
new file mode 100644
index 000000000000..4cf72887785e
--- /dev/null
+++ b/test/ELF/mips-n32-rels.s
@@ -0,0 +1,71 @@
+# Check handling of N32 ABI relocation records.
+
+# For now llvm-mc generates incorrect object files for N32 ABI.
+# We use the binary input file generated by GNU tool.
+# llvm-mc -filetype=obj -triple=mips64-unknown-linux \
+# -target-abi n32 %s -o %t.o
+# RUN: ld.lld %S/Inputs/mips-n32-rels.o -o %t.exe
+# RUN: llvm-objdump -t -d -s %t.exe | FileCheck %s
+# RUN: llvm-readobj -h %t.exe | FileCheck -check-prefix=ELF %s
+
+# REQUIRES: mips
+
+# .text
+# .type __start, @function
+# .global __start
+# __start:
+# lui $gp,%hi(%neg(%gp_rel(__start))) # R_MIPS_GPREL16
+# # R_MIPS_SUB
+# # R_MIPS_HI16
+# loc:
+# daddiu $gp,$gp,%lo(%neg(%gp_rel(__start))) # R_MIPS_GPREL16
+# # R_MIPS_SUB
+# # R_MIPS_LO16
+#
+# .section .rodata,"a",@progbits
+# .gpword(loc) # R_MIPS_32
+
+# CHECK: Disassembly of section .text:
+# CHECK-NEXT: __start:
+# CHECK-NEXT: 20000: 3c 1c 00 01 lui $gp, 1
+# ^-- 0x20000 - 0x37ff0
+# ^-- 0 - 0xfffe8010
+# ^-- %hi(0x17ff0)
+# CHECK: loc:
+# CHECK-NEXT: 20004: 67 9c 7f f0 daddiu $gp, $gp, 32752
+# ^-- 0x20000 - 0x37ff0
+# ^-- 0 - 0xfffe8010
+# ^-- %lo(0x17ff0)
+
+# CHECK: Contents of section .rodata:
+# CHECK-NEXT: 100f4 00020004
+# ^-- loc
+
+# CHECK: 00020004 .text 00000000 loc
+# CHECK: 00037ff0 *ABS* 00000000 .hidden _gp
+# CHECK: 00020000 g F .text 00000000 __start
+
+# ELF: Format: ELF32-mips
+# ELF-NEXT: Arch: mips
+# ELF-NEXT: AddressSize: 32bit
+# ELF-NEXT: LoadName:
+# ELF-NEXT: ElfHeader {
+# ELF-NEXT: Ident {
+# ELF-NEXT: Magic: (7F 45 4C 46)
+# ELF-NEXT: Class: 32-bit (0x1)
+# ELF-NEXT: DataEncoding: BigEndian (0x2)
+# ELF-NEXT: FileVersion: 1
+# ELF-NEXT: OS/ABI: SystemV (0x0)
+# ELF-NEXT: ABIVersion: 0
+# ELF-NEXT: Unused: (00 00 00 00 00 00 00)
+# ELF-NEXT: }
+# ELF-NEXT: Type: Executable (0x2)
+# ELF-NEXT: Machine: EM_MIPS (0x8)
+# ELF-NEXT: Version: 1
+# ELF-NEXT: Entry: 0x20000
+# ELF-NEXT: ProgramHeaderOffset:
+# ELF-NEXT: SectionHeaderOffset:
+# ELF-NEXT: Flags [
+# ELF-NEXT: EF_MIPS_ABI2
+# ELF-NEXT: EF_MIPS_ARCH_64R2
+# ELF-NEXT: ]
diff --git a/test/ELF/mips-no-objects.s b/test/ELF/mips-no-objects.s
new file mode 100644
index 000000000000..444477ea7206
--- /dev/null
+++ b/test/ELF/mips-no-objects.s
@@ -0,0 +1,5 @@
+# REQUIRES: mips
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -shared -o %t.so
+# RUN: ld.lld %t.so -shared -o %t2.so
+# RUN: llvm-readobj %t2.so > /dev/null 2>&1
diff --git a/test/ELF/mips-npic-call-pic.s b/test/ELF/mips-npic-call-pic.s
index dbf053570bd9..76c392e1f66c 100644
--- a/test/ELF/mips-npic-call-pic.s
+++ b/test/ELF/mips-npic-call-pic.s
@@ -3,9 +3,14 @@
# non-PIC code calls PIC function.
# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: %p/Inputs/mips-fpic.s -o %t-fpic.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: %p/Inputs/mips-fnpic.s -o %t-fnpic.o
+# RUN: ld.lld -r %t-fpic.o %t-fnpic.o -o %t-sto-pic.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
# RUN: %p/Inputs/mips-pic.s -o %t-pic.o
# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t-npic.o
-# RUN: ld.lld %t-npic.o %t-pic.o %p/Inputs/mips-sto-pic.o -o %t.exe
+# RUN: ld.lld %t-npic.o %t-pic.o %t-sto-pic.o -o %t.exe
# RUN: llvm-objdump -d %t.exe | FileCheck %s
# CHECK: Disassembly of section .text:
@@ -22,7 +27,7 @@
# CHECK-NEXT: 20018: 0c 00 80 19 jal 131172 <foo2+0x4>
# ^-- .pic.foo2
# CHECK-NEXT: 2001c: 00 00 00 00 nop
-# CHECK-NEXT: 20020: 0c 00 80 28 jal 131232 <fnpic+0x10>
+# CHECK-NEXT: 20020: 0c 00 80 25 jal 131220 <fnpic+0x4>
# ^-- .pic.fpic
# CHECK-NEXT: 20024: 00 00 00 00 nop
# CHECK-NEXT: 20028: 0c 00 80 24 jal 131216 <fnpic>
@@ -56,25 +61,19 @@
# CHECK-NEXT: 20078: 00 00 00 00 nop
# CHECK-NEXT: 2007c: 00 00 00 00 nop
#
-# CHECK: fpic:
-# CHECK-NEXT: 20080: 00 00 00 00 nop
-# CHECK-NEXT: 20084: 00 00 00 00 nop
-# CHECK-NEXT: 20088: 00 00 00 00 nop
-# CHECK-NEXT: 2008c: 00 00 00 00 nop
+# CHECK: fpic:
+# CHECK-NEXT: 20080: 00 00 00 00 nop
#
-# CHECK: fnpic:
+# CHECK: fnpic:
# CHECK-NEXT: 20090: 00 00 00 00 nop
-# CHECK-NEXT: 20094: 00 00 00 00 nop
-# CHECK-NEXT: 20098: 00 00 00 00 nop
-# CHECK-NEXT: 2009c: 00 00 00 00 nop
-# CHECK-NEXT: 200a0: 3c 19 00 02 lui $25, 2
-# CHECK-NEXT: 200a4: 08 00 80 20 j 131200 <fpic>
-# CHECK-NEXT: 200a8: 27 39 00 80 addiu $25, $25, 128
+# CHECK-NEXT: 20094: 3c 19 00 02 lui $25, 2
+# CHECK-NEXT: 20098: 08 00 80 20 j 131200 <fpic>
+# CHECK-NEXT: 2009c: 27 39 00 80 addiu $25, $25, 128
-# Make sure tha thunks are created properly no matter how
+# Make sure the thunks are created properly no matter how
# objects are laid out.
#
-# RUN: ld.lld %t-pic.o %t-npic.o %p/Inputs/mips-sto-pic.o -o %t.exe
+# RUN: ld.lld %t-pic.o %t-npic.o %t-sto-pic.o -o %t.exe
# RUN: llvm-objdump -d %t.exe | FileCheck -check-prefix=REVERSE %s
# REVERSE: foo1a:
@@ -112,26 +111,19 @@
# REVERSE-NEXT: 20064: 00 00 00 00 nop
# REVERSE-NEXT: 20068: 0c 00 80 0d jal 131124 <foo2+0x4>
# REVERSE-NEXT: 2006c: 00 00 00 00 nop
-# REVERSE-NEXT: 20070: 0c 00 80 28 jal 131232 <fnpic+0x10>
+# REVERSE-NEXT: 20070: 0c 00 80 25 jal 131220 <fnpic+0x4>
# REVERSE-NEXT: 20074: 00 00 00 00 nop
# REVERSE-NEXT: 20078: 0c 00 80 24 jal 131216 <fnpic>
# REVERSE-NEXT: 2007c: 00 00 00 00 nop
#
-# REVERSE: fpic:
+# REVERSE: fpic:
# REVERSE-NEXT: 20080: 00 00 00 00 nop
-# REVERSE-NEXT: 20084: 00 00 00 00 nop
-# REVERSE-NEXT: 20088: 00 00 00 00 nop
-# REVERSE-NEXT: 2008c: 00 00 00 00 nop
#
-# REVERSE: fnpic:
+# REVERSE: fnpic:
# REVERSE-NEXT: 20090: 00 00 00 00 nop
-# REVERSE-NEXT: 20094: 00 00 00 00 nop
-# REVERSE-NEXT: 20098: 00 00 00 00 nop
-# REVERSE-NEXT: 2009c: 00 00 00 00 nop
-# REVERSE-NEXT: 200a0: 3c 19 00 02 lui $25, 2
-# REVERSE-NEXT: 200a4: 08 00 80 20 j 131200 <fpic>
-# REVERSE-NEXT: 200a8: 27 39 00 80 addiu $25, $25, 128
-# REVERSE-NEXT: 200ac: 00 00 00 00 nop
+# REVERSE-NEXT: 20094: 3c 19 00 02 lui $25, 2
+# REVERSE-NEXT: 20098: 08 00 80 20 j 131200 <fpic>
+# REVERSE-NEXT: 2009c: 27 39 00 80 addiu $25, $25, 128
.text
.globl __start
diff --git a/test/ELF/mips-options-r.test b/test/ELF/mips-options-r.test
index c4144057e9ad..40c61c0d2bac 100644
--- a/test/ELF/mips-options-r.test
+++ b/test/ELF/mips-options-r.test
@@ -1,7 +1,6 @@
# Check that if input file contains .MIPS.options section and symbol
# points to the section and the linker generates a relocatable output,
-# LLD does not crash and write section symbols point to the output
-# .MIPS.options section.
+# LLD does not crash.
#
# PR 27878
#
@@ -12,8 +11,8 @@
# % as -mabi=64 -mips64r2 t.s
# RUN: ld.lld -r %p/Inputs/mips-options.o -o %t.o
-# RUN: llvm-readobj -t %t.o | FileCheck %s
+# RUN: llvm-readobj -s %t.o | FileCheck %s
# REQUIRES: mips
-# CHECK: Section: .MIPS.options
+# CHECK: Name: .MIPS.options
diff --git a/test/ELF/mips-options.s b/test/ELF/mips-options.s
index 30381ae55af7..f48ecad81d26 100644
--- a/test/ELF/mips-options.s
+++ b/test/ELF/mips-options.s
@@ -3,7 +3,10 @@
# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t1.o
# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux \
# RUN: %S/Inputs/mips-dynamic.s -o %t2.o
-# RUN: ld.lld %t1.o %t2.o -shared -o %t.so
+# RUN: echo "SECTIONS { \
+# RUN: . = 0x100000000; \
+# RUN: .got : { *(.got) } }" > %t.rel.script
+# RUN: ld.lld %t1.o %t2.o --script %t.rel.script -shared -o %t.so
# RUN: llvm-readobj -symbols -mips-options %t.so | FileCheck %s
# REQUIRES: mips
@@ -11,15 +14,15 @@
.text
.globl __start
__start:
- lw $t0,%call16(g1)($gp)
+ lui $gp, %hi(%neg(%gp_rel(g1)))
# CHECK: Name: _gp
-# CHECK-NEXT: Value: 0x[[GP:[0-9A-F]+]]
+# CHECK-NEXT: Value: 0x100008250
# CHECK: MIPS Options {
# CHECK-NEXT: ODK_REGINFO {
-# CHECK-NEXT: GP: 0x[[GP]]
-# CHECK-NEXT: General Mask: 0x10001001
+# CHECK-NEXT: GP: 0x100008250
+# CHECK-NEXT: General Mask: 0x10000001
# CHECK-NEXT: Co-Proc Mask0: 0x0
# CHECK-NEXT: Co-Proc Mask1: 0x0
# CHECK-NEXT: Co-Proc Mask2: 0x0
diff --git a/test/ELF/mips-plt-r6.s b/test/ELF/mips-plt-r6.s
new file mode 100644
index 000000000000..8685d6158f78
--- /dev/null
+++ b/test/ELF/mips-plt-r6.s
@@ -0,0 +1,38 @@
+# Check PLT entries generation in case of R6 ABI version.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mcpu=mips32r6 %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mcpu=mips32r6 %S/Inputs/mips-dynamic.s -o %t2.o
+# RUN: ld.lld %t2.o -shared -o %t.so
+# RUN: ld.lld %t1.o %t.so -o %t.exe
+# RUN: llvm-objdump -d %t.exe | FileCheck %s
+
+# REQUIRES: mips
+
+# CHECK: Disassembly of section .text:
+# CHECK-NEXT: __start:
+# CHECK-NEXT: 20000: 0c 00 80 0c jal 131120
+# ^-- 0x20030 gotplt[foo0]
+# CHECK-NEXT: 20004: 00 00 00 00 nop
+#
+# CHECK-NEXT: Disassembly of section .plt:
+# CHECK-NEXT: .plt:
+# CHECK-NEXT: 20010: 3c 1c 00 04 aui $zero, $gp, 4
+# CHECK-NEXT: 20014: 8f 99 00 04 lw $25, 4($gp)
+# CHECK-NEXT: 20018: 27 9c 00 04 addiu $gp, $gp, 4
+# CHECK-NEXT: 2001c: 03 1c c0 23 subu $24, $24, $gp
+# CHECK-NEXT: 20020: 03 e0 78 25 move $15, $ra
+# CHECK-NEXT: 20024: 00 18 c0 82 srl $24, $24, 2
+# CHECK-NEXT: 20028: 03 20 f8 09 jalr $25
+# CHECK-NEXT: 2002c: 27 18 ff fe addiu $24, $24, -2
+
+# CHECK-NEXT: 20030: 3c 0f 00 04 aui $zero, $15, 4
+# CHECK-NEXT: 20034: 8d f9 00 0c lw $25, 12($15)
+# CHECK-NEXT: 20038: 03 20 00 09 jr $25
+# CHECK-NEXT: 2003c: 25 f8 00 0c addiu $24, $15, 12
+
+ .text
+ .global __start
+__start:
+ jal foo0 # R_MIPS_26 against 'foo0' from DSO
diff --git a/test/ELF/mips-relocatable.s b/test/ELF/mips-relocatable.s
new file mode 100644
index 000000000000..168ddf736e1e
--- /dev/null
+++ b/test/ELF/mips-relocatable.s
@@ -0,0 +1,21 @@
+# Check linking MIPS code in case of -r linker's option.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
+# RUN: ld.lld -r -o %t-r.o %t.o
+# RUN: llvm-objdump -s -t %t-r.o | FileCheck %s
+
+# REQUIRES: mips
+
+ .text
+ .global __start
+__start:
+ lw $t0,%call16(__start)($gp)
+foo:
+ nop
+
+ .section .rodata, "a"
+v:
+ .gpword foo
+
+# CHECK-NOT: Contents of section .got:
+# CHECK-NOT: {{.*}} _gp
diff --git a/test/ELF/mips-sto-pic-flag.s b/test/ELF/mips-sto-pic-flag.s
new file mode 100644
index 000000000000..4d7923346496
--- /dev/null
+++ b/test/ELF/mips-sto-pic-flag.s
@@ -0,0 +1,58 @@
+# In case of linking PIC and non-PIC code together and generation
+# of a relocatable object, all PIC symbols should have STO_MIPS_PIC
+# flag in the symbol table of the ouput file.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t-npic.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: %p/Inputs/mips-pic.s -o %t-pic.o
+# RUN: ld.lld -r %t-npic.o %t-pic.o -o %t-rel.o
+# RUN: llvm-readobj -t %t-rel.o | FileCheck %s
+
+# REQUIRES: mips
+
+# CHECK: Symbol {
+# CHECK: Name: main
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Local
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .text
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: foo1a
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Function
+# CHECK-NEXT: Other [
+# CHECK-NEXT: STO_MIPS_PIC
+# CHECK-NEXT: ]
+# CHECK-NEXT: Section: .text
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: foo1b
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Function
+# CHECK-NEXT: Other [
+# CHECK-NEXT: STO_MIPS_PIC
+# CHECK-NEXT: ]
+# CHECK-NEXT: Section: .text
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: foo2
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Function
+# CHECK-NEXT: Other [
+# CHECK-NEXT: STO_MIPS_PIC
+# CHECK-NEXT: ]
+# CHECK-NEXT: Section: .text
+# CHECK-NEXT: }
+
+ .text
+main:
+ nop
diff --git a/test/ELF/mips-tls-64.s b/test/ELF/mips-tls-64.s
index 9c05e940b1c1..808fa4cbd1d7 100644
--- a/test/ELF/mips-tls-64.s
+++ b/test/ELF/mips-tls-64.s
@@ -1,86 +1,112 @@
# Check MIPS TLS 64-bit relocations handling.
# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux \
-# RUN: %p/Inputs/mips-dynamic.s -o %t.so.o
+# RUN: %p/Inputs/mips-tls.s -o %t.so.o
# RUN: ld.lld -shared %t.so.o -o %t.so
# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t.o
+
# RUN: ld.lld %t.o %t.so -o %t.exe
# RUN: llvm-objdump -d -s -t %t.exe | FileCheck -check-prefix=DIS %s
# RUN: llvm-readobj -r -mips-plt-got %t.exe | FileCheck %s
+# RUN: ld.lld -shared %t.o %t.so -o %t-out.so
+# RUN: llvm-objdump -d -s -t %t-out.so | FileCheck -check-prefix=DIS-SO %s
+# RUN: llvm-readobj -r -mips-plt-got %t-out.so | FileCheck -check-prefix=SO %s
+
# REQUIRES: mips
# DIS: __start:
-# DIS-NEXT: 20000: 24 62 80 28 addiu $2, $3, -32728
-# DIS-NEXT: 20004: 24 62 80 38 addiu $2, $3, -32712
-# DIS-NEXT: 20008: 8f 82 80 20 lw $2, -32736($gp)
+# DIS-NEXT: 20000: 24 62 80 20 addiu $2, $3, -32736
+# DIS-NEXT: 20004: 24 62 80 30 addiu $2, $3, -32720
+# DIS-NEXT: 20008: 24 62 80 38 addiu $2, $3, -32712
# DIS-NEXT: 2000c: 24 62 80 48 addiu $2, $3, -32696
+# DIS-NEXT: 20010: 24 62 80 58 addiu $2, $3, -32680
# DIS: Contents of section .got:
-# DIS_NEXT: 30008 00000000 00000000 80000000 00000000
-# DIS_NEXT: 30018 00000000 00020000 00000000 00000000
-# DIS_NEXT: 30028 00000000 00000004 00000000 00000000
-# DIS_NEXT: 30038 00000000 00000000 00000000 00000004
+# DIS-NEXT: 30008 00000000 00000000 80000000 00000000
+# DIS-NEXT: 30018 00000000 00000000 00000000 00000000
+# DIS-NEXT: 30028 00000000 00000000 00000000 00000001
+# DIS-NEXT: 30038 00000000 00000000 00000000 00000001
+# DIS-NEXT: 30048 ffffffff ffff8004 ffffffff ffff9004
# DIS: 0000000000030000 l .tdata 00000000 .tdata
# DIS: 0000000000030000 l .tdata 00000000 loc
-# DIS: 0000000000000004 g .tdata 00000000 foo
+# DIS: 0000000000000004 g .tdata 00000000 bar
+# DIS: 0000000000000000 g *UND* 00000000 foo
# CHECK: Relocations [
# CHECK-NEXT: Section (7) .rela.dyn {
-# CHECK-NEXT: 0x30020 R_MIPS_TLS_DTPMOD64/R_MIPS_NONE/R_MIPS_NONE - 0x0
-# CHECK-NEXT: 0x30028 R_MIPS_TLS_DTPREL64/R_MIPS_NONE/R_MIPS_NONE - 0x0
-# CHECK-NEXT: 0x30030 R_MIPS_TLS_DTPMOD64/R_MIPS_NONE/R_MIPS_NONE - 0x0
-# CHECK-NEXT: 0x30040 R_MIPS_TLS_TPREL64/R_MIPS_NONE/R_MIPS_NONE - 0x4
+# CHECK-NEXT: 0x30018 R_MIPS_TLS_DTPMOD64/R_MIPS_NONE/R_MIPS_NONE foo 0x0
+# CHECK-NEXT: 0x30020 R_MIPS_TLS_DTPREL64/R_MIPS_NONE/R_MIPS_NONE foo 0x0
+# CHECK-NEXT: 0x30028 R_MIPS_TLS_TPREL64/R_MIPS_NONE/R_MIPS_NONE foo 0x0
# CHECK-NEXT: }
# CHECK-NEXT: ]
# CHECK-NEXT: Primary GOT {
# CHECK-NEXT: Canonical gp value: 0x37FF8
# CHECK-NEXT: Reserved entries [
-# CHECK-NEXT: Entry {
-# CHECK-NEXT: Address: 0x30008
-# CHECK-NEXT: Access: -32752
-# CHECK-NEXT: Initial: 0x0
-# CHECK-NEXT: Purpose: Lazy resolver
-# CHECK-NEXT: }
-# CHECK-NEXT: Entry {
-# CHECK-NEXT: Address: 0x30010
-# CHECK-NEXT: Access: -32744
-# CHECK-NEXT: Initial: 0x80000000
-# CHECK-NEXT: Purpose: Module pointer (GNU extension)
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
+# CHECK: ]
# CHECK-NEXT: Local entries [
# CHECK-NEXT: ]
# CHECK-NEXT: Global entries [
-# CHECK-NEXT: Entry {
-# CHECK-NEXT: Address: 0x30018
-# CHECK-NEXT: Access: -32736
-# CHECK-NEXT: Initial: 0x0
-# CHECK-NEXT: Value: 0x0
-# CHECK-NEXT: Type: Function
-# CHECK-NEXT: Section: Undefined
-# CHECK-NEXT: Name: foo0
-# CHECK-NEXT: }
# CHECK-NEXT: ]
-# CHECK-NEXT: Number of TLS and multi-GOT entries: 5
-# ^-- 0x30020 / -32728 - R_MIPS_TLS_GD - R_MIPS_TLS_DTPMOD32 foo
-# ^-- 0x30028 / -32720 - R_MIPS_TLS_DTPREL32 foo
-# ^-- 0x30030 / -32712 - R_MIPS_TLS_LDM - R_MIPS_TLS_DTPMOD32 loc
-# ^-- 0x30038 / -32704
-# ^-- 0x30040 / -32696 - R_MIPS_TLS_GOTTPREL - R_MIPS_TLS_TPREL32
+# CHECK-NEXT: Number of TLS and multi-GOT entries: 8
+# ^-- -32736 R_MIPS_TLS_GD R_MIPS_TLS_DTPMOD64 foo
+# ^-- -32728 R_MIPS_TLS_DTPREL64 foo
+# ^-- -32720 R_MIPS_TLS_GOTTPREL R_MIPS_TLS_TPREL64 foo
+# ^-- -32712 R_MIPS_TLS_LDM 1 loc
+# ^-- -32704 0 loc
+# ^-- -32696 R_MIPS_TLS_GD 1 bar
+# ^-- -32688 VA - 0x8000 bar
+# ^-- -32680 R_MIPS_TLS_GOTTPREL VA - 0x7000 bar
+
+# DIS-SO: Contents of section .got:
+# DIS-SO-NEXT: 20008 00000000 00000000 80000000 00000000
+# DIS-SO-NEXT: 20018 00000000 00000000 00000000 00000000
+# DIS-SO-NEXT: 20028 00000000 00000000 00000000 00000000
+# DIS-SO-NEXT: 20038 00000000 00000000 00000000 00000000
+# DIS-SO-NEXT: 20048 00000000 00000000 00000000 00000000
+
+# SO: Relocations [
+# SO-NEXT: Section (7) .rela.dyn {
+# SO-NEXT: 0x20030 R_MIPS_TLS_DTPMOD64/R_MIPS_NONE/R_MIPS_NONE - 0x0
+# SO-NEXT: 0x20040 R_MIPS_TLS_DTPMOD64/R_MIPS_NONE/R_MIPS_NONE bar 0x0
+# SO-NEXT: 0x20048 R_MIPS_TLS_DTPREL64/R_MIPS_NONE/R_MIPS_NONE bar 0x0
+# SO-NEXT: 0x20050 R_MIPS_TLS_TPREL64/R_MIPS_NONE/R_MIPS_NONE bar 0x0
+# SO-NEXT: 0x20018 R_MIPS_TLS_DTPMOD64/R_MIPS_NONE/R_MIPS_NONE foo 0x0
+# SO-NEXT: 0x20020 R_MIPS_TLS_DTPREL64/R_MIPS_NONE/R_MIPS_NONE foo 0x0
+# SO-NEXT: 0x20028 R_MIPS_TLS_TPREL64/R_MIPS_NONE/R_MIPS_NONE foo 0x0
+# SO-NEXT: }
+# SO-NEXT: ]
+# SO-NEXT: Primary GOT {
+# SO-NEXT: Canonical gp value: 0x27FF8
+# SO-NEXT: Reserved entries [
+# SO: ]
+# SO-NEXT: Local entries [
+# SO-NEXT: ]
+# SO-NEXT: Global entries [
+# SO-NEXT: ]
+# SO-NEXT: Number of TLS and multi-GOT entries: 8
+# ^-- 0x20018 R_MIPS_TLS_GD R_MIPS_TLS_DTPMOD64 foo
+# ^-- 0x20020 R_MIPS_TLS_DTPREL64 foo
+# ^-- 0x20028 R_MIPS_TLS_GOTTPREL R_MIPS_TLS_TPREL64 foo
+# ^-- 0x20030 R_MIPS_TLS_LDM R_MIPS_TLS_DTPMOD64 loc
+# ^-- 0x20038 0 loc
+# ^-- 0x20040 R_MIPS_TLS_GD R_MIPS_TLS_DTPMOD64 bar
+# ^-- 0x20048 R_MIPS_TLS_DTPREL64 bar
+# ^-- 0x20050 R_MIPS_TLS_GOTTPREL R_MIPS_TLS_TPREL64 bar
.text
.global __start
__start:
addiu $2, $3, %tlsgd(foo) # R_MIPS_TLS_GD
- addiu $2, $3, %tlsldm(loc) # R_MIPS_TLS_LDM
- lw $2, %got(foo0)($gp)
addiu $2, $3, %gottprel(foo) # R_MIPS_TLS_GOTTPREL
+ addiu $2, $3, %tlsldm(loc) # R_MIPS_TLS_LDM
+ addiu $2, $3, %tlsgd(bar) # R_MIPS_TLS_GD
+ addiu $2, $3, %gottprel(bar) # R_MIPS_TLS_GOTTPREL
.section .tdata,"awT",%progbits
- .global foo
+ .global bar
loc:
.word 0
-foo:
+bar:
.word 0
diff --git a/test/ELF/mips-tls-static-64.s b/test/ELF/mips-tls-static-64.s
new file mode 100644
index 000000000000..e7baf67bb295
--- /dev/null
+++ b/test/ELF/mips-tls-static-64.s
@@ -0,0 +1,37 @@
+# Check handling TLS related relocations and symbols when linking
+# a 64-bit static executable.
+
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t
+# RUN: ld.lld -static %t -o %t.exe
+# RUN: llvm-objdump -s -t %t.exe | FileCheck %s
+
+# REQUIRES: mips
+
+# CHECK: Contents of section .data:
+# CHECK-NEXT: 40000 00020004 ffffffff ffff8004 ffffffff
+# CHECK-NEXT: 40010 ffff9004
+#
+# CHECK: SYMBOL TABLE:
+# CHECK: 0000000000020004 .text 00000000 __tls_get_addr
+# CHECK: 0000000000000000 g .tdata 00000000 tls1
+
+ .text
+ .global __start
+__start:
+ nop
+
+ .global __tls_get_addr
+__tls_get_addr:
+ nop
+
+ .data
+loc:
+ .word __tls_get_addr
+ .dtpreldword tls1+4 # R_MIPS_TLS_DTPREL64
+ .tpreldword tls1+4 # R_MIPS_TLS_TPREL64
+
+ .section .tdata,"awT",%progbits
+ .global tls1
+tls1:
+ .word __tls_get_addr
+ .word 0
diff --git a/test/ELF/mips-tls-static.s b/test/ELF/mips-tls-static.s
new file mode 100644
index 000000000000..6f0fa559ae0d
--- /dev/null
+++ b/test/ELF/mips-tls-static.s
@@ -0,0 +1,42 @@
+# Check handling TLS related relocations and symbols when linking
+# a static executable.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t
+# RUN: ld.lld -static %t -o %t.exe
+# RUN: llvm-objdump -s -t %t.exe | FileCheck %s
+
+# REQUIRES: mips
+
+# CHECK: Contents of section .got:
+# CHECK-NEXT: 30008 00000000 80000000 00000001 ffff8000
+# CHECK-NEXT: 30018 00000001 00000000 ffff9000
+# CHECK: Contents of section .data:
+# CHECK-NEXT: 40000 0002000c ffff8004 ffff9004
+#
+# CHECK: SYMBOL TABLE:
+# CHECK: 0002000c .text 00000000 __tls_get_addr
+# CHECK: 00000000 g .tdata 00000000 tls1
+
+ .text
+ .global __start
+__start:
+ addiu $2, $3, %tlsgd(tls1) # R_MIPS_TLS_GD
+ addiu $2, $3, %tlsldm(tls2) # R_MIPS_TLS_LDM
+ addiu $2, $3, %gottprel(tls1) # R_MIPS_TLS_GOTTPREL
+
+ .global __tls_get_addr
+__tls_get_addr:
+ nop
+
+ .data
+loc:
+ .word __tls_get_addr
+ .dtprelword tls1+4 # R_MIPS_TLS_DTPREL32
+ .tprelword tls1+4 # R_MIPS_TLS_TPREL32
+
+ .section .tdata,"awT",%progbits
+ .global tls1
+tls1:
+ .word __tls_get_addr
+tls2:
+ .word 0
diff --git a/test/ELF/mips-tls.s b/test/ELF/mips-tls.s
index 9635558c2d95..fa02324fd850 100644
--- a/test/ELF/mips-tls.s
+++ b/test/ELF/mips-tls.s
@@ -4,74 +4,105 @@
# RUN: %p/Inputs/mips-tls.s -o %t.so.o
# RUN: ld.lld -shared %t.so.o -o %t.so
# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
+
# RUN: ld.lld %t.o %t.so -o %t.exe
# RUN: llvm-objdump -d -s -t %t.exe | FileCheck -check-prefix=DIS %s
# RUN: llvm-readobj -r -mips-plt-got %t.exe | FileCheck %s
+# RUN: ld.lld -shared %t.o %t.so -o %t-out.so
+# RUN: llvm-objdump -d -s -t %t-out.so | FileCheck -check-prefix=DIS-SO %s
+# RUN: llvm-readobj -r -mips-plt-got %t-out.so | FileCheck -check-prefix=SO %s
+
# REQUIRES: mips
# DIS: __start:
-# DIS-NEXT: 20000: 24 62 80 1c addiu $2, $3, -32740
-# DIS-NEXT: 20004: 24 62 80 24 addiu $2, $3, -32732
-# DIS-NEXT: 20008: 8f 82 80 18 lw $2, -32744($gp)
+# DIS-NEXT: 20000: 24 62 80 18 addiu $2, $3, -32744
+# DIS-NEXT: 20004: 24 62 80 20 addiu $2, $3, -32736
+# DIS-NEXT: 20008: 24 62 80 24 addiu $2, $3, -32732
# DIS-NEXT: 2000c: 24 62 80 2c addiu $2, $3, -32724
+# DIS-NEXT: 20010: 24 62 80 34 addiu $2, $3, -32716
# DIS: Contents of section .got:
-# DIS_NEXT: 30004 00000000 80000000 00020000 00000000
-# DIS_NEXT: 30014 00000000 00000000 00000000 00000000
+# DIS-NEXT: 30008 00000000 80000000 00000000 00000000
+# DIS-NEXT: 30018 00000000 00000001 00000000 00000001
+# DIS-NEXT: 30028 ffff8004 ffff9004
# DIS: 00030000 l .tdata 00000000 .tdata
# DIS: 00030000 l .tdata 00000000 loc
+# DIS: 00000004 g .tdata 00000000 bar
# DIS: 00000000 g *UND* 00000000 foo
# CHECK: Relocations [
# CHECK-NEXT: Section (7) .rel.dyn {
-# CHECK-NEXT: 0x30018 R_MIPS_TLS_DTPMOD32 - 0x0
# CHECK-NEXT: 0x30010 R_MIPS_TLS_DTPMOD32 foo 0x0
# CHECK-NEXT: 0x30014 R_MIPS_TLS_DTPREL32 foo 0x0
-# CHECK-NEXT: 0x30020 R_MIPS_TLS_TPREL32 foo 0x0
+# CHECK-NEXT: 0x30018 R_MIPS_TLS_TPREL32 foo 0x0
# CHECK-NEXT: }
# CHECK-NEXT: ]
# CHECK-NEXT: Primary GOT {
-# CHECK-NEXT: Canonical gp value: 0x37FF4
+# CHECK-NEXT: Canonical gp value: 0x37FF8
# CHECK-NEXT: Reserved entries [
-# CHECK-NEXT: Entry {
-# CHECK-NEXT: Address: 0x30004
-# CHECK-NEXT: Access: -32752
-# CHECK-NEXT: Initial: 0x0
-# CHECK-NEXT: Purpose: Lazy resolver
-# CHECK-NEXT: }
-# CHECK-NEXT: Entry {
-# CHECK-NEXT: Address: 0x30008
-# CHECK-NEXT: Access: -32748
-# CHECK-NEXT: Initial: 0x80000000
-# CHECK-NEXT: Purpose: Module pointer (GNU extension)
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
+# CHECK: ]
# CHECK-NEXT: Local entries [
-# CHECK-NEXT: Entry {
-# CHECK-NEXT: Address: 0x3000C
-# CHECK-NEXT: Access: -32744
-# CHECK-NEXT: Initial: 0x20000
-# CHECK-NEXT: }
# CHECK-NEXT: ]
# CHECK-NEXT: Global entries [
# CHECK-NEXT: ]
-# CHECK-NEXT: Number of TLS and multi-GOT entries: 5
-# ^-- 0x30010 / -32740 - R_MIPS_TLS_GD - R_MIPS_TLS_DTPMOD32 foo
-# ^-- 0x30018 / -32736 - R_MIPS_TLS_DTPREL32 foo
-# ^-- 0x3001C / -32732 - R_MIPS_TLS_LDM - R_MIPS_TLS_DTPMOD32 loc
-# ^-- 0x30020 / -32728
-# ^-- 0x30024 / -32724 - R_MIPS_TLS_GOTTPREL - R_MIPS_TLS_TPREL32
+# CHECK-NEXT: Number of TLS and multi-GOT entries: 8
+# ^-- 0x30010 R_MIPS_TLS_GD R_MIPS_TLS_DTPMOD32 foo
+# ^-- 0x30014 R_MIPS_TLS_DTPREL32 foo
+# ^-- 0x30018 R_MIPS_TLS_GOTTPREL R_MIPS_TLS_TPREL32 foo
+# ^-- 0x3001C R_MIPS_TLS_LDM 1 loc
+# ^-- 0x30020 0 loc
+# ^-- 0x30024 R_MIPS_TLS_GD 1 bar
+# ^-- 0x30028 VA - 0x8000 bar
+# ^-- 0x3002C R_MIPS_TLS_GOTTPREL VA - 0x7000 bar
+
+# DIS-SO: Contents of section .got:
+# DIS-SO-NEXT: 20008 00000000 80000000 00000000 00000000
+# DIS-SO-NEXT: 20018 00000000 00000000 00000000 00000000
+# DIS-SO-NEXT: 20028 00000000 00000000
+
+# SO: Relocations [
+# SO-NEXT: Section (7) .rel.dyn {
+# SO-NEXT: 0x2001C R_MIPS_TLS_DTPMOD32 - 0x0
+# SO-NEXT: 0x20024 R_MIPS_TLS_DTPMOD32 bar 0x0
+# SO-NEXT: 0x20028 R_MIPS_TLS_DTPREL32 bar 0x0
+# SO-NEXT: 0x2002C R_MIPS_TLS_TPREL32 bar 0x0
+# SO-NEXT: 0x20010 R_MIPS_TLS_DTPMOD32 foo 0x0
+# SO-NEXT: 0x20014 R_MIPS_TLS_DTPREL32 foo 0x0
+# SO-NEXT: 0x20018 R_MIPS_TLS_TPREL32 foo 0x0
+# SO-NEXT: }
+# SO-NEXT: ]
+# SO-NEXT: Primary GOT {
+# SO-NEXT: Canonical gp value: 0x27FF8
+# SO-NEXT: Reserved entries [
+# SO: ]
+# SO-NEXT: Local entries [
+# SO-NEXT: ]
+# SO-NEXT: Global entries [
+# SO-NEXT: ]
+# SO-NEXT: Number of TLS and multi-GOT entries: 8
+# ^-- 0x20010 R_MIPS_TLS_GD R_MIPS_TLS_DTPMOD32 foo
+# ^-- 0x20014 R_MIPS_TLS_DTPREL32 foo
+# ^-- 0x20018 R_MIPS_TLS_GOTTPREL R_MIPS_TLS_TPREL32 foo
+# ^-- 0x2001C R_MIPS_TLS_LDM R_MIPS_TLS_DTPMOD32 loc
+# ^-- 0x20020 0 loc
+# ^-- 0x20024 R_MIPS_TLS_GD R_MIPS_TLS_DTPMOD32 bar
+# ^-- 0x20028 R_MIPS_TLS_DTPREL32 bar
+# ^-- 0x2002C R_MIPS_TLS_GOTTPREL R_MIPS_TLS_TPREL32 bar
.text
.global __start
__start:
addiu $2, $3, %tlsgd(foo) # R_MIPS_TLS_GD
- addiu $2, $3, %tlsldm(loc) # R_MIPS_TLS_LDM
- lw $2, %got(__start)($gp)
addiu $2, $3, %gottprel(foo) # R_MIPS_TLS_GOTTPREL
+ addiu $2, $3, %tlsldm(loc) # R_MIPS_TLS_LDM
+ addiu $2, $3, %tlsgd(bar) # R_MIPS_TLS_GD
+ addiu $2, $3, %gottprel(bar) # R_MIPS_TLS_GOTTPREL
.section .tdata,"awT",%progbits
+ .global bar
loc:
.word 0
+bar:
+ .word 0
diff --git a/test/ELF/mips-xgot-order.s b/test/ELF/mips-xgot-order.s
new file mode 100644
index 000000000000..d249715bafa5
--- /dev/null
+++ b/test/ELF/mips-xgot-order.s
@@ -0,0 +1,49 @@
+# Check that GOT entries accessed via 16-bit indexing are allocated
+# in the beginning of the GOT.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t.exe
+# RUN: llvm-objdump -d -s -t %t.exe | FileCheck %s
+
+# REQUIRES: mips
+
+# CHECK: Disassembly of section .text:
+# CHECK-NEXT: __start:
+# CHECK-NEXT: 20000: 3c 02 00 00 lui $2, 0
+# CHECK-NEXT: 20004: 8c 42 80 24 lw $2, -32732($2)
+# CHECK-NEXT: 20008: 3c 02 00 00 lui $2, 0
+# CHECK-NEXT: 2000c: 8c 42 80 28 lw $2, -32728($2)
+#
+# CHECK: bar:
+# CHECK-NEXT: 20010: 8c 42 80 20 lw $2, -32736($2)
+# CHECK-NEXT: 20014: 8c 42 80 18 lw $2, -32744($2)
+# CHECK-NEXT: 20018: 20 42 00 00 addi $2, $2, 0
+
+# CHECK: Contents of section .got:
+# CHECK-NEXT: 30000 00000000 80000000 00040000 00050000
+# ^ %hi(loc)
+# ^ redundant entry
+# CHECK-NEXT: 30010 00020010 00020000 00040000
+# ^ %got(bar)
+# ^ %got_hi/lo(start)
+# ^ %got_hi/lo(loc)
+
+# CHECK: 00040000 .data 00000000 loc
+# CHECK: 00020000 .text 00000000 __start
+# CHECK: 00020010 .text 00000000 bar
+
+ .text
+ .global __start, bar
+__start:
+ lui $2, %got_hi(__start)
+ lw $2, %got_lo(__start)($2)
+ lui $2, %got_hi(loc)
+ lw $2, %got_lo(loc)($2)
+bar:
+ lw $2, %got(bar)($2)
+ lw $2, %got(loc)($2)
+ addi $2, $2, %lo(loc)
+
+ .data
+loc:
+ .word 0
diff --git a/test/ELF/no-inhibit-exec.s b/test/ELF/no-inhibit-exec.s
index 31638fd92cc9..d0970d93f21e 100644
--- a/test/ELF/no-inhibit-exec.s
+++ b/test/ELF/no-inhibit-exec.s
@@ -6,7 +6,7 @@
# CHECK: Disassembly of section .text:
# CHECK-NEXT: _start
-# CHECK-NEXT: 11000: e8 fb ef fe ff callq -69637
+# CHECK-NEXT: 201000: {{.*}} callq -2101253
# next code will not link without noinhibit-exec flag
# because of undefined symbol _bar
diff --git a/test/ELF/no-merge.s b/test/ELF/no-merge.s
new file mode 100644
index 000000000000..5d8b8570ab49
--- /dev/null
+++ b/test/ELF/no-merge.s
@@ -0,0 +1,25 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS { .data : {*(.data.*)} }" > %t0.script
+# RUN: ld.lld %t.o -o %t0.out --script %t0.script
+# RUN: llvm-objdump -s %t0.out | FileCheck %s --check-prefix=OPT
+# OPT: Contents of section .data:
+# OPT-NEXT: 0000 01
+# OPT-NEXT: Contents of section .data:
+# OPT-NEXT: 0001 6100
+# OPT-NEXT: Contents of section .data:
+# OPT-NEXT: 0003 03
+
+# RUN: ld.lld -O0 %t.o -o %t1.out --script %t0.script
+# RUN: llvm-objdump -s %t1.out | FileCheck %s --check-prefix=NOOPT
+# NOOPT: Contents of section .data:
+# NOOPT-NEXT: 0000 01610003
+
+.section .data.aw,"aw",@progbits
+.byte 1
+
+.section .data.ams,"aMS",@progbits,1
+.asciz "a"
+
+.section .data.am,"aM",@progbits,1
+.byte 3
diff --git a/test/ELF/no-obj.s b/test/ELF/no-obj.s
index 68e9979e3ede..eea10a45d879 100644
--- a/test/ELF/no-obj.s
+++ b/test/ELF/no-obj.s
@@ -2,7 +2,7 @@
// RUN: llvm-ar rcs %t.a %t.o
// RUN: not ld.lld -o %t2 -u _start %t.a 2>&1 | FileCheck %s
-// CHECK: -m or at least a .o file required
+// CHECK: target emulation unknown: -m or at least one .o file required
.global _start
_start:
diff --git a/test/ELF/non-abs-reloc.s b/test/ELF/non-abs-reloc.s
new file mode 100644
index 000000000000..ef9ba4466133
--- /dev/null
+++ b/test/ELF/non-abs-reloc.s
@@ -0,0 +1,11 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: not ld.lld %t.o -o %t.so -shared 2>&1 | FileCheck %s
+// CHECK: {{.*}}:(.dummy+0x0): has non-ABS reloc
+
+.globl _start
+_start:
+ nop
+
+.section .dummy
+ .long foo@gotpcrel
diff --git a/test/ELF/noplt-pie.s b/test/ELF/noplt-pie.s
index 1eb84934184e..81e4410ac2c2 100644
--- a/test/ELF/noplt-pie.s
+++ b/test/ELF/noplt-pie.s
@@ -10,8 +10,8 @@
# CHECK: Relocations [
# CHECK-NEXT: Section ({{.*}}) .rela.dyn {
-# CHECK-NEXT: 0x120B0 R_X86_64_GLOB_DAT bar 0x0
-# CHECK-NEXT: 0x120B8 R_X86_64_GLOB_DAT zed 0x0
+# CHECK-NEXT: 0x2020B0 R_X86_64_GLOB_DAT bar 0x0
+# CHECK-NEXT: 0x2020B8 R_X86_64_GLOB_DAT zed 0x0
# CHECK-NEXT: }
# CHECK-NEXT: ]
diff --git a/test/ELF/oformat-binary-ttext.s b/test/ELF/oformat-binary-ttext.s
new file mode 100644
index 000000000000..aced1a3be4cb
--- /dev/null
+++ b/test/ELF/oformat-binary-ttext.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# RUN: ld.lld -N -Ttext 0x100 -o %t.out %t --oformat binary
+# RUN: od -t x1 -v %t.out | FileCheck %s --check-prefix=BIN
+
+# BIN: 0000000 90 00 00 00 00 00 00 00
+# BIN-NEXT: 0000010
+# BIN-NOT: 0000020
+
+## The same but without OMAGIC.
+# RUN: ld.lld -Ttext 0x100 -o %t.out %t --oformat binary
+# RUN: od -t x1 -v %t.out | FileCheck %s --check-prefix=BIN
+
+.text
+.globl _start
+_start:
+ nop
diff --git a/test/ELF/oformat-binary.s b/test/ELF/oformat-binary.s
new file mode 100644
index 000000000000..acd95c7cef36
--- /dev/null
+++ b/test/ELF/oformat-binary.s
@@ -0,0 +1,32 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# RUN: ld.lld -o %t.out %t --oformat binary
+# RUN: od -t x1 -v %t.out | FileCheck %s
+# CHECK: 000000 90 11 22 00 00 00 00 00
+# CHECK-NOT: 00000010
+
+## Check case when linkerscript is used.
+# RUN: echo "SECTIONS { . = 0x1000; }" > %t.script
+# RUN: ld.lld -o %t2.out --script %t.script %t --oformat binary
+# RUN: od -t x1 -v %t2.out | FileCheck %s
+
+# RUN: echo "SECTIONS { }" > %t.script
+# RUN: ld.lld -o %t2.out --script %t.script %t --oformat binary
+# RUN: od -t x1 -v %t2.out | FileCheck %s
+
+# RUN: not ld.lld -o %t3.out %t --oformat foo 2>&1 \
+# RUN: | FileCheck %s --check-prefix ERR
+# ERR: unknown --oformat value: foo
+
+.text
+.align 4
+.globl _start
+_start:
+ nop
+
+.section .mysec.1,"ax"
+.byte 0x11
+
+.section .mysec.2,"ax"
+.byte 0x22
diff --git a/test/ELF/openbsd-randomize.s b/test/ELF/openbsd-randomize.s
new file mode 100644
index 000000000000..794e87b28122
--- /dev/null
+++ b/test/ELF/openbsd-randomize.s
@@ -0,0 +1,20 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
+# RUN: ld.lld %t -o %t.out
+# RUN: llvm-readobj --program-headers %t.out | FileCheck %s
+
+# CHECK: ProgramHeader {
+# CHECK: Type: PT_OPENBSD_RANDOMIZE (0x65A3DBE6)
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: VirtualAddress:
+# CHECK-NEXT: PhysicalAddress:
+# CHECK-NEXT: FileSize: 8
+# CHECK-NEXT: MemSize: 8
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R (0x4)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 1
+# CHECK-NEXT: }
+
+.section .openbsd.randomdata, "a"
+.quad 0
diff --git a/test/ELF/openbsd-wxneeded.s b/test/ELF/openbsd-wxneeded.s
new file mode 100644
index 000000000000..d209e4f01998
--- /dev/null
+++ b/test/ELF/openbsd-wxneeded.s
@@ -0,0 +1,17 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
+# RUN: ld.lld -z wxneeded %t -o %t.out
+# RUN: llvm-readobj --program-headers %t.out | FileCheck %s
+
+# CHECK: ProgramHeader {
+# CHECK: Type: PT_OPENBSD_WXNEEDED (0x65A3DBE7)
+# CHECK-NEXT: Offset: 0x0
+# CHECK-NEXT: VirtualAddress: 0x0
+# CHECK-NEXT: PhysicalAddress: 0x0
+# CHECK-NEXT: FileSize: 0
+# CHECK-NEXT: MemSize: 0
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_X
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 0
+# CHECK-NEXT: }
diff --git a/test/ELF/phdr-align.s b/test/ELF/phdr-align.s
index 58d537b46840..354897eedd27 100644
--- a/test/ELF/phdr-align.s
+++ b/test/ELF/phdr-align.s
@@ -2,6 +2,7 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
# RUN: echo "SECTIONS { \
+# RUN: . = SIZEOF_HEADERS; \
# RUN: .bss : { *(.bss) } \
# RUN: .data : { *(.data) } \
# RUN: .text : { *(.text) } }" > %t.script
diff --git a/test/ELF/pie.s b/test/ELF/pie.s
index 4cf1743ee736..5964db5c9399 100644
--- a/test/ELF/pie.s
+++ b/test/ELF/pie.s
@@ -1,5 +1,12 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
+
+## Default is no PIE.
+# RUN: ld.lld %t1.o -o %t
+# RUN: llvm-readobj -file-headers -sections -program-headers -symbols -r %t \
+# RUN: | FileCheck %s --check-prefix=NOPIE
+
+## Check -pie.
# RUN: ld.lld -pie %t1.o -o %t
# RUN: llvm-readobj -file-headers -sections -program-headers -symbols -r %t | FileCheck %s
@@ -18,20 +25,6 @@
# CHECK-NEXT: Unused: (00 00 00 00 00 00 00)
# CHECK-NEXT: }
# CHECK-NEXT: Type: SharedObject
-# CHECK-NEXT: Machine: EM_X86_64
-# CHECK-NEXT: Version: 1
-# CHECK-NEXT: Entry: 0x1000
-# CHECK-NEXT: ProgramHeaderOffset: 0x40
-# CHECK-NEXT: SectionHeaderOffset: 0x1110
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: ]
-# CHECK-NEXT: HeaderSize: 64
-# CHECK-NEXT: ProgramHeaderEntrySize: 56
-# CHECK-NEXT: ProgramHeaderCount: 7
-# CHECK-NEXT: SectionHeaderEntrySize: 64
-# CHECK-NEXT: SectionHeaderCount: 9
-# CHECK-NEXT: StringTableSectionIndex: 7
-# CHECK-NEXT: }
# CHECK: ProgramHeaders [
# CHECK-NEXT: ProgramHeader {
@@ -39,8 +32,8 @@
# CHECK-NEXT: Offset: 0x40
# CHECK-NEXT: VirtualAddress: 0x40
# CHECK-NEXT: PhysicalAddress: 0x40
-# CHECK-NEXT: FileSize: 392
-# CHECK-NEXT: MemSize: 392
+# CHECK-NEXT: FileSize:
+# CHECK-NEXT: MemSize:
# CHECK-NEXT: Flags [
# CHECK-NEXT: PF_R
# CHECK-NEXT: ]
@@ -51,52 +44,13 @@
# CHECK-NEXT: Offset: 0x0
# CHECK-NEXT: VirtualAddress: 0x0
# CHECK-NEXT: PhysicalAddress: 0x0
-# CHECK-NEXT: FileSize: 497
-# CHECK-NEXT: MemSize: 497
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: PF_R
-# CHECK-NEXT: ]
-# CHECK-NEXT: Alignment: 4096
-# CHECK-NEXT: }
-# CHECK-NEXT: ProgramHeader {
-# CHECK-NEXT: Type: PT_LOAD
-# CHECK-NEXT: Offset: 0x1000
-# CHECK-NEXT: VirtualAddress: 0x1000
-# CHECK-NEXT: PhysicalAddress: 0x1000
-# CHECK-NEXT: FileSize: 0
-# CHECK-NEXT: MemSize: 0
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: PF_R
-# CHECK-NEXT: PF_X
-# CHECK-NEXT: ]
-# CHECK-NEXT: Alignment: 4096
-# CHECK-NEXT: }
-# CHECK-NEXT: ProgramHeader {
-# CHECK-NEXT: Type: PT_LOAD
-# CHECK-NEXT: Offset: 0x1000
-# CHECK-NEXT: VirtualAddress: 0x1000
-# CHECK-NEXT: PhysicalAddress: 0x1000
-# CHECK-NEXT: FileSize: 112
-# CHECK-NEXT: MemSize: 112
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: PF_R
-# CHECK-NEXT: PF_W
-# CHECK-NEXT: ]
-# CHECK-NEXT: Alignment: 4096
-# CHECK-NEXT: }
-# CHECK-NEXT: ProgramHeader {
-# CHECK-NEXT: Type: PT_DYNAMIC
-# CHECK-NEXT: Offset: 0x1000
-# CHECK-NEXT: VirtualAddress: 0x1000
-# CHECK-NEXT: PhysicalAddress: 0x1000
-# CHECK-NEXT: FileSize: 112
-# CHECK-NEXT: MemSize: 112
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: PF_R
-# CHECK-NEXT: PF_W
-# CHECK-NEXT: ]
-# CHECK-NEXT: Alignment: 8
-# CHECK-NEXT: }
+
+# CHECK: Type: PT_DYNAMIC
+
+## Check -nopie
+# RUN: ld.lld -nopie %t1.o -o %t2
+# RUN: llvm-readobj -file-headers -r %t2 | FileCheck %s --check-prefix=NOPIE
+# NOPIE-NOT: Type: SharedObject
.globl _start
_start:
diff --git a/test/ELF/plt-aarch64.s b/test/ELF/plt-aarch64.s
index 3f124b0af49c..9bc6f54743aa 100644
--- a/test/ELF/plt-aarch64.s
+++ b/test/ELF/plt-aarch64.s
@@ -18,7 +18,7 @@
// CHECKDSO-NEXT: SHF_ALLOC
// CHECKDSO-NEXT: SHF_EXECINSTR
// CHECKDSO-NEXT: ]
-// CHECKDSO-NEXT: Address: 0x1010
+// CHECKDSO-NEXT: Address: 0x10010
// CHECKDSO-NEXT: Offset:
// CHECKDSO-NEXT: Size: 80
// CHECKDSO-NEXT: Link:
@@ -31,7 +31,7 @@
// CHECKDSO-NEXT: SHF_ALLOC
// CHECKDSO-NEXT: SHF_WRITE
// CHECKDSO-NEXT: ]
-// CHECKDSO-NEXT: Address: 0x3000
+// CHECKDSO-NEXT: Address: 0x30000
// CHECKDSO-NEXT: Offset:
// CHECKDSO-NEXT: Size: 48
// CHECKDSO-NEXT: Link:
@@ -41,72 +41,72 @@
// CHECKDSO: Relocations [
// CHECKDSO-NEXT: Section ({{.*}}) .rela.plt {
-// &(.got.plt[3]) = 0x3000 + 3 * 8 = 0x3018
-// CHECKDSO-NEXT: 0x3018 R_AARCH64_JUMP_SLOT foo
+// &(.got.plt[3]) = 0x30000 + 3 * 8 = 0x30018
+// CHECKDSO-NEXT: 0x30018 R_AARCH64_JUMP_SLOT foo
-// &(.got.plt[4]) = 0x3000 + 4 * 8 = 0x3020
-// CHECKDSO-NEXT: 0x3020 R_AARCH64_JUMP_SLOT bar
+// &(.got.plt[4]) = 0x30000 + 4 * 8 = 0x30020
+// CHECKDSO-NEXT: 0x30020 R_AARCH64_JUMP_SLOT bar
-// &(.got.plt[5]) = 0x3000 + 5 * 8 = 0x3028
-// CHECKDSO-NEXT: 0x3028 R_AARCH64_JUMP_SLOT weak
+// &(.got.plt[5]) = 0x30000 + 5 * 8 = 0x30028
+// CHECKDSO-NEXT: 0x30028 R_AARCH64_JUMP_SLOT weak
// CHECKDSO-NEXT: }
// CHECKDSO-NEXT: ]
// DUMPDSO: Contents of section .got.plt:
// .got.plt[0..2] = 0 (reserved)
-// .got.plt[3..5] = .plt = 0x1010
-// DUMPDSO-NEXT: 3000 00000000 00000000 00000000 00000000 ................
-// DUMPDSO-NEXT: 3010 00000000 00000000 10100000 00000000 ................
-// DUMPDSO-NEXT: 3020 10100000 00000000 10100000 00000000 ................
+// .got.plt[3..5] = .plt = 0x10010
+// DUMPDSO-NEXT: 30000 00000000 00000000 00000000 00000000 ................
+// DUMPDSO-NEXT: 30010 00000000 00000000 10000100 00000000 ................
+// DUMPDSO-NEXT: 30020 10000100 00000000 10000100 00000000 ................
// DISASMDSO: _start:
-// 0x1030 - 0x1000 = 0x30 = 48
-// DISASMDSO-NEXT: 1000: 0c 00 00 14 b #48
-// 0x1040 - 0x1004 = 0x3c = 60
-// DISASMDSO-NEXT: 1004: 0f 00 00 14 b #60
-// 0x1050 - 0x1008 = 0x48 = 72
-// DISASMDSO-NEXT: 1008: 12 00 00 14 b #72
+// 0x10030 - 0x10000 = 0x30 = 48
+// DISASMDSO-NEXT: 10000: 0c 00 00 14 b #48
+// 0x10040 - 0x10004 = 0x3c = 60
+// DISASMDSO-NEXT: 10004: 0f 00 00 14 b #60
+// 0x10050 - 0x10008 = 0x48 = 72
+// DISASMDSO-NEXT: 10008: 12 00 00 14 b #72
// DISASMDSO: foo:
-// DISASMDSO-NEXT: 100c: 1f 20 03 d5 nop
+// DISASMDSO-NEXT: 1000c: 1f 20 03 d5 nop
// DISASMDSO: Disassembly of section .plt:
// DISASMDSO-NEXT: .plt:
-// DISASMDSO-NEXT: 1010: f0 7b bf a9 stp x16, x30, [sp, #-16]!
+// DISASMDSO-NEXT: 10010: f0 7b bf a9 stp x16, x30, [sp, #-16]!
// &(.got.plt[2]) = 0x3000 + 2 * 8 = 0x3010
-// Page(0x3010) - Page(0x1014) = 0x3000 - 0x1000 = 0x2000 = 8192
-// DISASMDSO-NEXT: 1014: 10 00 00 d0 adrp x16, #8192
+// Page(0x30010) - Page(0x10014) = 0x30000 - 0x10000 = 0x20000 = 131072
+// DISASMDSO-NEXT: 10014: 10 01 00 90 adrp x16, #131072
// 0x3010 & 0xFFF = 0x10 = 16
-// DISASMDSO-NEXT: 1018: 11 0a 40 f9 ldr x17, [x16, #16]
-// DISASMDSO-NEXT: 101c: 10 42 00 91 add x16, x16, #16
-// DISASMDSO-NEXT: 1020: 20 02 1f d6 br x17
-// DISASMDSO-NEXT: 1024: 1f 20 03 d5 nop
-// DISASMDSO-NEXT: 1028: 1f 20 03 d5 nop
-// DISASMDSO-NEXT: 102c: 1f 20 03 d5 nop
+// DISASMDSO-NEXT: 10018: 11 0a 40 f9 ldr x17, [x16, #16]
+// DISASMDSO-NEXT: 1001c: 10 42 00 91 add x16, x16, #16
+// DISASMDSO-NEXT: 10020: 20 02 1f d6 br x17
+// DISASMDSO-NEXT: 10024: 1f 20 03 d5 nop
+// DISASMDSO-NEXT: 10028: 1f 20 03 d5 nop
+// DISASMDSO-NEXT: 1002c: 1f 20 03 d5 nop
// foo@plt
-// Page(0x3018) - Page(0x1030) = 0x3000 - 0x1000 = 0x2000 = 8192
-// DISASMDSO-NEXT: 1030: 10 00 00 d0 adrp x16, #8192
+// Page(0x30018) - Page(0x10030) = 0x30000 - 0x10000 = 0x20000 = 131072
+// DISASMDSO-NEXT: 10030: 10 01 00 90 adrp x16, #131072
// 0x3018 & 0xFFF = 0x18 = 24
-// DISASMDSO-NEXT: 1034: 11 0e 40 f9 ldr x17, [x16, #24]
-// DISASMDSO-NEXT: 1038: 10 62 00 91 add x16, x16, #24
-// DISASMDSO-NEXT: 103c: 20 02 1f d6 br x17
+// DISASMDSO-NEXT: 10034: 11 0e 40 f9 ldr x17, [x16, #24]
+// DISASMDSO-NEXT: 10038: 10 62 00 91 add x16, x16, #24
+// DISASMDSO-NEXT: 1003c: 20 02 1f d6 br x17
// bar@plt
-// Page(0x3020) - Page(0x1040) = 0x3000 - 0x1000 = 0x2000 = 8192
-// DISASMDSO-NEXT: 1040: 10 00 00 d0 adrp x16, #8192
+// Page(0x30020) - Page(0x10040) = 0x30000 - 0x10000 = 0x20000 = 131072
+// DISASMDSO-NEXT: 10040: 10 01 00 90 adrp x16, #131072
// 0x3020 & 0xFFF = 0x20 = 32
-// DISASMDSO-NEXT: 1044: 11 12 40 f9 ldr x17, [x16, #32]
-// DISASMDSO-NEXT: 1048: 10 82 00 91 add x16, x16, #32
-// DISASMDSO-NEXT: 104c: 20 02 1f d6 br x17
+// DISASMDSO-NEXT: 10044: 11 12 40 f9 ldr x17, [x16, #32]
+// DISASMDSO-NEXT: 10048: 10 82 00 91 add x16, x16, #32
+// DISASMDSO-NEXT: 1004c: 20 02 1f d6 br x17
// weak@plt
-// Page(0x3028) - Page(0x1050) = 0x3000 - 0x1000 = 0x2000 = 8192
-// DISASMDSO-NEXT: 1050: 10 00 00 d0 adrp x16, #8192
+// Page(0x30028) - Page(0x10050) = 0x30000 - 0x10000 = 0x20000 = 131072
+// DISASMDSO-NEXT: 10050: 10 01 00 90 adrp x16, #131072
// 0x3028 & 0xFFF = 0x28 = 40
-// DISASMDSO-NEXT: 1054: 11 16 40 f9 ldr x17, [x16, #40]
-// DISASMDSO-NEXT: 1058: 10 a2 00 91 add x16, x16, #40
-// DISASMDSO-NEXT: 105c: 20 02 1f d6 br x17
+// DISASMDSO-NEXT: 10054: 11 16 40 f9 ldr x17, [x16, #40]
+// DISASMDSO-NEXT: 10058: 10 a2 00 91 add x16, x16, #40
+// DISASMDSO-NEXT: 1005c: 20 02 1f d6 br x17
// CHECKEXE: Name: .plt
// CHECKEXE-NEXT: Type: SHT_PROGBITS
@@ -114,7 +114,7 @@
// CHECKEXE-NEXT: SHF_ALLOC
// CHECKEXE-NEXT: SHF_EXECINSTR
// CHECKEXE-NEXT: ]
-// CHECKEXE-NEXT: Address: 0x11010
+// CHECKEXE-NEXT: Address: 0x20010
// CHECKEXE-NEXT: Offset:
// CHECKEXE-NEXT: Size: 64
// CHECKEXE-NEXT: Link:
@@ -127,7 +127,7 @@
// CHECKEXE-NEXT: SHF_ALLOC
// CHECKEXE-NEXT: SHF_WRITE
// CHECKEXE-NEXT: ]
-// CHECKEXE-NEXT: Address: 0x13000
+// CHECKEXE-NEXT: Address: 0x40000
// CHECKEXE-NEXT: Offset:
// CHECKEXE-NEXT: Size: 40
// CHECKEXE-NEXT: Link:
@@ -138,60 +138,58 @@
// CHECKEXE-NEXT: Section ({{.*}}) .rela.plt {
// &(.got.plt[3]) = 0x13000 + 3 * 8 = 0x13018
-// CHECKEXE-NEXT: 0x13018 R_AARCH64_JUMP_SLOT bar 0x0
+// CHECKEXE-NEXT: 0x40018 R_AARCH64_JUMP_SLOT bar 0x0
// &(.got.plt[4]) = 0x13000 + 4 * 8 = 0x13020
-// CHECKEXE-NEXT: 0x13020 R_AARCH64_JUMP_SLOT weak 0x0
+// CHECKEXE-NEXT: 0x40020 R_AARCH64_JUMP_SLOT weak 0x0
// CHECKEXE-NEXT: }
// CHECKEXE-NEXT: ]
// DUMPEXE: Contents of section .got.plt:
// .got.plt[0..2] = 0 (reserved)
-// .got.plt[3..4] = .plt = 0x11010
-// DUMPEXE-NEXT: 13000 00000000 00000000 00000000 00000000 ................
-// DUMPEXE-NEXT: 13010 00000000 00000000 10100100 00000000 ................
-// DUMPEXE-NEXT: 13020 10100100 00000000 ........
+// .got.plt[3..4] = .plt = 0x40010
+// DUMPEXE-NEXT: 40000 00000000 00000000 00000000 00000000 ................
+// DUMPEXE-NEXT: 40010 00000000 00000000 10000200 00000000 ................
+// DUMPEXE-NEXT: 40020 10000200 00000000 ........
// DISASMEXE: _start:
-// 0x1100c - 0x11000 = 0xc = 12
-// DISASMEXE-NEXT: 11000: 03 00 00 14 b #12
-// 0x11030 - 0x11004 = 0x2c = 44
-// DISASMEXE-NEXT: 11004: 0b 00 00 14 b #44
-// 0x11040 - 0x11008 = 0x38 = 56
-// DISASMEXE-NEXT: 11008: 0e 00 00 14 b #56
+// 0x2000c - 0x20000 = 0xc = 12
+// DISASMEXE-NEXT: 20000: 03 00 00 14 b #12
+// 0x20030 - 0x20004 = 0x2c = 44
+// DISASMEXE-NEXT: 20004: 0b 00 00 14 b #44
+// 0x20040 - 0x20008 = 0x38 = 56
+// DISASMEXE-NEXT: 20008: 0e 00 00 14 b #56
// DISASMEXE: foo:
-// DISASMEXE-NEXT: 1100c: 1f 20 03 d5 nop
+// DISASMEXE-NEXT: 2000c: 1f 20 03 d5 nop
// DISASMEXE: Disassembly of section .plt:
// DISASMEXE-NEXT: .plt:
-// DISASMEXE-NEXT: 11010: f0 7b bf a9 stp x16, x30, [sp, #-16]!
-// &(.got.plt[2]) = 0x120B0 + 2 * 8 = 0x120C0
-// Page(0x13010) - Page(0x11014) = 0x13000 - 0x11000 = 0x1000 = 8192
-// DISASMEXE-NEXT: 11014: 10 00 00 d0 adrp x16, #8192
+// DISASMEXE-NEXT: 20010: f0 7b bf a9 stp x16, x30, [sp, #-16]!
+// &(.got.plt[2]) = 0x300B0 + 2 * 8 = 0x300C0
+// Page(0x40010) - Page(0x20014) = 0x40000 - 0x20000 = 0x20000 = 131072
+// DISASMEXE-NEXT: 20014: 10 01 00 90 adrp x16, #131072
// 0x120c0 & 0xFFF = 0xC0 = 192
-// DISASMEXE-NEXT: 11018: 11 0a 40 f9 ldr x17, [x16, #16]
-// DISASMEXE-NEXT: 1101c: 10 42 00 91 add x16, x16, #16
-// DISASMEXE-NEXT: 11020: 20 02 1f d6 br x17
-// DISASMEXE-NEXT: 11024: 1f 20 03 d5 nop
-// DISASMEXE-NEXT: 11028: 1f 20 03 d5 nop
-// DISASMEXE-NEXT: 1102c: 1f 20 03 d5 nop
+// DISASMEXE-NEXT: 20018: 11 0a 40 f9 ldr x17, [x16, #16]
+// DISASMEXE-NEXT: 2001c: 10 42 00 91 add x16, x16, #16
+// DISASMEXE-NEXT: 20020: 20 02 1f d6 br x17
+// DISASMEXE-NEXT: 20024: 1f 20 03 d5 nop
+// DISASMEXE-NEXT: 20028: 1f 20 03 d5 nop
+// DISASMEXE-NEXT: 2002c: 1f 20 03 d5 nop
// bar@plt
-// Page(0x13018) - Page(0x11030) = 0x12000 - 0x11000 = 0x1000 = 8192
-// DISASMEXE-NEXT: 11030: 10 00 00 d0 adrp x16, #8192
-// 0x120C8 & 0xFFF = 0xC8 = 200
-// DISASMEXE-NEXT: 11034: 11 0e 40 f9 ldr x17, [x16, #24]
-// DISASMEXE-NEXT: 11038: 10 62 00 91 add x16, x16, #24
-// DISASMEXE-NEXT: 1103c: 20 02 1f d6 br x17
+// Page(0x40018) - Page(0x20030) = 0x40000 - 0x20000 = 0x20000 = 131072
+// DISASMEXE-NEXT: 20030: 10 01 00 90 adrp x16, #131072
+// DISASMEXE-NEXT: 20034: 11 0e 40 f9 ldr x17, [x16, #24]
+// DISASMEXE-NEXT: 20038: 10 62 00 91 add x16, x16, #24
+// DISASMEXE-NEXT: 2003c: 20 02 1f d6 br x17
// weak@plt
-// Page(0x13020) - Page(0x11040) = 0x12000 - 0x11000 = 0x1000 = 8192
-// DISASMEXE-NEXT: 11040: 10 00 00 d0 adrp x16, #8192
-// 0x120D0 & 0xFFF = 0xD0 = 208
-// DISASMEXE-NEXT: 11044: 11 12 40 f9 ldr x17, [x16, #32]
-// DISASMEXE-NEXT: 11048: 10 82 00 91 add x16, x16, #32
-// DISASMEXE-NEXT: 1104c: 20 02 1f d6 br x17
+// Page(0x40020) - Page(0x20040) = 0x40000 - 0x20000 = 0x20000 = 131072
+// DISASMEXE-NEXT: 20040: 10 01 00 90 adrp x16, #131072
+// DISASMEXE-NEXT: 20044: 11 12 40 f9 ldr x17, [x16, #32]
+// DISASMEXE-NEXT: 20048: 10 82 00 91 add x16, x16, #32
+// DISASMEXE-NEXT: 2004c: 20 02 1f d6 br x17
.global _start,foo,bar
.weak weak
diff --git a/test/ELF/plt.s b/test/ELF/plt.s
index 60268a68195a..0153e82786ae 100644
--- a/test/ELF/plt.s
+++ b/test/ELF/plt.s
@@ -37,7 +37,7 @@
// CHECK2-NEXT: SHF_ALLOC
// CHECK2-NEXT: SHF_EXECINSTR
// CHECK2-NEXT: ]
-// CHECK2-NEXT: Address: 0x11020
+// CHECK2-NEXT: Address: 0x201020
// CHECK2-NEXT: Offset:
// CHECK2-NEXT: Size: 48
// CHECK2-NEXT: Link: 0
@@ -46,8 +46,8 @@
// CHECK2: Relocations [
// CHECK2-NEXT: Section ({{.*}}) .rela.plt {
-// CHECK2-NEXT: 0x13018 R_X86_64_JUMP_SLOT bar 0x0
-// CHECK2-NEXT: 0x13020 R_X86_64_JUMP_SLOT zed 0x0
+// CHECK2-NEXT: 0x203018 R_X86_64_JUMP_SLOT bar 0x0
+// CHECK2-NEXT: 0x203020 R_X86_64_JUMP_SLOT zed 0x0
// CHECK2-NEXT: }
// CHECK2-NEXT: ]
@@ -84,32 +84,32 @@
// DISASM-NEXT: 1056: 68 02 00 00 00 pushq $2
// DISASM-NEXT: 105b: e9 c0 ff ff ff jmp -64 <.plt>
-// 0x11030 - (0x11000 + 1) - 4 = 43
-// 0x11030 - (0x11005 + 1) - 4 = 38
-// 0x11040 - (0x1100a + 1) - 4 = 49
-// 0x11000 - (0x1100f + 1) - 4 = -20
+// 0x201030 - (0x201000 + 1) - 4 = 43
+// 0x201030 - (0x201005 + 1) - 4 = 38
+// 0x201040 - (0x20100a + 1) - 4 = 49
+// 0x201000 - (0x20100f + 1) - 4 = -20
// DISASM2: _start:
-// DISASM2-NEXT: 11000: e9 {{.*}} jmp 43
-// DISASM2-NEXT: 11005: e9 {{.*}} jmp 38
-// DISASM2-NEXT: 1100a: e9 {{.*}} jmp 49
-// DISASM2-NEXT: 1100f: e9 {{.*}} jmp -20
+// DISASM2-NEXT: 201000: e9 {{.*}} jmp 43
+// DISASM2-NEXT: 201005: e9 {{.*}} jmp 38
+// DISASM2-NEXT: 20100a: e9 {{.*}} jmp 49
+// DISASM2-NEXT: 20100f: e9 {{.*}} jmp -20
-// 0x13018 - 0x11036 = 4242
-// 0x13020 - 0x11046 = 4234
+// 0x203018 - 0x201036 = 4242
+// 0x203020 - 0x201046 = 4234
// DISASM2: Disassembly of section .plt:
// DISASM2-NEXT: .plt:
-// DISASM2-NEXT: 11020: ff 35 e2 1f 00 00 pushq 8162(%rip)
-// DISASM2-NEXT: 11026: ff 25 e4 1f 00 00 jmpq *8164(%rip)
-// DISASM2-NEXT: 1102c: 0f 1f 40 00 nopl (%rax)
-// DISASM2-NEXT: 11030: ff 25 e2 1f 00 00 jmpq *8162(%rip)
-// DISASM2-NEXT: 11036: 68 00 00 00 00 pushq $0
-// DISASM2-NEXT: 1103b: e9 e0 ff ff ff jmp -32 <.plt>
-// DISASM2-NEXT: 11040: ff 25 da 1f 00 00 jmpq *8154(%rip)
-// DISASM2-NEXT: 11046: 68 01 00 00 00 pushq $1
-// DISASM2-NEXT: 1104b: e9 d0 ff ff ff jmp -48 <.plt>
-// DISASM2-NOT: 110C0
+// DISASM2-NEXT: 201020: ff 35 e2 1f 00 00 pushq 8162(%rip)
+// DISASM2-NEXT: 201026: ff 25 e4 1f 00 00 jmpq *8164(%rip)
+// DISASM2-NEXT: 20102c: 0f 1f 40 00 nopl (%rax)
+// DISASM2-NEXT: 201030: ff 25 e2 1f 00 00 jmpq *8162(%rip)
+// DISASM2-NEXT: 201036: 68 00 00 00 00 pushq $0
+// DISASM2-NEXT: 20103b: e9 e0 ff ff ff jmp -32 <.plt>
+// DISASM2-NEXT: 201040: ff 25 da 1f 00 00 jmpq *8154(%rip)
+// DISASM2-NEXT: 201046: 68 01 00 00 00 pushq $1
+// DISASM2-NEXT: 20104b: e9 d0 ff ff ff jmp -48 <.plt>
+// DISASM2-NOT: 2010C0
.global _start
_start:
diff --git a/test/ELF/ppc-relocs.s b/test/ELF/ppc-relocs.s
index a70cac1615e0..78542dd64a4c 100644
--- a/test/ELF/ppc-relocs.s
+++ b/test/ELF/ppc-relocs.s
@@ -28,3 +28,37 @@ mystr:
# CHECK: 11008: 38 84 10 04 addi 4, 4, 4100
# CHECK: mystr:
# CHECK: 1100c: 62 6c 61 68 ori 12, 19, 24936
+
+.align 2
+.section .R_PPC_REL24,"ax",@progbits
+.globl .FR_PPC_REL24
+.FR_PPC_REL24:
+ b .Lfoox
+.section .R_PPC_REL24_2,"ax",@progbits
+.Lfoox:
+
+# CHECK: Disassembly of section .R_PPC_REL24:
+# CHECK: .FR_PPC_REL24:
+# CHECK: 11014: 48 00 00 04 b .+4
+
+.section .R_PPC_REL32,"ax",@progbits
+.globl .FR_PPC_REL32
+.FR_PPC_REL32:
+ .long .Lfoox3 - .
+.section .R_PPC_REL32_2,"ax",@progbits
+.Lfoox3:
+
+# CHECK: Disassembly of section .R_PPC_REL32:
+# CHECK: .FR_PPC_REL32:
+# CHECK: 11018: 00 00 00 04
+
+.section .R_PPC_ADDR32,"ax",@progbits
+.globl .FR_PPC_ADDR32
+.FR_PPC_ADDR32:
+ .long .Lfoox2
+.section .R_PPC_ADDR32_2,"ax",@progbits
+.Lfoox2:
+
+# CHECK: Disassembly of section .R_PPC_ADDR32:
+# CHECK: .FR_PPC_ADDR32:
+# CHECK: 1101c: 00 01 10 20
diff --git a/test/ELF/pre_init_fini_array.s b/test/ELF/pre_init_fini_array.s
index 4ddcb6947e5b..1192fd0dc851 100644
--- a/test/ELF/pre_init_fini_array.s
+++ b/test/ELF/pre_init_fini_array.s
@@ -63,7 +63,7 @@ _start:
// CHECK-NEXT: ]
// CHECK: Name: __fini_array_end
-// CHECK-NEXT: Value: 0x1201B
+// CHECK-NEXT: Value: 0x20201B
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -85,7 +85,7 @@ _start:
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: __init_array_end
-// CHECK-NEXT: Value: 0x12008
+// CHECK-NEXT: Value: 0x202008
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -107,7 +107,7 @@ _start:
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: __preinit_array_end
-// CHECK-NEXT: Value: 0x12011
+// CHECK-NEXT: Value: 0x202011
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -137,16 +137,16 @@ _start:
// CHECK: FINI_ARRAYSZ [[FINI_SIZE]] (bytes)
-// 0x12008 - (0x11000 + 5) = 4099
-// 0x12011 - (0x11005 + 5) = 4103
-// 0x12000 - (0x1100a + 5) = 4081
-// 0x12008 - (0x1100f + 5) = 4084
-// 0x12011 - (0x11014 + 5) = 4088
-// 0x1201B - (0x11019 + 5) = 4093
+// 0x202008 - (0x201000 + 5) = 4099
+// 0x202011 - (0x201005 + 5) = 4103
+// 0x202000 - (0x20100a + 5) = 4081
+// 0x202008 - (0x20100f + 5) = 4084
+// 0x202011 - (0x201014 + 5) = 4088
+// 0x20201B - (0x201019 + 5) = 4093
// DISASM: _start:
-// DISASM-NEXT: 11000: e8 {{.*}} callq 4099
-// DISASM-NEXT: 11005: e8 {{.*}} callq 4103
-// DISASM-NEXT: 1100a: e8 {{.*}} callq 4081
-// DISASM-NEXT: 1100f: e8 {{.*}} callq 4084
-// DISASM-NEXT: 11014: e8 {{.*}} callq 4088
-// DISASM-NEXT: 11019: e8 {{.*}} callq 4093
+// DISASM-NEXT: 201000: e8 {{.*}} callq 4099
+// DISASM-NEXT: 201005: e8 {{.*}} callq 4103
+// DISASM-NEXT: 20100a: e8 {{.*}} callq 4081
+// DISASM-NEXT: 20100f: e8 {{.*}} callq 4084
+// DISASM-NEXT: 201014: e8 {{.*}} callq 4088
+// DISASM-NEXT: 201019: e8 {{.*}} callq 4093
diff --git a/test/ELF/pre_init_fini_array_missing.s b/test/ELF/pre_init_fini_array_missing.s
index 02b83240193e..de976805d76c 100644
--- a/test/ELF/pre_init_fini_array_missing.s
+++ b/test/ELF/pre_init_fini_array_missing.s
@@ -13,18 +13,18 @@ _start:
call __fini_array_end
// With no .init_array section the symbols resolve to 0
-// 0 - (0x11000 + 5) = -69637
-// 0 - (0x11005 + 5) = -69642
-// 0 - (0x1100a + 5) = -69647
-// 0 - (0x1100f + 5) = -69652
-// 0 - (0x11014 + 5) = -69657
-// 0 - (0x11019 + 5) = -69662
+// 0 - (0x201000 + 5) = -2101253
+// 0 - (0x201005 + 5) = -2101258
+// 0 - (0x20100a + 5) = -2101263
+// 0 - (0x20100f + 5) = -2101268
+// 0 - (0x201014 + 5) = -2101273
+// 0 - (0x201019 + 5) = -2101278
// CHECK: Disassembly of section .text:
// CHECK-NEXT: _start:
-// CHECK-NEXT: 11000: e8 fb ef fe ff callq -69637
-// CHECK-NEXT: 11005: e8 f6 ef fe ff callq -69642
-// CHECK-NEXT: 1100a: e8 f1 ef fe ff callq -69647
-// CHECK-NEXT: 1100f: e8 ec ef fe ff callq -69652
-// CHECK-NEXT: 11014: e8 e7 ef fe ff callq -69657
-// CHECK-NEXT: 11019: e8 e2 ef fe ff callq -69662
+// CHECK-NEXT: 201000: e8 fb ef df ff callq -2101253
+// CHECK-NEXT: 201005: e8 f6 ef df ff callq -2101258
+// CHECK-NEXT: 20100a: e8 f1 ef df ff callq -2101263
+// CHECK-NEXT: 20100f: e8 ec ef df ff callq -2101268
+// CHECK-NEXT: 201014: e8 e7 ef df ff callq -2101273
+// CHECK-NEXT: 201019: e8 e2 ef df ff callq -2101278
diff --git a/test/ELF/program-header-layout.s b/test/ELF/program-header-layout.s
index 8e32227ac741..57759c946dac 100644
--- a/test/ELF/program-header-layout.s
+++ b/test/ELF/program-header-layout.s
@@ -35,8 +35,8 @@ _start:
# CHECK-NEXT: ProgramHeader {
# CHECK-NEXT: Type: PT_PHDR (0x6)
# CHECK-NEXT: Offset: 0x40
-# CHECK-NEXT: VirtualAddress: 0x10040
-# CHECK-NEXT: PhysicalAddress: 0x10040
+# CHECK-NEXT: VirtualAddress: 0x200040
+# CHECK-NEXT: PhysicalAddress: 0x200040
# CHECK-NEXT: FileSize: 280
# CHECK-NEXT: MemSize: 280
# CHECK-NEXT: Flags [ (0x4)
@@ -61,19 +61,6 @@ _start:
# CHECK-NEXT: Offset:
# CHECK-NEXT: VirtualAddress:
# CHECK-NEXT: PhysicalAddress:
-# CHECK-NEXT: FileSize: 0
-# CHECK-NEXT: MemSize: 0
-# 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:
-# CHECK-NEXT: VirtualAddress:
-# CHECK-NEXT: PhysicalAddress:
# CHECK-NEXT: FileSize: 16
# CHECK-NEXT: MemSize: 16
# CHECK-NEXT: Flags [
diff --git a/test/ELF/relative-dynamic-reloc-ppc64.s b/test/ELF/relative-dynamic-reloc-ppc64.s
index 81a7a7027bb8..1df974b087c9 100644
--- a/test/ELF/relative-dynamic-reloc-ppc64.s
+++ b/test/ELF/relative-dynamic-reloc-ppc64.s
@@ -28,7 +28,7 @@
// CHECK: DynamicSymbols [
// CHECK-NEXT: Symbol {
-// CHECK-NEXT: Name: @ (0)
+// CHECK-NEXT: Name: @
// CHECK-NEXT: Value: 0x0
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
diff --git a/test/ELF/relative-dynamic-reloc.s b/test/ELF/relative-dynamic-reloc.s
index aac55669bb17..947c86d89024 100644
--- a/test/ELF/relative-dynamic-reloc.s
+++ b/test/ELF/relative-dynamic-reloc.s
@@ -27,7 +27,7 @@
// CHECK: DynamicSymbols [
// CHECK-NEXT: Symbol {
-// CHECK-NEXT: Name: @ (0)
+// CHECK-NEXT: Name: @
// CHECK-NEXT: Value: 0x0
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
diff --git a/test/ELF/relocatable-comment.s b/test/ELF/relocatable-comment.s
new file mode 100644
index 000000000000..8ec22c2f43de
--- /dev/null
+++ b/test/ELF/relocatable-comment.s
@@ -0,0 +1,27 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o
+# RUN: ld.lld -r %t1.o -o %t
+# RUN: llvm-readobj -s -section-data %t | FileCheck %s
+
+# CHECK: Name: .comment
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_MERGE
+# CHECK-NEXT: SHF_STRINGS
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 7
+# CHECK-NEXT: Link:
+# CHECK-NEXT: Info:
+# CHECK-NEXT: AddressAlignment: 1
+# CHECK-NEXT: EntrySize: 1
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 666F6F62 617200 |foobar.|
+# CHECK-NEXT: )
+
+
+# We used to crash creating a merge and non merge .comment sections.
+
+ .section .comment,"MS",@progbits,1
+ .asciz "foobar"
diff --git a/test/ELF/relocatable-local-sym.s b/test/ELF/relocatable-local-sym.s
new file mode 100644
index 000000000000..b894d6b9900c
--- /dev/null
+++ b/test/ELF/relocatable-local-sym.s
@@ -0,0 +1,16 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o
+# RUN: ld.lld -r %t1.o -o %t2.o
+# RUN: llvm-readobj -r %t2.o | FileCheck %s
+
+# CHECK: Relocations [
+# CHECK-NEXT: Section ({{.*}}) .rela.text {
+# CHECK-NEXT: 0x3 R_X86_64_PC32 .Lstr 0xFFFFFFFFFFFFFFFC
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+ leaq .Lstr(%rip), %rdi
+
+ .section .rodata.str1.1,"aMS",@progbits,1
+ .Lstr:
+ .asciz "abc\n"
diff --git a/test/ELF/relocatable-sections.s b/test/ELF/relocatable-sections.s
new file mode 100644
index 000000000000..d6a922fba482
--- /dev/null
+++ b/test/ELF/relocatable-sections.s
@@ -0,0 +1,30 @@
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
+# RUN: ld.lld -r %t1.o -o %t
+# RUN: llvm-objdump -section-headers %t | FileCheck %s
+
+# CHECK: .text
+# CHECK-NEXT: .rela.text
+# CHECK: .text._init
+# CHECK-NEXT: .rela.text._init
+# CHECK: .text._fini
+# CHECK-NEXT: .rela.text._fini
+
+.globl _start
+_start:
+ call foo
+ nop
+
+.section .xxx,"a"
+ .quad 0
+
+.section .text._init,"ax"
+ .quad .xxx
+foo:
+ call bar
+ nop
+
+
+.section .text._fini,"ax"
+ .quad .xxx
+bar:
+ nop
diff --git a/test/ELF/relocatable-symbols.s b/test/ELF/relocatable-symbols.s
index 75ed17ec5f6f..79608e7b74f0 100644
--- a/test/ELF/relocatable-symbols.s
+++ b/test/ELF/relocatable-symbols.s
@@ -2,6 +2,7 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
# RUN: ld.lld -r %t -o %tout
# RUN: llvm-objdump -d %tout | FileCheck -check-prefix=DISASM %s
+# RUN: llvm-readobj -r %t | FileCheck -check-prefix=RELOC %s
# RUN: llvm-readobj -symbols -r %tout | FileCheck -check-prefix=SYMBOL %s
# DISASM: _start:
@@ -28,20 +29,37 @@
# DISASM-NEXT: 1: 90 nop
# DISASM-NEXT: 2: 90 nop
+# RELOC: Relocations [
+# RELOC-NEXT: Section ({{.*}}) .rela.text {
+# RELOC-NEXT: 0x1 R_X86_64_PC32 __start_foo 0xFFFFFFFFFFFFFFFC
+# RELOC-NEXT: 0x6 R_X86_64_PC32 __stop_foo 0xFFFFFFFFFFFFFFFC
+# RELOC-NEXT: 0xB R_X86_64_PC32 __start_bar 0xFFFFFFFFFFFFFFFC
+# RELOC-NEXT: 0x10 R_X86_64_PC32 __stop_bar 0xFFFFFFFFFFFFFFFC
+# RELOC-NEXT: 0x15 R_X86_64_PC32 __start_doo 0xFFFFFFFFFFFFFFFC
+# RELOC-NEXT: 0x1A R_X86_64_PC32 __stop_doo 0xFFFFFFFFFFFFFFFC
+# RELOC-NEXT: 0x1F R_X86_64_PC32 __preinit_array_start 0xFFFFFFFFFFFFFFFC
+# RELOC-NEXT: 0x24 R_X86_64_PC32 __preinit_array_end 0xFFFFFFFFFFFFFFFC
+# RELOC-NEXT: 0x29 R_X86_64_PC32 __init_array_start 0xFFFFFFFFFFFFFFFC
+# RELOC-NEXT: 0x2E R_X86_64_PC32 __init_array_end 0xFFFFFFFFFFFFFFFC
+# RELOC-NEXT: 0x33 R_X86_64_PC32 __fini_array_start 0xFFFFFFFFFFFFFFFC
+# RELOC-NEXT: 0x38 R_X86_64_PC32 __fini_array_end 0xFFFFFFFFFFFFFFFC
+# RELOC-NEXT: }
+# RELOC-NEXT: ]
+
# SYMBOL: Relocations [
# SYMBOL-NEXT: Section ({{.*}}) .rela.text {
-# SYMBOL-NEXT: 0x1 R_X86_64_PC32 __start_foo 0x0
-# SYMBOL-NEXT: 0x6 R_X86_64_PC32 __stop_foo 0x0
-# SYMBOL-NEXT: 0xB R_X86_64_PC32 __start_bar 0x0
-# SYMBOL-NEXT: 0x10 R_X86_64_PC32 __stop_bar 0x0
-# SYMBOL-NEXT: 0x15 R_X86_64_PC32 __start_doo 0x0
-# SYMBOL-NEXT: 0x1A R_X86_64_PC32 __stop_doo 0x0
-# SYMBOL-NEXT: 0x1F R_X86_64_PC32 __preinit_array_start 0x0
-# SYMBOL-NEXT: 0x24 R_X86_64_PC32 __preinit_array_end 0x0
-# SYMBOL-NEXT: 0x29 R_X86_64_PC32 __init_array_start 0x0
-# SYMBOL-NEXT: 0x2E R_X86_64_PC32 __init_array_end 0x0
-# SYMBOL-NEXT: 0x33 R_X86_64_PC32 __fini_array_start 0x0
-# SYMBOL-NEXT: 0x38 R_X86_64_PC32 __fini_array_end 0x0
+# SYMBOL-NEXT: 0x1 R_X86_64_PC32 __start_foo 0xFFFFFFFFFFFFFFFC
+# SYMBOL-NEXT: 0x6 R_X86_64_PC32 __stop_foo 0xFFFFFFFFFFFFFFFC
+# SYMBOL-NEXT: 0xB R_X86_64_PC32 __start_bar 0xFFFFFFFFFFFFFFFC
+# SYMBOL-NEXT: 0x10 R_X86_64_PC32 __stop_bar 0xFFFFFFFFFFFFFFFC
+# SYMBOL-NEXT: 0x15 R_X86_64_PC32 __start_doo 0xFFFFFFFFFFFFFFFC
+# SYMBOL-NEXT: 0x1A R_X86_64_PC32 __stop_doo 0xFFFFFFFFFFFFFFFC
+# SYMBOL-NEXT: 0x1F R_X86_64_PC32 __preinit_array_start 0xFFFFFFFFFFFFFFFC
+# SYMBOL-NEXT: 0x24 R_X86_64_PC32 __preinit_array_end 0xFFFFFFFFFFFFFFFC
+# SYMBOL-NEXT: 0x29 R_X86_64_PC32 __init_array_start 0xFFFFFFFFFFFFFFFC
+# SYMBOL-NEXT: 0x2E R_X86_64_PC32 __init_array_end 0xFFFFFFFFFFFFFFFC
+# SYMBOL-NEXT: 0x33 R_X86_64_PC32 __fini_array_start 0xFFFFFFFFFFFFFFFC
+# SYMBOL-NEXT: 0x38 R_X86_64_PC32 __fini_array_end 0xFFFFFFFFFFFFFFFC
# SYMBOL-NEXT: }
# SYMBOL-NEXT: ]
# SYMBOL: Symbol {
diff --git a/test/ELF/relocatable-tls.s b/test/ELF/relocatable-tls.s
new file mode 100644
index 000000000000..88d38c2ae4b4
--- /dev/null
+++ b/test/ELF/relocatable-tls.s
@@ -0,0 +1,15 @@
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \
+# RUN: %S/Inputs/relocatable-tls.s -o %t2.o
+
+# RUN: ld.lld -r %t2.o -o %t3.r
+# RUN: llvm-objdump -t %t3.r | FileCheck --check-prefix=RELOCATABLE %s
+# RELOCATABLE: SYMBOL TABLE:
+# RELOCATABLE: 0000000000000000 *UND* 00000000 __tls_get_addr
+
+# RUN: ld.lld -shared %t2.o %t3.r -o %t4.out
+# RUN: llvm-objdump -t %t4.out | FileCheck --check-prefix=DSO %s
+# DSO: SYMBOL TABLE:
+# DSO: 0000000000000000 *UND* 00000000 __tls_get_addr
+
+callq __tls_get_addr@PLT
diff --git a/test/ELF/relocatable-visibility.s b/test/ELF/relocatable-visibility.s
new file mode 100644
index 000000000000..7cc7da3097bb
--- /dev/null
+++ b/test/ELF/relocatable-visibility.s
@@ -0,0 +1,19 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: ld.lld -r %t.o -o %t1
+# RUN: llvm-readobj -t %t1 | FileCheck --check-prefix=RELOCATABLE %s
+
+# RELOCATABLE: Name: foo
+# RELOCATABLE-NEXT: Value: 0x0
+# RELOCATABLE-NEXT: Size: 0
+# RELOCATABLE-NEXT: Binding: Global
+# RELOCATABLE-NEXT: Type: None
+# RELOCATABLE-NEXT: Other [
+# RELOCATABLE-NEXT: STV_HIDDEN
+# RELOCATABLE-NEXT: ]
+# RELOCATABLE-NEXT: Section: Undefined
+
+.global _start
+_start:
+ callq foo
+ .hidden foo
diff --git a/test/ELF/relocatable.s b/test/ELF/relocatable.s
index 032cb6336c08..14e9d605892e 100644
--- a/test/ELF/relocatable.s
+++ b/test/ELF/relocatable.s
@@ -42,7 +42,7 @@
# CHECK-NEXT: }
# CHECK: Relocations [
-# CHECK-NEXT: Section (3) .rela.text {
+# CHECK-NEXT: Section ({{.*}}) .rela.text {
# CHECK-NEXT: 0x3 R_X86_64_32S x 0x0
# CHECK-NEXT: 0xE R_X86_64_32S y 0x0
# CHECK-NEXT: 0x23 R_X86_64_32S xx 0x0
@@ -79,17 +79,17 @@
# CHECKEXE-NEXT: Type: Executable
# CHECKEXE-NEXT: Machine: EM_X86_64
# CHECKEXE-NEXT: Version: 1
-# CHECKEXE-NEXT: Entry: 0x11000
+# CHECKEXE-NEXT: Entry: 0x201000
# CHECKEXE-NEXT: ProgramHeaderOffset: 0x40
-# CHECKEXE-NEXT: SectionHeaderOffset: 0x11E8
+# CHECKEXE-NEXT: SectionHeaderOffset: 0x11F8
# CHECKEXE-NEXT: Flags [
# CHECKEXE-NEXT: ]
# CHECKEXE-NEXT: HeaderSize: 64
# CHECKEXE-NEXT: ProgramHeaderEntrySize: 56
# CHECKEXE-NEXT: ProgramHeaderCount: 5
# CHECKEXE-NEXT: SectionHeaderEntrySize: 64
-# CHECKEXE-NEXT: SectionHeaderCount: 6
-# CHECKEXE-NEXT: StringTableSectionIndex: 4
+# CHECKEXE-NEXT: SectionHeaderCount: 7
+# CHECKEXE-NEXT: StringTableSectionIndex: 5
# CHECKEXE-NEXT: }
.text
diff --git a/test/ELF/relocation-common.s b/test/ELF/relocation-common.s
index d5d379a355aa..28276bfe0750 100644
--- a/test/ELF/relocation-common.s
+++ b/test/ELF/relocation-common.s
@@ -10,5 +10,5 @@ _start:
.global sym1
.comm sym1,4,4
-// CHECK: 11000: {{.*}} movl $1, 4086(%rip)
-// CHECK: 0000000000012000 g .bss 00000004 sym1
+// CHECK: 201000: {{.*}} movl $1, 4086(%rip)
+// CHECK: 0000000000202000 g .bss 00000004 sym1
diff --git a/test/ELF/relocation-copy-flags.s b/test/ELF/relocation-copy-flags.s
index 50b4b15267e6..069866865c27 100644
--- a/test/ELF/relocation-copy-flags.s
+++ b/test/ELF/relocation-copy-flags.s
@@ -22,7 +22,7 @@ _start:
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_EXECINSTR
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x11000
+// CHECK-NEXT: Address: 0x201000
// CHECK-NEXT: Offset: 0x1000
// CHECK-NEXT: Size: 8
// CHECK-NEXT: Link: 0
@@ -30,7 +30,7 @@ _start:
// CHECK-NEXT: AddressAlignment: 4
// CHECK-NEXT: EntrySize: 0
// CHECK-NEXT: SectionData (
-// CHECK-NEXT: 0000: 10300100
+// CHECK-NEXT: 0000: 10302000
// CHECK-NEXT: )
// CHECK: Name: bar
@@ -39,7 +39,7 @@ _start:
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x13000
+// CHECK-NEXT: Address: 0x203000
// CHECK-NEXT: Offset: 0x3000
// CHECK-NEXT: Size: 8
// CHECK-NEXT: Link: 0
@@ -67,7 +67,7 @@ _start:
// CHECK: Relocations [
// CHECK-NEXT: Section (4) .rela.dyn {
-// CHECK-NEXT: 0x13010 R_X86_64_COPY x 0x0
-// CHECK-NEXT: 0x13000 R_X86_64_64 z 0x0
+// CHECK-NEXT: 0x203010 R_X86_64_COPY x 0x0
+// CHECK-NEXT: 0x203000 R_X86_64_64 z 0x0
// CHECK-NEXT: }
// CHECK-NEXT: ]
diff --git a/test/ELF/relocation-copy.s b/test/ELF/relocation-copy.s
index 9d13241f5496..9a3254fb2549 100644
--- a/test/ELF/relocation-copy.s
+++ b/test/ELF/relocation-copy.s
@@ -22,7 +22,7 @@ movl $z, %edx
// CHECK-NEXT: SHF_ALLOC (0x2)
// CHECK-NEXT: SHF_WRITE (0x1)
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x13000
+// CHECK-NEXT: Address: 0x203000
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 24
// CHECK-NEXT: Link: 0
@@ -53,15 +53,15 @@ movl $z, %edx
// CHECK-NEXT: }
// CHECK-NEXT: ]
-// 77824 = 0x13000
+// 2109440 = 0x203000
// 16 is alignment here
-// 77840 = 0x13000 + 16
-// 77844 = 0x13000 + 16 + 4
+// 2109456 = 0x203000 + 16
+// 2109460 = 0x203000 + 16 + 4
// CODE: Disassembly of section .text:
// CODE-NEXT: _start:
-// CODE-NEXT: 11000: c7 04 25 00 30 01 00 05 00 00 00 movl $5, 77824
-// CODE-NEXT: 1100b: c7 04 25 10 30 01 00 07 00 00 00 movl $7, 77840
-// CODE-NEXT: 11016: c7 04 25 14 30 01 00 09 00 00 00 movl $9, 77844
-// CODE-NEXT: 11021: ba 00 30 01 00 movl $77824, %edx
-// CODE-NEXT: 11026: ba 10 30 01 00 movl $77840, %edx
-// CODE-NEXT: 1102b: ba 14 30 01 00 movl $77844, %edx
+// CODE-NEXT: 201000: {{.*}} movl $5, 2109440
+// CODE-NEXT: 20100b: {{.*}} movl $7, 2109456
+// CODE-NEXT: 201016: {{.*}} movl $9, 2109460
+// CODE-NEXT: 201021: {{.*}} movl $2109440, %edx
+// CODE-NEXT: 201026: {{.*}} movl $2109456, %edx
+// CODE-NEXT: 20102b: {{.*}} movl $2109460, %edx
diff --git a/test/ELF/relocation-dtrace.test b/test/ELF/relocation-dtrace.test
new file mode 100644
index 000000000000..ef2cc4956ca6
--- /dev/null
+++ b/test/ELF/relocation-dtrace.test
@@ -0,0 +1,24 @@
+# RUN: yaml2obj %s -o %t.o
+# RUN: ld.lld -shared %t.o -o %t.so
+
+# Test that we can handle R_X86_64_NONE as produced by dtrace.
+
+!ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ OSABI: ELFOSABI_FREEBSD
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC ]
+ - Name: .rela.text
+ Type: SHT_RELA
+ Link: .symtab
+ Info: .text
+ Relocations:
+ - Offset: 0x0000000000000000
+ Symbol: ''
+ Type: R_X86_64_NONE
diff --git a/test/ELF/relocation-i686.s b/test/ELF/relocation-i686.s
index 96d760601127..262f697184bb 100644
--- a/test/ELF/relocation-i686.s
+++ b/test/ELF/relocation-i686.s
@@ -56,16 +56,18 @@ movl bar@GOT, %eax
// ADDR-NEXT: SHF_WRITE
// ADDR-NEXT: ]
// ADDR-NEXT: Address: 0x12078
+// ADDR-NEXT: Offset:
+// ADDR-NEXT: Size: 8
.section .R_386_GOTPC,"ax",@progbits
R_386_GOTPC:
movl $_GLOBAL_OFFSET_TABLE_, %eax
-// 0x12050 - 0x11014 = 4156
+// 0x12078 + 8 - 0x11014 = 4204
// CHECK: Disassembly of section .R_386_GOTPC:
// CHECK-NEXT: R_386_GOTPC:
-// CHECK-NEXT: 11014: {{.*}} movl $4196, %eax
+// CHECK-NEXT: 11014: {{.*}} movl $4204, %eax
.section .dynamic_reloc, "ax",@progbits
call bar
diff --git a/test/ELF/relocation-local.s b/test/ELF/relocation-local.s
index 26e89fc12f8d..8173dac0522f 100644
--- a/test/ELF/relocation-local.s
+++ b/test/ELF/relocation-local.s
@@ -20,7 +20,7 @@ R_X86_64_32:
// constants in hex.
// CHECK: Disassembly of section .text2:
// CHECK-NEXT: R_X86_64_32:
-// CHECK-NEXT: 11009: {{.*}} movl $69641, %edx
+// CHECK-NEXT: 201009: {{.*}} movl $2101257, %edx
.section .R_X86_64_32S,"ax",@progbits
R_X86_64_32S:
@@ -28,11 +28,11 @@ R_X86_64_32S:
// CHECK: Disassembly of section .R_X86_64_32S:
// CHECK-NEXT: R_X86_64_32S:
-// CHECK-NEXT: {{.*}}: {{.*}} movq -978935, %rdx
+// CHECK-NEXT: {{.*}}: {{.*}} movq 1052681, %rdx
.section .R_X86_64_64,"a",@progbits
R_X86_64_64:
.quad R_X86_64_64
// CHECK: Contents of section .R_X86_64_64:
-// CHECK-NEXT: 10120 20010100 00000000
+// CHECK-NEXT: 200120 20012000 00000000
diff --git a/test/ELF/relocation-past-merge-end.s b/test/ELF/relocation-past-merge-end.s
index 993b071f62d2..d08bde7b9f6c 100644
--- a/test/ELF/relocation-past-merge-end.s
+++ b/test/ELF/relocation-past-merge-end.s
@@ -1,8 +1,9 @@
// REQUIRES: x86
// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux
// RUN: not ld.lld %t.o -o %t.so -shared 2>&1 | FileCheck %s
-// CHECK: entry is past the end of the section
+// CHECK: relocation-past-merge-end.s.tmp.o:(.foo): entry is past the end of the section
- .data
- .long .foo + 1
- .section .foo,"aM",@progbits,4
+.data
+.long .foo + 10
+.section .foo,"aM",@progbits,4
+.quad 0
diff --git a/test/ELF/relocation-relative-absolute.s b/test/ELF/relocation-relative-absolute.s
index 5253191331cc..e1636c734a88 100644
--- a/test/ELF/relocation-relative-absolute.s
+++ b/test/ELF/relocation-relative-absolute.s
@@ -1,12 +1,12 @@
# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-# RUN: not ld.lld %t.o -o %t -pie 2>&1 | FileCheck %s
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %tinput1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux \
+# RUN: %S/Inputs/relocation-relative-absolute.s -o %tinput2.o
+# RUN: not ld.lld %tinput1.o %tinput2.o -o %t -pie 2>&1 | FileCheck %s
.globl _start
_start:
-# CHECK: relocation R_X86_64_PLT32 cannot refer to absolute symbol answer
-call answer@PLT
+# CHECK: {{.*}}input1.o:(.text+0x1): relocation R_X86_64_PLT32 cannot refer to absolute symbol 'answer' defined in {{.*}}input2.o
-.globl answer
-answer = 42
+call answer@PLT
diff --git a/test/ELF/relocation-size-shared.s b/test/ELF/relocation-size-shared.s
index d6274e9ae12e..7b700ad74401 100644
--- a/test/ELF/relocation-size-shared.s
+++ b/test/ELF/relocation-size-shared.s
@@ -7,48 +7,48 @@
// RELOCSHARED: Relocations [
// RELOCSHARED-NEXT: Section ({{.*}}) .rela.dyn {
-// RELOCSHARED-NEXT: 0x13018 R_X86_64_SIZE64 fooshared 0xFFFFFFFFFFFFFFFF
-// RELOCSHARED-NEXT: 0x13020 R_X86_64_SIZE64 fooshared 0x0
-// RELOCSHARED-NEXT: 0x13028 R_X86_64_SIZE64 fooshared 0x1
-// RELOCSHARED-NEXT: 0x13048 R_X86_64_SIZE32 fooshared 0xFFFFFFFFFFFFFFFF
-// RELOCSHARED-NEXT: 0x1304F R_X86_64_SIZE32 fooshared 0x0
-// RELOCSHARED-NEXT: 0x13056 R_X86_64_SIZE32 fooshared 0x1
+// RELOCSHARED-NEXT: 0x203018 R_X86_64_SIZE64 fooshared 0xFFFFFFFFFFFFFFFF
+// RELOCSHARED-NEXT: 0x203020 R_X86_64_SIZE64 fooshared 0x0
+// RELOCSHARED-NEXT: 0x203028 R_X86_64_SIZE64 fooshared 0x1
+// RELOCSHARED-NEXT: 0x203048 R_X86_64_SIZE32 fooshared 0xFFFFFFFFFFFFFFFF
+// RELOCSHARED-NEXT: 0x20304F R_X86_64_SIZE32 fooshared 0x0
+// RELOCSHARED-NEXT: 0x203056 R_X86_64_SIZE32 fooshared 0x1
// RELOCSHARED-NEXT: }
// RELOCSHARED-NEXT:]
// DISASM: Disassembly of section test
// DISASM: _data:
-// DISASM-NEXT: 13000: 19 00
-// DISASM-NEXT: 13002: 00 00
-// DISASM-NEXT: 13004: 00 00
-// DISASM-NEXT: 13006: 00 00
-// DISASM-NEXT: 13008: 1a 00
-// DISASM-NEXT: 1300a: 00 00
-// DISASM-NEXT: 1300c: 00 00
-// DISASM-NEXT: 1300e: 00 00
-// DISASM-NEXT: 13010: 1b 00
-// DISASM-NEXT: 13012: 00 00
-// DISASM-NEXT: 13014: 00 00
-// DISASM-NEXT: 13016: 00 00
-// DISASM-NEXT: 13018: 00 00
-// DISASM-NEXT: 1301a: 00 00
-// DISASM-NEXT: 1301c: 00 00
-// DISASM-NEXT: 1301e: 00 00
-// DISASM-NEXT: 13020: 00 00
-// DISASM-NEXT: 13022: 00 00
-// DISASM-NEXT: 13024: 00 00
-// DISASM-NEXT: 13026: 00 00
-// DISASM-NEXT: 13028: 00 00
-// DISASM-NEXT: 1302a: 00 00
-// DISASM-NEXT: 1302c: 00 00
-// DISASM-NEXT: 1302e: 00 00
+// DISASM-NEXT: 203000: 19 00
+// DISASM-NEXT: 203002: 00 00
+// DISASM-NEXT: 203004: 00 00
+// DISASM-NEXT: 203006: 00 00
+// DISASM-NEXT: 203008: 1a 00
+// DISASM-NEXT: 20300a: 00 00
+// DISASM-NEXT: 20300c: 00 00
+// DISASM-NEXT: 20300e: 00 00
+// DISASM-NEXT: 203010: 1b 00
+// DISASM-NEXT: 203012: 00 00
+// DISASM-NEXT: 203014: 00 00
+// DISASM-NEXT: 203016: 00 00
+// DISASM-NEXT: 203018: 00 00
+// DISASM-NEXT: 20301a: 00 00
+// DISASM-NEXT: 20301c: 00 00
+// DISASM-NEXT: 20301e: 00 00
+// DISASM-NEXT: 203020: 00 00
+// DISASM-NEXT: 203022: 00 00
+// DISASM-NEXT: 203024: 00 00
+// DISASM-NEXT: 203026: 00 00
+// DISASM-NEXT: 203028: 00 00
+// DISASM-NEXT: 20302a: 00 00
+// DISASM-NEXT: 20302c: 00 00
+// DISASM-NEXT: 20302e: 00 00
// DISASM: _start:
-// DISASM-NEXT: 13030: 8b 04 25 19 00 00 00 movl 25, %eax
-// DISASM-NEXT: 13037: 8b 04 25 1a 00 00 00 movl 26, %eax
-// DISASM-NEXT: 1303e: 8b 04 25 1b 00 00 00 movl 27, %eax
-// DISASM-NEXT: 13045: 8b 04 25 00 00 00 00 movl 0, %eax
-// DISASM-NEXT: 1304c: 8b 04 25 00 00 00 00 movl 0, %eax
-// DISASM-NEXT: 13053: 8b 04 25 00 00 00 00 movl 0, %eax
+// DISASM-NEXT: 203030: 8b 04 25 19 00 00 00 movl 25, %eax
+// DISASM-NEXT: 203037: 8b 04 25 1a 00 00 00 movl 26, %eax
+// DISASM-NEXT: 20303e: 8b 04 25 1b 00 00 00 movl 27, %eax
+// DISASM-NEXT: 203045: 8b 04 25 00 00 00 00 movl 0, %eax
+// DISASM-NEXT: 20304c: 8b 04 25 00 00 00 00 movl 0, %eax
+// DISASM-NEXT: 203053: 8b 04 25 00 00 00 00 movl 0, %eax
.data
.global foo
diff --git a/test/ELF/relocation-size.s b/test/ELF/relocation-size.s
index 58604dbcb4e4..2256be67df06 100644
--- a/test/ELF/relocation-size.s
+++ b/test/ELF/relocation-size.s
@@ -11,37 +11,37 @@
// DISASM: Disassembly of section test:
// DISASM-NEXT: _data:
-// DISASM-NEXT: 12000: 19 00
-// DISASM-NEXT: 12002: 00 00
-// DISASM-NEXT: 12004: 00 00
-// DISASM-NEXT: 12006: 00 00
-// DISASM-NEXT: 12008: 1a 00
-// DISASM-NEXT: 1200a: 00 00
-// DISASM-NEXT: 1200c: 00 00
-// DISASM-NEXT: 1200e: 00 00
-// DISASM-NEXT: 12010: 1b 00
-// DISASM-NEXT: 12012: 00 00
-// DISASM-NEXT: 12014: 00 00
-// DISASM-NEXT: 12016: 00 00
-// DISASM-NEXT: 12018: 19 00
-// DISASM-NEXT: 1201a: 00 00
-// DISASM-NEXT: 1201c: 00 00
-// DISASM-NEXT: 1201e: 00 00
-// DISASM-NEXT: 12020: 1a 00
-// DISASM-NEXT: 12022: 00 00
-// DISASM-NEXT: 12024: 00 00
-// DISASM-NEXT: 12026: 00 00
-// DISASM-NEXT: 12028: 1b 00
-// DISASM-NEXT: 1202a: 00 00
-// DISASM-NEXT: 1202c: 00 00
-// DISASM-NEXT: 1202e: 00 00
+// DISASM-NEXT: 202000: 19 00
+// DISASM-NEXT: 202002: 00 00
+// DISASM-NEXT: 202004: 00 00
+// DISASM-NEXT: 202006: 00 00
+// DISASM-NEXT: 202008: 1a 00
+// DISASM-NEXT: 20200a: 00 00
+// DISASM-NEXT: 20200c: 00 00
+// DISASM-NEXT: 20200e: 00 00
+// DISASM-NEXT: 202010: 1b 00
+// DISASM-NEXT: 202012: 00 00
+// DISASM-NEXT: 202014: 00 00
+// DISASM-NEXT: 202016: 00 00
+// DISASM-NEXT: 202018: 19 00
+// DISASM-NEXT: 20201a: 00 00
+// DISASM-NEXT: 20201c: 00 00
+// DISASM-NEXT: 20201e: 00 00
+// DISASM-NEXT: 202020: 1a 00
+// DISASM-NEXT: 202022: 00 00
+// DISASM-NEXT: 202024: 00 00
+// DISASM-NEXT: 202026: 00 00
+// DISASM-NEXT: 202028: 1b 00
+// DISASM-NEXT: 20202a: 00 00
+// DISASM-NEXT: 20202c: 00 00
+// DISASM-NEXT: 20202e: 00 00
// DISASM: _start:
-// DISASM-NEXT: 12030: 8b 04 25 19 00 00 00 movl 25, %eax
-// DISASM-NEXT: 12037: 8b 04 25 1a 00 00 00 movl 26, %eax
-// DISASM-NEXT: 1203e: 8b 04 25 1b 00 00 00 movl 27, %eax
-// DISASM-NEXT: 12045: 8b 04 25 19 00 00 00 movl 25, %eax
-// DISASM-NEXT: 1204c: 8b 04 25 1a 00 00 00 movl 26, %eax
-// DISASM-NEXT: 12053: 8b 04 25 1b 00 00 00 movl 27, %eax
+// DISASM-NEXT: 202030: 8b 04 25 19 00 00 00 movl 25, %eax
+// DISASM-NEXT: 202037: 8b 04 25 1a 00 00 00 movl 26, %eax
+// DISASM-NEXT: 20203e: 8b 04 25 1b 00 00 00 movl 27, %eax
+// DISASM-NEXT: 202045: 8b 04 25 19 00 00 00 movl 25, %eax
+// DISASM-NEXT: 20204c: 8b 04 25 1a 00 00 00 movl 26, %eax
+// DISASM-NEXT: 202053: 8b 04 25 1b 00 00 00 movl 27, %eax
// RELOCSHARED: Relocations [
// RELOCSHARED-NEXT: Section ({{.*}}) .rela.dyn {
diff --git a/test/ELF/relocation-undefined-weak.s b/test/ELF/relocation-undefined-weak.s
index f1cb706329a4..6aa84ec483f4 100644
--- a/test/ELF/relocation-undefined-weak.s
+++ b/test/ELF/relocation-undefined-weak.s
@@ -18,10 +18,10 @@ _start:
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_EXECINSTR
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x11000
+// CHECK-NEXT: Address: 0x201000
// Unfortunately FileCheck can't do math, so we have to check for explicit
// values:
-// R_86_64_PC32 = 0 + (-8 - (0x11000 + 2)) = -69642
+// R_86_64_PC32 = 0 + (-8 - (0x201000 + 2)) = -2101258
-// DISASM: movl $1, -69642(%rip)
+// DISASM: movl $1, -2101258(%rip)
diff --git a/test/ELF/relocation.s b/test/ELF/relocation.s
index 11832683d9be..452d1bedba5a 100644
--- a/test/ELF/relocation.s
+++ b/test/ELF/relocation.s
@@ -12,7 +12,7 @@
// SEC-NEXT: SHF_ALLOC
// SEC-NEXT: SHF_EXECINSTR
// SEC-NEXT: ]
-// SEC-NEXT: Address: 0x11030
+// SEC-NEXT: Address: 0x201030
// SEC-NEXT: Offset: 0x1030
// SEC-NEXT: Size: 48
@@ -22,7 +22,7 @@
// SEC-NEXT: SHF_ALLOC
// SEC-NEXT: SHF_WRITE
// SEC-NEXT: ]
-// SEC-NEXT: Address: 0x120F0
+// SEC-NEXT: Address: 0x2020F0
// SEC-NEXT: Offset:
// SEC-NEXT: Size: 8
// SEC-NEXT: Link: 0
@@ -37,7 +37,7 @@
// SEC-NEXT: SHF_ALLOC
// SEC-NEXT: SHF_WRITE
// SEC-NEXT: ]
-// SEC-NEXT: Address: 0x13000
+// SEC-NEXT: Address: 0x203000
// SEC-NEXT: Offset: 0x3000
// SEC-NEXT: Size: 40
// SEC-NEXT: Link: 0
@@ -59,11 +59,11 @@ lulz:
// CHECK: Disassembly of section .text:
// CHECK-NEXT: _start:
-// CHECK-NEXT: 11000: e8 04 00 00 00 callq 4
-// CHECK-NEXT: 11005:
+// CHECK-NEXT: 201000: e8 04 00 00 00 callq 4
+// CHECK-NEXT: 201005:
// CHECK: lulz:
-// CHECK-NEXT: 11009: 90 nop
+// CHECK-NEXT: 201009: 90 nop
.section .text2,"ax",@progbits
@@ -75,7 +75,7 @@ R_X86_64_32:
// constants in hex.
// CHECK: Disassembly of section .text2:
// CHECK-NEXT: R_X86_64_32:
-// CHECK-NEXT: 1100a: {{.*}} movl $69642, %edx
+// CHECK-NEXT: 20100a: {{.*}} movl $2101258, %edx
.section .R_X86_64_32S,"ax",@progbits
.global R_X86_64_32S
@@ -84,7 +84,7 @@ R_X86_64_32S:
// CHECK: Disassembly of section .R_X86_64_32S:
// CHECK-NEXT: R_X86_64_32S:
-// CHECK-NEXT: {{.*}}: {{.*}} movq -978935, %rdx
+// CHECK-NEXT: {{.*}}: {{.*}} movq 1052681, %rdx
.section .R_X86_64_PC32,"ax",@progbits
.global R_X86_64_PC32
@@ -92,20 +92,20 @@ R_X86_64_PC32:
call bar
movl $bar, %eax
//16 is a size of PLT[0]
-// 0x11030 + 16 - (0x11017 + 5) = 20
+// 0x201030 + 16 - (0x201017 + 5) = 20
// CHECK: Disassembly of section .R_X86_64_PC32:
// CHECK-NEXT: R_X86_64_PC32:
-// CHECK-NEXT: 11017: {{.*}} callq 36
-// CHECK-NEXT: 1101c: {{.*}} movl $69696, %eax
+// CHECK-NEXT: 201017: {{.*}} callq 36
+// CHECK-NEXT: 20101c: {{.*}} movl $2101312, %eax
.section .R_X86_64_32S_2,"ax",@progbits
.global R_X86_64_32S_2
R_X86_64_32S_2:
mov bar2, %eax
-// plt is at 0x11030. The second plt entry is at 0x11050 == 69712
+// plt is at 0x201030. The second plt entry is at 0x201050 == 69712
// CHECK: Disassembly of section .R_X86_64_32S_2:
// CHECK-NEXT: R_X86_64_32S_2:
-// CHECK-NEXT: 11021: {{.*}} movl 69712, %eax
+// CHECK-NEXT: 201021: {{.*}} movl 2101328, %eax
.section .R_X86_64_64,"a",@progbits
.global R_X86_64_64
@@ -113,17 +113,17 @@ R_X86_64_64:
.quad R_X86_64_64
// CHECK: Contents of section .R_X86_64_64:
-// CHECK-NEXT: 101c8 c8010100 00000000
+// CHECK-NEXT: 2001c8 c8012000 00000000
.section .R_X86_64_GOTPCREL,"a",@progbits
.global R_X86_64_GOTPCREL
R_X86_64_GOTPCREL:
.long zed@gotpcrel
-// 0x120F8 - 0x101D8 = 7952
+// 0x2020F8 - 0x2001D8 = 7952
// 7952 = 0x101f0000 in little endian
// CHECK: Contents of section .R_X86_64_GOTPCREL
-// CHECK-NEXT: 101d0 201f0000
+// CHECK-NEXT: 2001d0 201f0000
.section .R_X86_64_GOT32,"a",@progbits
.global R_X86_64_GOT32
@@ -132,3 +132,11 @@ R_X86_64_GOT32:
// CHECK: Contents of section .R_X86_64_GOT32:
// CHECK-NEXT: f8ffffff
+
+
+// CHECK: Contents of section .R_X86_64_GOT64:
+// CHECK-NEXT: f8ffffff ffffffff
+.section .R_X86_64_GOT64,"a",@progbits
+.global R_X86_64_GOT64
+R_X86_64_GOT64:
+ .quad zed@got
diff --git a/test/ELF/relro-omagic.s b/test/ELF/relro-omagic.s
new file mode 100644
index 000000000000..35a92db2f07a
--- /dev/null
+++ b/test/ELF/relro-omagic.s
@@ -0,0 +1,34 @@
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o
+# RUN: ld.lld -shared %t2.o -o %t2.so
+# RUN: ld.lld -N %t.o %t2.so -o %t
+# RUN: llvm-objdump -section-headers %t | FileCheck --check-prefix=NORELRO %s
+# RUN: llvm-readobj --program-headers %t | FileCheck --check-prefix=NOPHDRS %s
+
+# NORELRO: Sections:
+# NORELRO-NEXT: Idx Name Size Address Type
+# NORELRO-NEXT: 0 00000000 0000000000000000
+# NORELRO-NEXT: 1 .dynsym 00000048 0000000000200120
+# NORELRO-NEXT: 2 .hash 00000020 0000000000200168
+# NORELRO-NEXT: 3 .dynstr 00000021 0000000000200188
+# NORELRO-NEXT: 4 .rela.dyn 00000018 00000000002001b0
+# NORELRO-NEXT: 5 .rela.plt 00000018 00000000002001c8
+# NORELRO-NEXT: 6 .text 0000000a 00000000002001e0 TEXT DATA
+# NORELRO-NEXT: 7 .plt 00000020 00000000002001f0 TEXT DATA
+# NORELRO-NEXT: 8 .data 00000008 0000000000200210 DATA
+# NORELRO-NEXT: 9 .foo 00000004 0000000000200218 DATA
+# NORELRO-NEXT: 10 .dynamic 000000f0 0000000000200220
+# NORELRO-NEXT: 11 .got 00000008 0000000000200310 DATA
+# NORELRO-NEXT: 12 .got.plt 00000020 0000000000200318 DATA
+
+# NOPHDRS: ProgramHeaders [
+# NOPHDRS-NOT: PT_GNU_RELRO
+
+.long bar
+jmp *bar2@GOTPCREL(%rip)
+
+.section .data,"aw"
+.quad 0
+
+.section .foo,"aw"
+.zero 4
diff --git a/test/ELF/relro.s b/test/ELF/relro.s
index d35548740001..b21e514303ab 100644
--- a/test/ELF/relro.s
+++ b/test/ELF/relro.s
@@ -2,232 +2,28 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o
// RUN: ld.lld -shared %t2.o -o %t2.so
// RUN: ld.lld %t.o %t2.so -z now -z relro -o %t
-// RUN: llvm-readobj --program-headers --dynamic-table -t -s -dyn-symbols -section-data %t | FileCheck --check-prefix=FULLRELRO %s
+// RUN: llvm-readobj -l --elf-output-style=GNU %t | FileCheck --check-prefix=CHECK --check-prefix=FULLRELRO %s
// RUN: ld.lld %t.o %t2.so -z relro -o %t
-// RUN: llvm-readobj --program-headers --dynamic-table -t -s -dyn-symbols -section-data %t | FileCheck --check-prefix=PARTRELRO %s
+// RUN: llvm-readobj -l --elf-output-style=GNU %t | FileCheck --check-prefix=CHECK --check-prefix=PARTRELRO %s
// RUN: ld.lld %t.o %t2.so -z norelro -o %t
-// RUN: llvm-readobj --program-headers --dynamic-table -t -s -dyn-symbols -section-data %t | FileCheck --check-prefix=NORELRO %s
+// RUN: llvm-readobj -l --elf-output-style=GNU %t | FileCheck --check-prefix=NORELRO %s
// REQUIRES: x86
-// FULLRELRO: Section {
-// FULLRELRO: Index: 9
-// FULLRELRO-NEXT: Name: .got
-// FULLRELRO-NEXT: Type: SHT_PROGBITS
-// FULLRELRO-NEXT: Flags [
-// FULLRELRO-NEXT: SHF_ALLOC
-// FULLRELRO-NEXT: SHF_WRITE
-// FULLRELRO-NEXT: ]
-// FULLRELRO-NEXT: Address: 0x12110
-// FULLRELRO-NEXT: Offset: 0x2110
-// FULLRELRO-NEXT: Size: 8
-// FULLRELRO-NEXT: Link: 0
-// FULLRELRO-NEXT: Info: 0
-// FULLRELRO-NEXT: AddressAlignment: 8
-// FULLRELRO-NEXT: EntrySize: 0
-// FULLRELRO-NEXT: SectionData (
-// FULLRELRO-NEXT: 0000: 00000000 00000000
-// FULLRELRO-NEXT: )
-// FULLRELRO-NEXT: }
-// FULLRELRO-NEXT: Section {
-// FULLRELRO-NEXT: Index: 10
-// FULLRELRO-NEXT: Name: .got.plt
-// FULLRELRO-NEXT: Type: SHT_PROGBITS
-// FULLRELRO-NEXT: Flags [
-// FULLRELRO-NEXT: SHF_ALLOC
-// FULLRELRO-NEXT: SHF_WRITE
-// FULLRELRO-NEXT: ]
-// FULLRELRO-NEXT: Address: 0x12118
-// FULLRELRO-NEXT: Offset: 0x2118
-// FULLRELRO-NEXT: Size: 32
-// FULLRELRO-NEXT: Link: 0
-// FULLRELRO-NEXT: Info: 0
-// FULLRELRO-NEXT: AddressAlignment: 8
-// FULLRELRO-NEXT: EntrySize: 0
-// FULLRELRO-NEXT: SectionData (
-// FULLRELRO-NEXT: 0000:
-// FULLRELRO-NEXT: 0010:
-// FULLRELRO-NEXT: )
-// FULLRELRO-NEXT: }
-// FULLRELRO-NEXT: Section {
-// FULLRELRO-NEXT: Index: 11
-// FULLRELRO-NEXT: Name: .data
-// FULLRELRO-NEXT: Type: SHT_PROGBITS
-// FULLRELRO-NEXT: Flags [
-// FULLRELRO-NEXT: SHF_ALLOC
-// FULLRELRO-NEXT: SHF_WRITE
-// FULLRELRO-NEXT: ]
-// FULLRELRO-NEXT: Address: 0x13000
-// FULLRELRO-NEXT: Offset: 0x3000
-// FULLRELRO-NEXT: Size: 12
-// FULLRELRO-NEXT: Link: 0
-// FULLRELRO-NEXT: Info: 0
-// FULLRELRO-NEXT: AddressAlignment:
-// FULLRELRO-NEXT: EntrySize: 0
-// FULLRELRO-NEXT: SectionData (
-// FULLRELRO-NEXT: 0000:
-// FULLRELRO-NEXT: )
-// FULLRELRO-NEXT: }
-// FULLRELRO-NEXT: Section {
-// FULLRELRO-NEXT: Index: 12
-// FULLRELRO-NEXT: Name: .foo
-// FULLRELRO-NEXT: Type: SHT_PROGBITS
-// FULLRELRO-NEXT: Flags [
-// FULLRELRO-NEXT: SHF_ALLOC
-// FULLRELRO-NEXT: SHF_WRITE
-// FULLRELRO-NEXT: ]
-// FULLRELRO-NEXT: Address: 0x1300C
-// FULLRELRO-NEXT: Offset: 0x300C
-// FULLRELRO-NEXT: Size: 0
-// FULLRELRO-NEXT: Link: 0
-// FULLRELRO-NEXT: Info: 0
-// FULLRELRO-NEXT: AddressAlignment:
-// FULLRELRO-NEXT: EntrySize: 0
-// FULLRELRO-NEXT: SectionData (
-// FULLRELRO-NEXT: )
-// FULLRELRO-NEXT: }
-// 308 - sizeof(.data)(12) = 296
-// FULLRELRO: ProgramHeaders [
-// FULLRELRO: Type: PT_LOAD
-// FULLRELRO: Offset: 0x2000
-// FULLRELRO-NEXT: VirtualAddress: [[RWADDR:.*]]
-// FULLRELRO-NEXT: PhysicalAddress:
-// FULLRELRO-NEXT: FileSize: 4108
-// FULLRELRO-NEXT: MemSize: 4108
-// FULLRELRO-NEXT: Flags [
-// FULLRELRO-NEXT: PF_R
-// FULLRELRO-NEXT: PF_W
-// FULLRELRO-NEXT: ]
-// FULLRELRO-NEXT: Alignment: 4096
-// FULLRELRO-NEXT:}
-// FULLRELRO: Type: PT_GNU_RELRO
-// FULLRELRO-NEXT: Offset: 0x
-// FULLRELRO-NEXT: VirtualAddress: [[RWADDR]]
-// FULLRELRO-NEXT: PhysicalAddress:
-// FULLRELRO-NEXT: FileSize: 312
-// FULLRELRO-NEXT: MemSize: 312
-// FULLRELRO-NEXT: Flags [
-// FULLRELRO-NEXT: PF_R
-// FULLRELRO-NEXT: ]
-// FULLRELRO-NEXT: Alignment: 1
-// FULLRELRO-NEXT:}
+// CHECK: Program Headers:
+// CHECK-NEXT: Type
+// CHECK-NEXT: PHDR
+// CHECK-NEXT: LOAD
+// CHECK-NEXT: LOAD
+// CHECK-NEXT: LOAD
+// CHECK-NEXT: DYNAMIC
+// CHECK-NEXT: GNU_RELRO
+// CHECK: Section to Segment mapping:
-// PARTRELRO: Section {
-// PARTRELRO: Index: 9
-// PARTRELRO-NEXT: Name: .got
-// PARTRELRO-NEXT: Type: SHT_PROGBITS
-// PARTRELRO-NEXT: Flags [
-// PARTRELRO-NEXT: SHF_ALLOC
-// PARTRELRO-NEXT: SHF_WRITE
-// PARTRELRO-NEXT: ]
-// PARTRELRO-NEXT: Address: 0x120F0
-// PARTRELRO-NEXT: Offset: 0x20F0
-// PARTRELRO-NEXT: Size: 8
-// PARTRELRO-NEXT: Link: 0
-// PARTRELRO-NEXT: Info: 0
-// PARTRELRO-NEXT: AddressAlignment: 8
-// PARTRELRO-NEXT: EntrySize: 0
-// PARTRELRO-NEXT: SectionData (
-// PARTRELRO-NEXT: 0000:
-// PARTRELRO-NEXT: )
-// PARTRELRO-NEXT: }
-// PARTRELRO-NEXT: Section {
-// PARTRELRO-NEXT: Index: 10
-// PARTRELRO-NEXT: Name: .data
-// PARTRELRO-NEXT: Type: SHT_PROGBITS
-// PARTRELRO-NEXT: Flags [
-// PARTRELRO-NEXT: SHF_ALLOC
-// PARTRELRO-NEXT: SHF_WRITE
-// PARTRELRO-NEXT: ]
-// PARTRELRO-NEXT: Address: 0x13000
-// PARTRELRO-NEXT: Offset: 0x3000
-// PARTRELRO-NEXT: Size: 12
-// PARTRELRO-NEXT: Link: 0
-// PARTRELRO-NEXT: Info: 0
-// PARTRELRO-NEXT: AddressAlignment: 1
-// PARTRELRO-NEXT: EntrySize: 0
-// PARTRELRO-NEXT: SectionData (
-// PARTRELRO-NEXT: 0000:
-// PARTRELRO-NEXT: )
-// PARTRELRO-NEXT: }
-// PARTRELRO-NEXT: Section {
-// PARTRELRO-NEXT: Index: 11
-// PARTRELRO-NEXT: Name: .foo
-// PARTRELRO-NEXT: Type: SHT_PROGBITS
-// PARTRELRO-NEXT: Flags [
-// PARTRELRO-NEXT: SHF_ALLOC
-// PARTRELRO-NEXT: SHF_WRITE
-// PARTRELRO-NEXT: ]
-// PARTRELRO-NEXT: Address: 0x1300C
-// PARTRELRO-NEXT: Offset: 0x300C
-// PARTRELRO-NEXT: Size: 0
-// PARTRELRO-NEXT: Link: 0
-// PARTRELRO-NEXT: Info: 0
-// PARTRELRO-NEXT: AddressAlignment: 1
-// PARTRELRO-NEXT: EntrySize: 0
-// PARTRELRO-NEXT: SectionData (
-// PARTRELRO-NEXT: )
-// PARTRELRO-NEXT: }
-// PARTRELRO-NEXT: Section {
-// PARTRELRO-NEXT: Index: 12
-// PARTRELRO-NEXT: Name: .got.plt
-// PARTRELRO-NEXT: Type: SHT_PROGBITS
-// PARTRELRO-NEXT: Flags [
-// PARTRELRO-NEXT: SHF_ALLOC
-// PARTRELRO-NEXT: SHF_WRITE
-// PARTRELRO-NEXT: ]
-// PARTRELRO-NEXT: Address: 0x13010
-// PARTRELRO-NEXT: Offset: 0x3010
-// PARTRELRO-NEXT: Size: 32
-// PARTRELRO-NEXT: Link: 0
-// PARTRELRO-NEXT: Info: 0
-// PARTRELRO-NEXT: AddressAlignment: 8
-// PARTRELRO-NEXT: EntrySize: 0
-// PARTRELRO-NEXT: SectionData (
-// PARTRELRO-NEXT: 0000:
-// PARTRELRO-NEXT: 0010:
-// PARTRELRO-NEXT: )
-// PARTRELRO-NEXT: }
-// PARTRELRO-NEXT: Section {
-// PARTRELRO-NEXT: Index: 13
-// PARTRELRO-NEXT: Name: .bss
-// PARTRELRO-NEXT: Type: SHT_NOBITS
-// PARTRELRO-NEXT: Flags [
-// PARTRELRO-NEXT: SHF_ALLOC
-// PARTRELRO-NEXT: SHF_WRITE
-// PARTRELRO-NEXT: ]
-// PARTRELRO-NEXT: Address: 0x13030
-// PARTRELRO-NEXT: Offset: 0x3030
-// PARTRELRO-NEXT: Size: 0
-// PARTRELRO-NEXT: Link: 0
-// PARTRELRO-NEXT: Info: 0
-// PARTRELRO-NEXT: AddressAlignment: 1
-// PARTRELRO-NEXT: EntrySize: 0
-// PARTRELRO-NEXT: }
-// PARTRELRO: ProgramHeader {
-// PARTRELRO: Type: PT_LOAD
-// PARTRELRO: Offset: 0x2000
-// PARTRELRO-NEXT: VirtualAddress: [[RWADDR:.*]]
-// PARTRELRO-NEXT: PhysicalAddress:
-// PARTRELRO-NEXT: FileSize: 4144
-// PARTRELRO-NEXT: MemSize: 4144
-// PARTRELRO-NEXT: Flags [
-// PARTRELRO-NEXT: PF_R (0x4)
-// PARTRELRO-NEXT: PF_W (0x2)
-// PARTRELRO-NEXT: ]
-// PARTRELRO-NEXT: Alignment: 4096
-// PARTRELRO: Type: PT_GNU_RELRO
-// PARTRELRO-NEXT: Offset: 0x2000
-// PARTRELRO-NEXT: VirtualAddress: [[RWADDR]]
-// PARTRELRO-NEXT: PhysicalAddress:
-// PARTRELRO-NEXT: FileSize: 248
-// PARTRELRO-NEXT: MemSize: 248
-// PARTRELRO-NEXT: Flags [
-// PARTRELRO-NEXT: PF_R
-// PARTRELRO-NEXT: ]
-// PARTRELRO-NEXT: Alignment: 1
+// FULLRELRO: 05 .openbsd.randomdata .dynamic .got .got.plt {{$}}
+// PARTRELRO: 05 .openbsd.randomdata .dynamic .got {{$}}
-// NORELRO: ProgramHeaders [
-// NORELRO-NOT: PT_GNU_RELRO
+
+// NORELRO-NOT: GNU_RELRO
.global _start
_start:
@@ -240,3 +36,6 @@ _start:
.zero 4
.section .foo,"aw"
.section .bss,"",@nobits
+
+.section .openbsd.randomdata, "aw"
+.quad 0
diff --git a/test/ELF/reproduce-backslash.s b/test/ELF/reproduce-backslash.s
new file mode 100644
index 000000000000..849c8f8fb3dd
--- /dev/null
+++ b/test/ELF/reproduce-backslash.s
@@ -0,0 +1,9 @@
+# REQUIRES: x86, cpio, shell
+
+# Test that we don't erroneously replace \ with / on UNIX, as it's
+# legal for a filename to contain backslashes.
+# RUN: llvm-mc %s -o foo\\.o -filetype=obj -triple=x86_64-pc-linux
+# RUN: ld.lld foo\\.o --reproduce repro
+# RUN: cpio -it < repro.cpio | FileCheck %s
+
+# CHECK: repro/{{.*}}/foo\.o
diff --git a/test/ELF/reproduce-windows.s b/test/ELF/reproduce-windows.s
index 464b27098694..4825a8a95e78 100644
--- a/test/ELF/reproduce-windows.s
+++ b/test/ELF/reproduce-windows.s
@@ -1,12 +1,12 @@
-# REQUIRES: x86
+# REQUIRES: x86, cpio
# Test that a repro archive always uses / instead of \.
# RUN: rm -rf %t.dir
# RUN: mkdir -p %t.dir/build
# RUN: llvm-mc %s -o %t.dir/build/foo.o -filetype=obj -triple=x86_64-pc-linux
# RUN: cd %t.dir
-# RUN: not ld.lld build/foo.o --reproduce repro
-# RUN: cpio -t < repro.cpio | FileCheck %s
+# RUN: ld.lld build/foo.o --reproduce repro
+# RUN: cpio -it < repro.cpio | FileCheck %s
# CHECK: repro/response.txt
# CHECK: repro/{{.*}}/build/foo.o
diff --git a/test/ELF/reproduce-windows2.s b/test/ELF/reproduce-windows2.s
new file mode 100644
index 000000000000..d428288a3640
--- /dev/null
+++ b/test/ELF/reproduce-windows2.s
@@ -0,0 +1,11 @@
+# REQUIRES: system-windows, x86, cpio
+
+# Test that a response.txt file always uses / instead of \.
+# RUN: rm -rf %t.dir
+# RUN: mkdir -p %t.dir/build
+# RUN: llvm-mc %s -o %t.dir/build/foo.o -filetype=obj -triple=x86_64-pc-linux
+# RUN: cd %t.dir
+# RUN: ld.lld build/foo.o --reproduce repro
+# RUN: echo "*response.txt" > list.txt
+# RUN: cpio -i --to-stdout --pattern-file=list.txt < repro.cpio | FileCheck %s
+# CHECK: {{.*}}/build/foo.o
diff --git a/test/ELF/reproduce.s b/test/ELF/reproduce.s
index 9d256c12d827..471a0f82da44 100644
--- a/test/ELF/reproduce.s
+++ b/test/ELF/reproduce.s
@@ -1,4 +1,4 @@
-# REQUIRES: x86
+# REQUIRES: x86, cpio
# Extracting the cpio archive can get over the path limit on windows.
# REQUIRES: shell
@@ -30,12 +30,12 @@
# RUN: diff %t.dir/build2/foo.o repro/%:t.dir/build2/foo.o
# RUN: echo "{ local: *; };" > ver
-# RUN: echo > dyn
+# RUN: echo "{};" > dyn
# RUN: echo > file
# RUN: echo > file2
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o 'foo bar'
# RUN: ld.lld --reproduce repro2 'foo bar' -L"foo bar" -Lfile -Tfile2 \
-# RUN: --dynamic-list dyn -rpath file --script file --version-script ver \
+# RUN: --dynamic-list dyn -rpath file --script=file --version-script ver \
# RUN: --dynamic-linker "some unusual/path" -soname 'foo bar' -soname='foo bar'
# RUN: cpio -id < repro2.cpio
# RUN: FileCheck %s --check-prefix=RSP2 < repro2/response.txt
@@ -51,7 +51,7 @@
# RSP2-NEXT: -soname="foo bar"
# RSP2-NEXT: -soname="foo bar"
-# RUN: cpio -t < repro2.cpio | FileCheck %s
+# RUN: cpio -it < repro2.cpio | FileCheck %s
# CHECK: repro2/response.txt
# CHECK-NEXT: repro2/version.txt
# CHECK-NEXT: repro2/{{.*}}/dyn
diff --git a/test/ELF/resolution.s b/test/ELF/resolution.s
index 5596212b3bac..4a42d941eab6 100644
--- a/test/ELF/resolution.s
+++ b/test/ELF/resolution.s
@@ -19,15 +19,6 @@
// CHECK-NEXT: Section: Undefined (0x0)
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
-// CHECK-NEXT: Name: _start
-// CHECK-NEXT: Value:
-// CHECK-NEXT: Size: 0
-// CHECK-NEXT: Binding: Global
-// CHECK-NEXT: Type: None
-// CHECK-NEXT: Other: 0
-// CHECK-NEXT: Section: .text
-// CHECK-NEXT: }
-// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: CommonStrong_with_CommonStrong
// CHECK-NEXT: Value:
// CHECK-NEXT: Size: 63
@@ -315,6 +306,15 @@
// CHECK-NEXT: Other: 0
// CHECK-NEXT: Section: Undefined
// CHECK-NEXT: }
+// CHECK-NEXT: Symbol {
+// CHECK-NEXT: Name: _start
+// CHECK-NEXT: Value: 0x201000
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Global (0x1)
+// CHECK-NEXT: Type: None (0x0)
+// CHECK-NEXT: Other: 0
+// CHECK-NEXT: Section: .text (0x1)
+// CHECK-NEXT: }
// CHECK-NEXT: ]
.globl _start
diff --git a/test/ELF/retain-symbols-file.s b/test/ELF/retain-symbols-file.s
new file mode 100644
index 000000000000..b146bd797b5c
--- /dev/null
+++ b/test/ELF/retain-symbols-file.s
@@ -0,0 +1,44 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
+# RUN: echo "bar" > %t_retain.txt
+# RUN: echo "foo" >> %t_retain.txt
+# RUN: ld.lld -shared --retain-symbols-file=%t_retain.txt %t -o %t2
+# RUN: llvm-readobj -s -sd -t %t2 | FileCheck %s
+
+## Check separate form.
+# RUN: ld.lld -shared --retain-symbols-file %t_retain.txt %t -o %t2
+# RUN: llvm-readobj -s -sd -t %t2 | FileCheck %s
+
+# CHECK: Symbols [
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: (0)
+# CHECK: Symbol {
+# CHECK-NEXT: Name: bar
+# CHECK: Symbol {
+# CHECK-NEXT: Name: foo
+# CHECK-NOT: Symbol
+
+.text
+.globl _start
+_start:
+call zed@PLT
+call und@PLT
+
+.globl foo
+.type foo,@function
+foo:
+retq
+
+.globl bar
+.type bar,@function
+bar:
+retq
+
+.globl zed
+.type zed,@function
+zed:
+retq
+
+.type loc,@function
+loc:
+retq
diff --git a/test/ELF/section-name.s b/test/ELF/section-name.s
index f470c48c6a37..53f0e536f3d6 100644
--- a/test/ELF/section-name.s
+++ b/test/ELF/section-name.s
@@ -1,6 +1,6 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
# RUN: ld.lld %t -o %tout
-# RUN: llvm-readobj -sections %tout | FileCheck %s
+# RUN: llvm-objdump --section-headers %tout | FileCheck %s
# REQUIRES: x86
.global _start
@@ -8,36 +8,52 @@
_start:
.section .text.a,"ax"
+.byte 0
.section .text.,"ax"
+.byte 0
.section .rodata.a,"a"
+.byte 0
.section .rodata,"a"
+.byte 0
.section .data.a,"aw"
+.byte 0
.section .data,"aw"
+.byte 0
.section .bss.a,"",@nobits
+.byte 0
.section .bss,"",@nobits
+.byte 0
.section .foo.a,"aw"
+.byte 0
.section .foo,"aw"
+.byte 0
.section .data.rel.ro,"aw",%progbits
+.byte 0
.section .data.rel.ro.a,"aw",%progbits
+.byte 0
.section .data.rel.ro.local,"aw",%progbits
+.byte 0
.section .data.rel.ro.local.a,"aw",%progbits
+.byte 0
.section .tbss.foo,"aGwT",@nobits,foo,comdat
+.byte 0
.section .gcc_except_table.foo,"aG",@progbits,foo,comdat
+.byte 0
.section .tdata.foo,"aGwT",@progbits,foo,comdat
+.byte 0
-// CHECK-NOT: Name: .rodata.a
-// CHECK: Name: .rodata
-// CHECK: Name: .gcc_except_table ({{.*}})
-// CHECK-NOT: Name: .text.a
-// CHECK: Name: .text
-// CHECK: Name: .tdata ({{.*}})
-// CHECK: Name: .tbss ({{.*}})
-// CHECK-NOT: Name: .data.rel.ro.a
-// CHECK-NOT: Name: .data.rel.ro.local.a
-// CHECK: Name: .data.rel.ro
-// CHECK-NOT: Name: .data.a
-// CHECK: Name: .data
-// CHECK: Name: .foo.a
-// CHECK: Name: .foo
-// CHECK-NOT: Name: .bss.a
-// CHECK: Name: .bss
+// CHECK: 1 .rodata 00000002
+// CHECK: 2 .gcc_except_table 00000001
+// CHECK: 3 .text 00000002
+// CHECK: 4 .tdata 00000001
+// CHECK: 5 .tbss 00000001
+// CHECK: 6 .data.rel.ro 00000004
+// CHECK: 7 .data 00000002
+// CHECK: 8 .foo.a 00000001
+// CHECK: 9 .foo 00000001
+// CHECK: 10 .bss 00000001
+// CHECK: 11 .bss 00000001
+// CHECK: 12 .comment 00000008
+// CHECK: 13 .symtab 00000060
+// CHECK: 14 .shstrtab 00000075
+// CHECK: 15 .strtab 0000001d
diff --git a/test/ELF/section-symbols.test b/test/ELF/section-symbols.test
new file mode 100644
index 000000000000..2ef77a828b65
--- /dev/null
+++ b/test/ELF/section-symbols.test
@@ -0,0 +1,35 @@
+# RUN: yaml2obj %s -o %t
+# RUN: ld.lld -shared %t -o %tout
+
+# Verify that lld can handle STT_SECTION symbols associated
+# with SHT_REL[A]/SHT_SYMTAB/SHT_STRTAB sections.
+
+!ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ OSABI: ELFOSABI_FREEBSD
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x0000000000000010
+ Content: "00000000"
+ - Name: .rela.text
+ Type: SHT_RELA
+ Link: .symtab
+ AddressAlign: 0x0000000000000008
+ Info: .text
+ Relocations:
+Symbols:
+ Local:
+ - Type: STT_SECTION
+ Section: .rela.text
+ - Type: STT_SECTION
+ Section: .shstrtab
+ - Type: STT_SECTION
+ Section: .symtab
+ - Type: STT_SECTION
+ Section: .strtab
diff --git a/test/ELF/sectionstart.s b/test/ELF/sectionstart.s
new file mode 100644
index 000000000000..23574c14748a
--- /dev/null
+++ b/test/ELF/sectionstart.s
@@ -0,0 +1,67 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o --section-start .text=0x100000 \
+# RUN: --section-start .data=0x110000 --section-start .bss=0x200000 -o %t
+# RUN: llvm-objdump -section-headers %t | FileCheck %s
+
+# CHECK: Sections:
+# CHECK-NEXT: Idx Name Size Address Type
+# CHECK-NEXT: 0 00000000 0000000000000000
+# CHECK-NEXT: 1 .text 00000001 0000000000100000 TEXT DATA
+# CHECK-NEXT: 2 .data 00000004 0000000000110000 DATA
+# CHECK-NEXT: 3 .bss 00000004 0000000000200000 BSS
+
+## The same, but dropped "0x" prefix.
+# RUN: ld.lld %t.o --section-start .text=100000 \
+# RUN: --section-start .data=110000 --section-start .bss=0x200000 -o %t1
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+
+## Use -Ttext, -Tdata, -Tbss as replacement for --section-start:
+# RUN: ld.lld %t.o -Ttext=0x100000 -Tdata=0x110000 -Tbss=0x200000 -o %t4
+# RUN: llvm-objdump -section-headers %t4 | FileCheck %s
+
+## Check Ttext-segment X and Ttext-segment=X forms.
+# RUN: ld.lld %t.o -Ttext-segment=0x100000 -Tdata=0x110000 -Tbss=0x200000 -o %t4
+# RUN: llvm-objdump -section-headers %t4 | FileCheck %s
+# RUN: ld.lld %t.o -Ttext-segment 0x100000 -Tdata=0x110000 -Tbss=0x200000 -o %t4
+# RUN: llvm-objdump -section-headers %t4 | FileCheck %s
+
+## The same, but dropped "0x" prefix.
+# RUN: ld.lld %t.o -Ttext=100000 -Tdata=110000 -Tbss=200000 -o %t5
+# RUN: llvm-objdump -section-headers %t5 | FileCheck %s
+
+## Check form without assignment:
+# RUN: ld.lld %t.o -Ttext 0x100000 -Tdata 0x110000 -Tbss 0x200000 -o %t4
+# RUN: llvm-objdump -section-headers %t4 | FileCheck %s
+
+## Errors:
+# RUN: not ld.lld %t.o --section-start .text100000 -o %t2 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR1 %s
+# ERR1: invalid argument: --section-start .text100000
+
+# RUN: not ld.lld %t.o --section-start .text=1Q0000 -o %t3 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR2 %s
+# ERR2: invalid argument: --section-start .text=1Q0000
+
+# RUN: not ld.lld %t.o -Ttext=1w0000 -o %t6 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR3 %s
+# ERR3: invalid argument: --Ttext 1w0000
+
+# RUN: not ld.lld %t.o -Tbss=1w0000 -o %t6 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR4 %s
+# ERR4: invalid argument: --Tbss 1w0000
+
+# RUN: not ld.lld %t.o -Tdata=1w0000 -o %t6 2>&1 \
+# RUN: | FileCheck -check-prefix=ERR5 %s
+# ERR5: invalid argument: --Tdata 1w0000
+
+.text
+.globl _start
+_start:
+ nop
+
+.data
+.long 0
+
+.bss
+.zero 4
diff --git a/test/ELF/segments.s b/test/ELF/segments.s
new file mode 100644
index 000000000000..9307ba39fe46
--- /dev/null
+++ b/test/ELF/segments.s
@@ -0,0 +1,108 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: ld.lld %t -o %t1
+# RUN: llvm-readobj --program-headers %t1 | FileCheck --check-prefix=ROSEGMENT %s
+
+# ROSEGMENT: ProgramHeader {
+# ROSEGMENT: Type: PT_LOAD
+# ROSEGMENT-NEXT: Offset: 0x0
+# ROSEGMENT-NEXT: VirtualAddress:
+# ROSEGMENT-NEXT: PhysicalAddress:
+# ROSEGMENT-NEXT: FileSize:
+# ROSEGMENT-NEXT: MemSize:
+# ROSEGMENT-NEXT: Flags [
+# ROSEGMENT-NEXT: PF_R
+# ROSEGMENT-NEXT: ]
+# ROSEGMENT-NEXT: Alignment: 4096
+# ROSEGMENT-NEXT: }
+# ROSEGMENT-NEXT: ProgramHeader {
+# ROSEGMENT-NEXT: Type: PT_LOAD
+# ROSEGMENT-NEXT: Offset: 0x1000
+# ROSEGMENT-NEXT: VirtualAddress:
+# ROSEGMENT-NEXT: PhysicalAddress:
+# ROSEGMENT-NEXT: FileSize:
+# ROSEGMENT-NEXT: MemSize:
+# ROSEGMENT-NEXT: Flags [
+# ROSEGMENT-NEXT: PF_R
+# ROSEGMENT-NEXT: PF_X
+# ROSEGMENT-NEXT: ]
+# ROSEGMENT-NEXT: Alignment: 4096
+# ROSEGMENT-NEXT: }
+# ROSEGMENT-NEXT: ProgramHeader {
+# ROSEGMENT-NEXT: Type: PT_LOAD
+# ROSEGMENT-NEXT: Offset: 0x2000
+# ROSEGMENT-NEXT: VirtualAddress:
+# ROSEGMENT-NEXT: PhysicalAddress:
+# ROSEGMENT-NEXT: FileSize: 1
+# ROSEGMENT-NEXT: MemSize: 1
+# ROSEGMENT-NEXT: Flags [
+# ROSEGMENT-NEXT: PF_R
+# ROSEGMENT-NEXT: PF_W
+# ROSEGMENT-NEXT: ]
+# ROSEGMENT-NEXT: Alignment: 4096
+# ROSEGMENT-NEXT: }
+
+# RUN: ld.lld -no-rosegment %t -o %t2
+# RUN: llvm-readobj --program-headers %t2 | FileCheck --check-prefix=NOROSEGMENT %s
+
+# NOROSEGMENT: ProgramHeader {
+# NOROSEGMENT: Type: PT_LOAD
+# NOROSEGMENT-NEXT: Offset: 0x0
+# NOROSEGMENT-NEXT: VirtualAddress:
+# NOROSEGMENT-NEXT: PhysicalAddress:
+# NOROSEGMENT-NEXT: FileSize:
+# NOROSEGMENT-NEXT: MemSize:
+# NOROSEGMENT-NEXT: Flags [
+# NOROSEGMENT-NEXT: PF_R
+# NOROSEGMENT-NEXT: PF_X
+# NOROSEGMENT-NEXT: ]
+# NOROSEGMENT-NEXT: Alignment: 4096
+# NOROSEGMENT-NEXT: }
+# NOROSEGMENT-NEXT: ProgramHeader {
+# NOROSEGMENT-NEXT: Type: PT_LOAD
+# NOROSEGMENT-NEXT: Offset: 0x1000
+# NOROSEGMENT-NEXT: VirtualAddress:
+# NOROSEGMENT-NEXT: PhysicalAddress:
+# NOROSEGMENT-NEXT: FileSize:
+# NOROSEGMENT-NEXT: MemSize:
+# NOROSEGMENT-NEXT: Flags [
+# NOROSEGMENT-NEXT: PF_R
+# NOROSEGMENT-NEXT: PF_W
+# NOROSEGMENT-NEXT: ]
+# NOROSEGMENT-NEXT: Alignment: 4096
+# NOROSEGMENT-NEXT: }
+# NOROSEGMENT-NEXT: ProgramHeader {
+# NOROSEGMENT-NEXT: Type: PT_GNU_STACK
+
+# RUN: ld.lld -N %t -o %t3
+# RUN: llvm-readobj --program-headers %t3 | FileCheck --check-prefix=OMAGIC %s
+
+# OMAGIC: ProgramHeader {
+# OMAGIC: Type: PT_LOAD
+# OMAGIC-NEXT: Offset: 0x0
+# OMAGIC-NEXT: VirtualAddress:
+# OMAGIC-NEXT: PhysicalAddress:
+# OMAGIC-NEXT: FileSize:
+# OMAGIC-NEXT: MemSize:
+# OMAGIC-NEXT: Flags [
+# OMAGIC-NEXT: PF_R
+# OMAGIC-NEXT: PF_W
+# OMAGIC-NEXT: PF_X
+# OMAGIC-NEXT: ]
+# OMAGIC-NEXT: Alignment: 4096
+# OMAGIC-NEXT: }
+# OMAGIC-NEXT: ProgramHeader {
+# OMAGIC-NEXT: Type: PT_GNU_STACK
+
+.global _start
+_start:
+ nop
+
+.section .ro,"a"
+nop
+
+.section .rw,"aw"
+nop
+
+.section .rx,"ax"
+nop
diff --git a/test/ELF/shared-be.s b/test/ELF/shared-be.s
index 12eb3131050a..c969793d9d21 100644
--- a/test/ELF/shared-be.s
+++ b/test/ELF/shared-be.s
@@ -22,11 +22,11 @@
// CHECK-NEXT: Tag Type Name/Value
// CHECK-NEXT: 0x000000000000001D RUNPATH foo:bar
// CHECK-NEXT: 0x0000000000000001 NEEDED SharedLibrary ({{.*}}2.so)
+// CHECK-NEXT: 0x0000000000000015 DEBUG 0x0
// CHECK-NEXT: 0x0000000000000007 RELA [[RELADDR]]
// CHECK-NEXT: 0x0000000000000008 RELASZ [[RELSIZE]] (bytes)
// CHECK-NEXT: 0x0000000000000009 RELAENT [[RELENT]] (bytes)
-// CHECK: 0x0000000000000015 DEBUG 0x0
-// CHECK-NEXT: 0x0000000000000000 NULL 0x0
+// CHECK: 0x0000000000000000 NULL 0x0
// CHECK-NEXT: ]
.global _start
diff --git a/test/ELF/shared.s b/test/ELF/shared.s
index 086cc7345125..350ef5c602d0 100644
--- a/test/ELF/shared.s
+++ b/test/ELF/shared.s
@@ -14,7 +14,7 @@
// SO-NEXT: Flags [
// SO-NEXT: ]
// SO-NEXT: Address:
-// SO-NEXT: Offset: 0x1030
+// SO-NEXT: Offset: 0x1038
// SO-NEXT: Size:
// SO-NEXT: Link:
// SO-NEXT: Info:
@@ -184,7 +184,7 @@
// CHECK: DynamicSymbols [
// CHECK-NEXT: Symbol {
-// CHECK-NEXT: Name: @ (0)
+// CHECK-NEXT: Name: @
// CHECK-NEXT: Value: 0x0
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
@@ -223,7 +223,7 @@
// DONT_EXPORT: DynamicSymbols [
// DONT_EXPORT-NEXT: Symbol {
-// DONT_EXPORT-NEXT: Name: @ (0)
+// DONT_EXPORT-NEXT: Name: @
// DONT_EXPORT-NEXT: Value: 0x0
// DONT_EXPORT-NEXT: Size: 0
// DONT_EXPORT-NEXT: Binding: Local (0x0)
@@ -255,6 +255,7 @@
// CHECK-NEXT: Tag Type Name/Value
// CHECK-NEXT: 0x0000001D RUNPATH foo:bar
// CHECK-NEXT: 0x00000001 NEEDED SharedLibrary ({{.*}}2.so)
+// CHECK-NEXT: 0x00000015 DEBUG 0x0
// CHECK-NEXT: 0x00000011 REL [[RELADDR]]
// CHECK-NEXT: 0x00000012 RELSZ [[RELSIZE]] (bytes)
// CHECK-NEXT: 0x00000013 RELENT [[RELENT]] (bytes)
@@ -263,7 +264,6 @@
// CHECK-NEXT: 0x00000005 STRTAB [[DYNSTRADDR]]
// CHECK-NEXT: 0x0000000A STRSZ
// CHECK-NEXT: 0x00000004 HASH [[HASHADDR]]
-// CHECK-NEXT: 0x00000015 DEBUG 0x0
// CHECK-NEXT: 0x00000000 NULL 0x0
// CHECK-NEXT: ]
diff --git a/test/ELF/shf-info-link.test b/test/ELF/shf-info-link.test
new file mode 100644
index 000000000000..ea15655ec89f
--- /dev/null
+++ b/test/ELF/shf-info-link.test
@@ -0,0 +1,32 @@
+# RUN: yaml2obj %s -o %t.o
+# RUN: yaml2obj %S/Inputs/shf-info-link.test -o %t2.o
+# RUN: ld.lld %t.o %t2.o -o %t3.o -r
+# RUN: llvm-readobj -s %t3.o | FileCheck %s
+
+# CHECK-NOT: Name: .rela.text
+# CHECK: Name: .rela.text
+# CHECK-NOT: Name: .rela.text
+
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ - Name: .rela.text
+ Type: SHT_RELA
+ Flags: [ SHF_INFO_LINK ]
+ Link: .symtab
+ Info: .text
+ Relocations:
+ - Offset: 0x0000000000000000
+ Symbol: foo
+ Type: R_X86_64_64
+Symbols:
+ Global:
+ - Name: foo
diff --git a/test/ELF/sort-norosegment.s b/test/ELF/sort-norosegment.s
new file mode 100644
index 000000000000..3026bb2dbf3c
--- /dev/null
+++ b/test/ELF/sort-norosegment.s
@@ -0,0 +1,15 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
+
+# RUN: ld.lld -no-rosegment -o %t1 %t -shared
+# RUN: llvm-readobj -elf-output-style=GNU -s %t1 | FileCheck %s
+
+# CHECK: .text {{.*}} AX
+# CHECK-NEXT: .dynsym {{.*}} A
+# CHECK-NEXT: .hash {{.*}} A
+# CHECK-NEXT: .dynstr {{.*}} A
+# CHECK-NEXT: .dynamic {{.*}} WA
+# CHECK-NEXT: foo {{.*}} WA
+
+.section foo, "aw"
+.byte 0
diff --git a/test/ELF/startstop-gccollect.s b/test/ELF/startstop-gccollect.s
index b0cd41337e34..589ccafafcca 100644
--- a/test/ELF/startstop-gccollect.s
+++ b/test/ELF/startstop-gccollect.s
@@ -12,13 +12,13 @@
# RUN: llvm-objdump -d %tout | FileCheck -check-prefix=DISASM %s
# DISASM: _start:
-# DISASM-NEXT: 11000: 90 nop
+# DISASM-NEXT: 201000: 90 nop
# DISASM-NEXT: Disassembly of section foo:
# DISASM-NEXT: foo:
-# DISASM-NEXT: 11001: 90 nop
+# DISASM-NEXT: 201001: 90 nop
# DISASM-NEXT: Disassembly of section bar:
# DISASM-NEXT: bar:
-# DISASM-NEXT: 11002: 90 nop
+# DISASM-NEXT: 201002: 90 nop
.global _start
.text
diff --git a/test/ELF/startstop-shared.s b/test/ELF/startstop-shared.s
index 77411f3f25c9..80ccf3df0aad 100644
--- a/test/ELF/startstop-shared.s
+++ b/test/ELF/startstop-shared.s
@@ -6,21 +6,23 @@
.data
.quad __start_foo
.section foo,"aw"
-// By default the symbol is hidden.
-// CHECK: R_X86_64_RELATIVE - 0x[[ADDR1:.*]]
.hidden __start_bar
.quad __start_bar
.section bar,"a"
-// References do not affect the visibility.
-// CHECK: R_X86_64_RELATIVE - 0x[[ADDR2:.*]]
+
+// Test that we are able to hide the symbol.
+// CHECK: R_X86_64_RELATIVE - 0x[[ADDR:.*]]
+
+// By default the symbol is visible and we need a dynamic reloc.
+// CHECK: R_X86_64_64 __start_foo 0x0
// CHECK: Name: __start_bar
-// CHECK-NEXT: Value: 0x[[ADDR2]]
+// CHECK-NEXT: Value: 0x[[ADDR]]
// CHECK-NEXT: Size:
// CHECK-NEXT: Binding: Local
// CHECK: Name: __start_foo
-// CHECK-NEXT: Value: 0x[[ADDR1]]
+// CHECK-NEXT: Value:
// CHECK-NEXT: Size:
-// CHECK-NEXT: Binding: Local
+// CHECK-NEXT: Binding: Global
diff --git a/test/ELF/startstop-shared2.s b/test/ELF/startstop-shared2.s
new file mode 100644
index 000000000000..b8bd8abe90d4
--- /dev/null
+++ b/test/ELF/startstop-shared2.s
@@ -0,0 +1,14 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/startstop-shared2.s -o %t.o
+// RUN: ld.lld -o %t.so %t.o -shared
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t2.o
+// RUN: ld.lld -o %t %t2.o %t.so
+// RUN: llvm-objdump -s -h %t | FileCheck %s
+
+// CHECK: foo 00000000 0000000000201008
+
+// CHECK: Contents of section .text:
+// CHECK-NEXT: 201000 08102000 00000000
+
+.quad __start_foo
+.section foo,"ax"
diff --git a/test/ELF/startstop.s b/test/ELF/startstop.s
index a7b2e43e6a5f..326196dafa68 100644
--- a/test/ELF/startstop.s
+++ b/test/ELF/startstop.s
@@ -22,30 +22,46 @@
// SYMBOL: Relocations [
// SYMBOL-NEXT: Section ({{.*}}) .rela.dyn {
-// SYMBOL-NEXT: 0x3000 R_X86_64_RELATIVE - 0x3020
-// SYMBOL-NEXT: 0x3008 R_X86_64_RELATIVE - 0x3021
-// SYMBOL-NEXT: 0x3010 R_X86_64_RELATIVE - 0x3010
-// SYMBOL-NEXT: 0x3018 R_X86_64_RELATIVE - 0x3011
+// SYMBOL-NEXT: 0x3010 R_X86_64_64 __stop_zed1 0x0
+// SYMBOL-NEXT: 0x3018 R_X86_64_64 __stop_zed1 0x1
+// SYMBOL-NEXT: 0x3000 R_X86_64_64 __stop_zed2 0x0
+// SYMBOL-NEXT: 0x3008 R_X86_64_64 __stop_zed2 0x1
// SYMBOL-NEXT: }
// SYMBOL-NEXT: ]
// SYMBOL: Symbol {
// SYMBOL: Name: __start_bar
// SYMBOL: Value: 0x1012
+// SYMBOL: STV_HIDDEN
// SYMBOL: Section: bar
// SYMBOL: }
// SYMBOL-NOT: Section: __stop_bar
// SYMBOL: Symbol {
// SYMBOL: Name: __start_foo
// SYMBOL: Value: 0x100F
+// SYMBOL: STV_HIDDEN
// SYMBOL: Section: foo
// SYMBOL: }
// SYMBOL: Symbol {
// SYMBOL: Name: __stop_foo
// SYMBOL: Value: 0x1012
+// STMBOL: STV_HIDDEN
// SYMBOL: Section: foo
// SYMBOL: }
+// SYMBOL: Symbol {
+// SYMBOL: Name: __stop_zed1
+// SYMBOL: Value: 0x3010
+// STMBOL: Other: 0
+// SYMBOL: Section: zed1
+// SYMBOL: }
+// SYMBOL: Symbol {
+// SYMBOL: Name: __stop_zed2
+// SYMBOL: Value: 0x3020
+// STMBOL: Other: 0
+// SYMBOL: Section: zed2
+// SYMBOL: }
+
.hidden __start_foo
.hidden __stop_foo
.hidden __start_bar
diff --git a/test/ELF/string-gc.s b/test/ELF/string-gc.s
index e7f86a1c6077..3157a79a65cd 100644
--- a/test/ELF/string-gc.s
+++ b/test/ELF/string-gc.s
@@ -14,7 +14,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: s3
-// CHECK-NEXT: Value: 0x10125
+// CHECK-NEXT: Value: 0x200125
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local (0x0)
// CHECK-NEXT: Type: Object (0x1)
@@ -23,7 +23,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: s1
-// CHECK-NEXT: Value: 0x10120
+// CHECK-NEXT: Value: 0x200120
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local (0x0)
// CHECK-NEXT: Type: Object (0x1)
@@ -34,7 +34,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: _start
-// CHECK-NEXT: Value: 0x11000
+// CHECK-NEXT: Value: 0x201000
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global (0x1)
// CHECK-NEXT: Type: Function (0x2)
diff --git a/test/ELF/string-table.s b/test/ELF/string-table.s
index 892c348f6fd0..c783445f8e31 100644
--- a/test/ELF/string-table.s
+++ b/test/ELF/string-table.s
@@ -19,7 +19,7 @@ _start:
// CHECK-NEXT: Flags [
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x10120
+// CHECK-NEXT: Address: 0x200120
// CHECK: Name: foobar
// CHECK-NEXT: Type: SHT_PROGBITS
@@ -59,8 +59,9 @@ _start:
// CHECK-NEXT: EntrySize: 0
// CHECK-NEXT: SectionData (
// CHECK-NEXT: 0000: 00626172 002E7465 78740066 6F6F6261 |.bar..text.fooba|
-// CHECK-NEXT: 0010: 72002E73 796D7461 62002E73 68737472 |r..symtab..shstr|
-// CHECK-NEXT: 0020: 74616200 2E737472 74616200 |tab..strtab.|
+// CHECK-NEXT: 0010: 72002E63 6F6D6D65 6E74002E 73796D74 |r..comment..symt|
+// CHECK-NEXT: 0020: 6162002E 73687374 72746162 002E7374 |ab..shstrtab..st|
+// CHECK-NEXT: 0030: 72746162 00 |rtab.|
// CHECK-NEXT: )
// CHECK-NEXT:}
// CHECK: Name: .strtab
diff --git a/test/ELF/symbol-ordering-file.s b/test/ELF/symbol-ordering-file.s
new file mode 100644
index 000000000000..5a88b8c0827f
--- /dev/null
+++ b/test/ELF/symbol-ordering-file.s
@@ -0,0 +1,44 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t.out
+# RUN: llvm-objdump -s %t.out| FileCheck %s --check-prefix=BEFORE
+
+# BEFORE: Contents of section .foo:
+# BEFORE-NEXT: 201000 11223344 5566
+
+# RUN: echo "_foo4 " > %t_order.txt
+# RUN: echo " _foo3" >> %t_order.txt
+# RUN: echo "_foo5" >> %t_order.txt
+# RUN: echo "_foo2" >> %t_order.txt
+# RUN: echo " " >> %t_order.txt
+# RUN: echo "_foo4" >> %t_order.txt
+# RUN: echo "_bar1" >> %t_order.txt
+# RUN: echo "_foo1" >> %t_order.txt
+
+# RUN: ld.lld --symbol-ordering-file %t_order.txt %t.o -o %t2.out
+# RUN: llvm-objdump -s %t2.out| FileCheck %s --check-prefix=AFTER
+
+# AFTER: Contents of section .foo:
+# AFTER-NEXT: 201000 44335566 2211
+
+.section .foo,"ax",@progbits,unique,1
+_foo1:
+ .byte 0x11
+
+.section .foo,"ax",@progbits,unique,2
+_foo2:
+ .byte 0x22
+
+.section .foo,"ax",@progbits,unique,3
+_foo3:
+ .byte 0x33
+
+.section .foo,"ax",@progbits,unique,4
+_foo4:
+ .byte 0x44
+
+.section .foo,"ax",@progbits,unique,5
+_foo5:
+ .byte 0x55
+_bar1:
+ .byte 0x66
diff --git a/test/ELF/symbol-override.s b/test/ELF/symbol-override.s
index 487885b75b36..8b62d8749aa0 100644
--- a/test/ELF/symbol-override.s
+++ b/test/ELF/symbol-override.s
@@ -25,7 +25,7 @@
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: foo
-// CHECK-NEXT: Value: 0x11000
+// CHECK-NEXT: Value: 0x201000
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: Function
@@ -37,7 +37,7 @@
.text
.globl foo
.type foo,@function
-foo:
+foo:
nop
.text
diff --git a/test/ELF/symbols.s b/test/ELF/symbols.s
index a6c838cf0b74..54dfcbd087cd 100644
--- a/test/ELF/symbols.s
+++ b/test/ELF/symbols.s
@@ -50,7 +50,7 @@ internal:
// CHECK-NEXT: Flags [
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x10158
+// CHECK-NEXT: Address: 0x200158
// CHECK: Name: .text
// CHECK-NEXT: Type: SHT_PROGBITS
@@ -58,7 +58,7 @@ internal:
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_EXECINSTR
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x1100
+// CHECK-NEXT: Address: 0x201000
// CHECK: Name: .bss
// CHECK-NEXT: Type: SHT_NOBITS
@@ -66,7 +66,7 @@ internal:
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x12000
+// CHECK-NEXT: Address: 0x202000
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 4
@@ -82,7 +82,7 @@ internal:
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: hidden
-// CHECK-NEXT: Value: 0x10160
+// CHECK-NEXT: Value: 0x200160
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -93,7 +93,7 @@ internal:
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: internal
-// CHECK-NEXT: Value: 0x10160
+// CHECK-NEXT: Value: 0x200160
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
@@ -104,7 +104,7 @@ internal:
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: _start
-// CHECK-NEXT: Value: 0x11000
+// CHECK-NEXT: Value: 0x201000
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global (0x1)
// CHECK-NEXT: Type: Function
@@ -131,7 +131,7 @@ internal:
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: common
-// CHECK-NEXT: Value: 0x12000
+// CHECK-NEXT: Value: 0x202000
// CHECK-NEXT: Size: 4
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: Object
@@ -140,7 +140,7 @@ internal:
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: foo
-// CHECK-NEXT: Value: 0x11000
+// CHECK-NEXT: Value: 0x201000
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Weak (0x2)
// CHECK-NEXT: Type: Object
@@ -149,7 +149,7 @@ internal:
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: protected
-// CHECK-NEXT: Value: 0x10160
+// CHECK-NEXT: Value: 0x200160
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: None
@@ -160,7 +160,7 @@ internal:
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: zed
-// CHECK-NEXT: Value: 0x10158
+// CHECK-NEXT: Value: 0x200158
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global (0x1)
// CHECK-NEXT: Type: None
@@ -169,7 +169,7 @@ internal:
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: zed2
-// CHECK-NEXT: Value: 0x1015C
+// CHECK-NEXT: Value: 0x20015C
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: None
@@ -178,7 +178,7 @@ internal:
// CHECK-NEXT: }
// CHECK-NEXT: Symbol {
// CHECK-NEXT: Name: zed3
-// CHECK-NEXT: Value: 0x10160
+// CHECK-NEXT: Value: 0x200160
// CHECK-NEXT: Size: 4
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: None
diff --git a/test/ELF/synthetic-got.s b/test/ELF/synthetic-got.s
new file mode 100644
index 000000000000..c84d27827aef
--- /dev/null
+++ b/test/ELF/synthetic-got.s
@@ -0,0 +1,32 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS { }" > %t0.script
+# RUN: ld.lld -shared %t.o -o %t0.out --script %t0.script
+# RUN: llvm-objdump -section-headers %t0.out | FileCheck %s --check-prefix=GOT
+# RUN: llvm-objdump -s -section=.got -section=.got.plt %t0.out \
+# RUN: | FileCheck %s --check-prefix=GOTDATA
+
+# GOT: Sections:
+# GOT: 9 .got 00000008 00000000000001b0 DATA
+# GOT-NEXT: 10 .got.plt 00000020 00000000000001b8 DATA
+# GOTDATA: Contents of section .got:
+# GOTDATA-NEXT: 01b0 00000000 00000000
+# GOTDATA-NEXT: Contents of section .got.plt:
+# GOTDATA-NEXT: 01b8 e0000000 00000000 00000000 00000000
+# GOTDATA-NEXT: 01c8 00000000 00000000 d6000000 00000000
+
+# RUN: echo "SECTIONS { .mygot : { *(.got) *(.got.plt) } }" > %t1.script
+# RUN: ld.lld -shared %t.o -o %t1.out --script %t1.script
+# RUN: llvm-objdump -section-headers %t1.out | FileCheck %s --check-prefix=MYGOT
+# RUN: llvm-objdump -s -section=.mygot %t1.out | FileCheck %s --check-prefix=MYGOTDATA
+
+# MYGOT: Sections:
+# MYGOT: 9 .mygot 00000028 00000000000001b0 DATA
+# MYGOT-NOT: .got
+# MYGOT-NOT: .got.plt
+# MYGOTDATA: 01b0 00000000 00000000 e0000000 00000000
+# MYGOTDATA-NEXT: 01c0 00000000 00000000 00000000 00000000
+# MYGOTDATA-NEXT: 01d0 d6000000 00000000
+
+mov bar@gotpcrel(%rip), %rax
+call foo@plt
diff --git a/test/ELF/sysroot.s b/test/ELF/sysroot.s
index 38fd8cdde402..0b7b17260e58 100644
--- a/test/ELF/sysroot.s
+++ b/test/ELF/sysroot.s
@@ -9,7 +9,7 @@
// Should not link because of undefined symbol _bar
// RUN: not ld.lld -o %t/r %t/m.o 2>&1 \
// RUN: | FileCheck --check-prefix=UNDEFINED %s
-// UNDEFINED: undefined symbol: _bar
+// UNDEFINED: error: {{.*}}:(.text+0x1): undefined symbol '_bar'
// We need to be sure that there is no suitable library in the /lib directory
// RUN: not ld.lld -o %t/r %t/m.o -L/lib -l:libls.a 2>&1 \
@@ -34,3 +34,4 @@
.globl _start,_bar
_start:
+ call _bar
diff --git a/test/ELF/tls-got.s b/test/ELF/tls-got.s
index f36d94e40cb1..450dd634d7a9 100644
--- a/test/ELF/tls-got.s
+++ b/test/ELF/tls-got.s
@@ -24,23 +24,23 @@
// CHECK: Relocations [
// CHECK-NEXT: Section (4) .rela.dyn {
-// CHECK-NEXT: 0x120B8 R_X86_64_TPOFF64 tls0 0x0
+// CHECK-NEXT: 0x2020B8 R_X86_64_TPOFF64 tls0 0x0
// CHECK-NEXT: [[ADDR]] R_X86_64_TPOFF64 tls1 0x0
// CHECK-NEXT: }
// CHECK-NEXT: ]
-//0x11000 + 4249 + 7 = 0x120B0
-//0x1100A + 4247 + 7 = 0x120B8
-//0x11014 + 4237 + 7 = 0x120B8
+//0x201000 + 4249 + 7 = 0x2020B0
+//0x20100A + 4247 + 7 = 0x2020B8
+//0x201014 + 4237 + 7 = 0x2020B8
//DISASM: Disassembly of section .text:
//DISASM-NEXT: main:
-//DISASM-NEXT: 11000: 48 8b 05 a9 10 00 00 movq 4265(%rip), %rax
-//DISASM-NEXT: 11007: 64 8b 00 movl %fs:(%rax), %eax
-//DISASM-NEXT: 1100a: 48 8b 05 a7 10 00 00 movq 4263(%rip), %rax
-//DISASM-NEXT: 11011: 64 8b 00 movl %fs:(%rax), %eax
-//DISASM-NEXT: 11014: 48 8b 05 9d 10 00 00 movq 4253(%rip), %rax
-//DISASM-NEXT: 1101b: 64 8b 00 movl %fs:(%rax), %eax
-//DISASM-NEXT: 1101e: c3 retq
+//DISASM-NEXT: 201000: 48 8b 05 a9 10 00 00 movq 4265(%rip), %rax
+//DISASM-NEXT: 201007: 64 8b 00 movl %fs:(%rax), %eax
+//DISASM-NEXT: 20100a: 48 8b 05 a7 10 00 00 movq 4263(%rip), %rax
+//DISASM-NEXT: 201011: 64 8b 00 movl %fs:(%rax), %eax
+//DISASM-NEXT: 201014: 48 8b 05 9d 10 00 00 movq 4253(%rip), %rax
+//DISASM-NEXT: 20101b: 64 8b 00 movl %fs:(%rax), %eax
+//DISASM-NEXT: 20101e: c3 retq
.section .tdata,"awT",@progbits
diff --git a/test/ELF/tls-mismatch.s b/test/ELF/tls-mismatch.s
index fb70229e1a52..aee1f73dedce 100644
--- a/test/ELF/tls-mismatch.s
+++ b/test/ELF/tls-mismatch.s
@@ -2,7 +2,7 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/tls-mismatch.s -o %t2
// RUN: not ld.lld %t %t2 -o %t3 2>&1 | FileCheck %s
-// CHECK: TLS attribute mismatch for symbol: tlsvar
+// CHECK: TLS attribute mismatch for symbol 'tlsvar'
.globl _start,tlsvar
_start:
diff --git a/test/ELF/tls-offset.s b/test/ELF/tls-offset.s
index 8f5a46d988da..1734f7fb9e9c 100644
--- a/test/ELF/tls-offset.s
+++ b/test/ELF/tls-offset.s
@@ -30,7 +30,7 @@ _start:
// CHECK-NEXT: SHF_TLS
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x12000
+// CHECK-NEXT: Address: 0x202000
// CHECK-NEXT: Offset: 0x2000
// CHECK-NEXT: Size: 4
@@ -41,7 +41,7 @@ _start:
// CHECK-NEXT: SHF_TLS
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x12010
+// CHECK-NEXT: Address: 0x202010
// CHECK-NEXT: Offset: 0x2004
// CHECK-NEXT: Size: 16
@@ -51,6 +51,6 @@ _start:
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x12004
+// CHECK-NEXT: Address: 0x202004
// CHECK-NEXT: Offset: 0x2004
// CHECK-NEXT: Size: 4
diff --git a/test/ELF/tls-opt-gdie.s b/test/ELF/tls-opt-gdie.s
index 6ed370e3f0b2..c423a3e0f246 100644
--- a/test/ELF/tls-opt-gdie.s
+++ b/test/ELF/tls-opt-gdie.s
@@ -13,7 +13,7 @@
//RELOC-NEXT: SHF_ALLOC
//RELOC-NEXT: SHF_WRITE
//RELOC-NEXT: ]
-//RELOC-NEXT: Address: 0x120B0
+//RELOC-NEXT: Address: 0x2020B0
//RELOC-NEXT: Offset: 0x20B0
//RELOC-NEXT: Size: 16
//RELOC-NEXT: Link: 0
@@ -23,19 +23,19 @@
//RELOC-NEXT: }
//RELOC: Relocations [
//RELOC-NEXT: Section (4) .rela.dyn {
-//RELOC-NEXT: 0x120B0 R_X86_64_TPOFF64 tlsshared0 0x0
-//RELOC-NEXT: 0x120B8 R_X86_64_TPOFF64 tlsshared1 0x0
+//RELOC-NEXT: 0x2020B0 R_X86_64_TPOFF64 tlsshared0 0x0
+//RELOC-NEXT: 0x2020B8 R_X86_64_TPOFF64 tlsshared1 0x0
//RELOC-NEXT: }
//RELOC-NEXT: ]
-//0x11009 + (4256 + 7) = 0x120B0
-//0x11019 + (4248 + 7) = 0x120B8
+//0x201009 + (4256 + 7) = 0x2020B0
+//0x201019 + (4248 + 7) = 0x2020B8
// DISASM: Disassembly of section .text:
// DISASM-NEXT: _start:
-// DISASM-NEXT: 11000: {{.*}} movq %fs:0, %rax
-// DISASM-NEXT: 11009: {{.*}} addq 4256(%rip), %rax
-// DISASM-NEXT: 11010: {{.*}} movq %fs:0, %rax
-// DISASM-NEXT: 11019: {{.*}} addq 4248(%rip), %rax
+// DISASM-NEXT: 201000: {{.*}} movq %fs:0, %rax
+// DISASM-NEXT: 201009: {{.*}} addq 4256(%rip), %rax
+// DISASM-NEXT: 201010: {{.*}} movq %fs:0, %rax
+// DISASM-NEXT: 201019: {{.*}} addq 4248(%rip), %rax
.section .text
.globl _start
diff --git a/test/ELF/tls-opt-local.s b/test/ELF/tls-opt-local.s
index d2904ac6fba0..633c22b0611c 100644
--- a/test/ELF/tls-opt-local.s
+++ b/test/ELF/tls-opt-local.s
@@ -8,18 +8,18 @@
// DISASM: Disassembly of section .text:
// DISASM-NEXT: _start:
-// DISASM-NEXT: 11000: 48 c7 c0 f8 ff ff ff movq $-8, %rax
-// DISASM-NEXT: 11007: 49 c7 c7 f8 ff ff ff movq $-8, %r15
-// DISASM-NEXT: 1100e: 48 8d 80 f8 ff ff ff leaq -8(%rax), %rax
-// DISASM-NEXT: 11015: 4d 8d bf f8 ff ff ff leaq -8(%r15), %r15
-// DISASM-NEXT: 1101c: 48 81 c4 f8 ff ff ff addq $-8, %rsp
-// DISASM-NEXT: 11023: 49 81 c4 f8 ff ff ff addq $-8, %r12
-// DISASM-NEXT: 1102a: 48 c7 c0 fc ff ff ff movq $-4, %rax
-// DISASM-NEXT: 11031: 49 c7 c7 fc ff ff ff movq $-4, %r15
-// DISASM-NEXT: 11038: 48 8d 80 fc ff ff ff leaq -4(%rax), %rax
-// DISASM-NEXT: 1103f: 4d 8d bf fc ff ff ff leaq -4(%r15), %r15
-// DISASM-NEXT: 11046: 48 81 c4 fc ff ff ff addq $-4, %rsp
-// DISASM-NEXT: 1104d: 49 81 c4 fc ff ff ff addq $-4, %r12
+// DISASM-NEXT: 201000: 48 c7 c0 f8 ff ff ff movq $-8, %rax
+// DISASM-NEXT: 201007: 49 c7 c7 f8 ff ff ff movq $-8, %r15
+// DISASM-NEXT: 20100e: 48 8d 80 f8 ff ff ff leaq -8(%rax), %rax
+// DISASM-NEXT: 201015: 4d 8d bf f8 ff ff ff leaq -8(%r15), %r15
+// DISASM-NEXT: 20101c: 48 81 c4 f8 ff ff ff addq $-8, %rsp
+// DISASM-NEXT: 201023: 49 81 c4 f8 ff ff ff addq $-8, %r12
+// DISASM-NEXT: 20102a: 48 c7 c0 fc ff ff ff movq $-4, %rax
+// DISASM-NEXT: 201031: 49 c7 c7 fc ff ff ff movq $-4, %r15
+// DISASM-NEXT: 201038: 48 8d 80 fc ff ff ff leaq -4(%rax), %rax
+// DISASM-NEXT: 20103f: 4d 8d bf fc ff ff ff leaq -4(%r15), %r15
+// DISASM-NEXT: 201046: 48 81 c4 fc ff ff ff addq $-4, %rsp
+// DISASM-NEXT: 20104d: 49 81 c4 fc ff ff ff addq $-4, %r12
.section .tbss,"awT",@nobits
diff --git a/test/ELF/tls-opt.s b/test/ELF/tls-opt.s
index 52468f16b09d..6835e06f3402 100644
--- a/test/ELF/tls-opt.s
+++ b/test/ELF/tls-opt.s
@@ -7,36 +7,36 @@
// NORELOC-NEXT: ]
// DISASM: _start:
-// DISASM-NEXT: 11000: 48 c7 c0 f8 ff ff ff movq $-8, %rax
-// DISASM-NEXT: 11007: 49 c7 c7 f8 ff ff ff movq $-8, %r15
-// DISASM-NEXT: 1100e: 48 8d 80 f8 ff ff ff leaq -8(%rax), %rax
-// DISASM-NEXT: 11015: 4d 8d bf f8 ff ff ff leaq -8(%r15), %r15
-// DISASM-NEXT: 1101c: 48 81 c4 f8 ff ff ff addq $-8, %rsp
-// DISASM-NEXT: 11023: 49 81 c4 f8 ff ff ff addq $-8, %r12
-// DISASM-NEXT: 1102a: 48 c7 c0 fc ff ff ff movq $-4, %rax
-// DISASM-NEXT: 11031: 49 c7 c7 fc ff ff ff movq $-4, %r15
-// DISASM-NEXT: 11038: 48 8d 80 fc ff ff ff leaq -4(%rax), %rax
-// DISASM-NEXT: 1103f: 4d 8d bf fc ff ff ff leaq -4(%r15), %r15
-// DISASM-NEXT: 11046: 48 81 c4 fc ff ff ff addq $-4, %rsp
-// DISASM-NEXT: 1104d: 49 81 c4 fc ff ff ff addq $-4, %r12
+// DISASM-NEXT: 201000: 48 c7 c0 f8 ff ff ff movq $-8, %rax
+// DISASM-NEXT: 201007: 49 c7 c7 f8 ff ff ff movq $-8, %r15
+// DISASM-NEXT: 20100e: 48 8d 80 f8 ff ff ff leaq -8(%rax), %rax
+// DISASM-NEXT: 201015: 4d 8d bf f8 ff ff ff leaq -8(%r15), %r15
+// DISASM-NEXT: 20101c: 48 81 c4 f8 ff ff ff addq $-8, %rsp
+// DISASM-NEXT: 201023: 49 81 c4 f8 ff ff ff addq $-8, %r12
+// DISASM-NEXT: 20102a: 48 c7 c0 fc ff ff ff movq $-4, %rax
+// DISASM-NEXT: 201031: 49 c7 c7 fc ff ff ff movq $-4, %r15
+// DISASM-NEXT: 201038: 48 8d 80 fc ff ff ff leaq -4(%rax), %rax
+// DISASM-NEXT: 20103f: 4d 8d bf fc ff ff ff leaq -4(%r15), %r15
+// DISASM-NEXT: 201046: 48 81 c4 fc ff ff ff addq $-4, %rsp
+// DISASM-NEXT: 20104d: 49 81 c4 fc ff ff ff addq $-4, %r12
// LD to LE:
-// DISASM-NEXT: 11054: 66 66 66 64 48 8b 04 25 00 00 00 00 movq %fs:0, %rax
-// DISASM-NEXT: 11060: 48 8d 88 f8 ff ff ff leaq -8(%rax), %rcx
-// DISASM-NEXT: 11067: 66 66 66 64 48 8b 04 25 00 00 00 00 movq %fs:0, %rax
-// DISASM-NEXT: 11073: 48 8d 88 fc ff ff ff leaq -4(%rax), %rcx
+// DISASM-NEXT: 201054: 66 66 66 64 48 8b 04 25 00 00 00 00 movq %fs:0, %rax
+// DISASM-NEXT: 201060: 48 8d 88 f8 ff ff ff leaq -8(%rax), %rcx
+// DISASM-NEXT: 201067: 66 66 66 64 48 8b 04 25 00 00 00 00 movq %fs:0, %rax
+// DISASM-NEXT: 201073: 48 8d 88 fc ff ff ff leaq -4(%rax), %rcx
// GD to LE:
-// DISASM-NEXT: 1107a: 64 48 8b 04 25 00 00 00 00 movq %fs:0, %rax
-// DISASM-NEXT: 11083: 48 8d 80 f8 ff ff ff leaq -8(%rax), %rax
-// DISASM-NEXT: 1108a: 64 48 8b 04 25 00 00 00 00 movq %fs:0, %rax
-// DISASM-NEXT: 11093: 48 8d 80 fc ff ff ff leaq -4(%rax), %rax
+// DISASM-NEXT: 20107a: 64 48 8b 04 25 00 00 00 00 movq %fs:0, %rax
+// DISASM-NEXT: 201083: 48 8d 80 f8 ff ff ff leaq -8(%rax), %rax
+// DISASM-NEXT: 20108a: 64 48 8b 04 25 00 00 00 00 movq %fs:0, %rax
+// DISASM-NEXT: 201093: 48 8d 80 fc ff ff ff leaq -4(%rax), %rax
// LD to LE:
// DISASM: _DTPOFF64_1:
-// DISASM-NEXT: 1109a: f8 clc
+// DISASM-NEXT: 20109a: f8 clc
// DISASM: _DTPOFF64_2:
-// DISASM-NEXT: 110a3: fc cld
+// DISASM-NEXT: 2010a3: fc cld
.type tls0,@object
.section .tbss,"awT",@nobits
@@ -88,7 +88,7 @@ _start:
.word 0x6666
rex64
call __tls_get_addr@plt
-
+
// LD to LE
_DTPOFF64_1:
.quad tls0@DTPOFF
diff --git a/test/ELF/tls-relocatable.s b/test/ELF/tls-relocatable.s
new file mode 100644
index 000000000000..2743861c1de0
--- /dev/null
+++ b/test/ELF/tls-relocatable.s
@@ -0,0 +1,21 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o
+// RUN: ld.lld %t.o -r -o %tr.o
+// RUN: ld.lld %tr.o -shared -o %t1
+// RUN: llvm-readobj -t %t1 | FileCheck %s
+
+// CHECK: Symbol {
+// CHECK: Name: tls0
+// CHECK-NEXT: Value: 0x0
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Global
+// CHECK-NEXT: Type: TLS
+// CHECK-NEXT: Other: 0
+// CHECK-NEXT: Section: .tdata
+// CHECK-NEXT: }
+
+.type tls0,@object
+.section .tdata,"awT",@progbits
+.globl tls0
+tls0:
+ .long 0
diff --git a/test/ELF/tls-static.s b/test/ELF/tls-static.s
index 4c515abb1252..b098b68b03f1 100644
--- a/test/ELF/tls-static.s
+++ b/test/ELF/tls-static.s
@@ -10,4 +10,4 @@
_start:
call __tls_get_addr
-// CHECK: undefined symbol: __tls_get_addr
+// CHECK: error: {{.*}}:(.text+0x1): undefined symbol '__tls_get_addr'
diff --git a/test/ELF/tls-weak-undef.s b/test/ELF/tls-weak-undef.s
new file mode 100644
index 000000000000..7aa6ef13a2af
--- /dev/null
+++ b/test/ELF/tls-weak-undef.s
@@ -0,0 +1,16 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: ld.lld %t.o -o %t --gc-sections
+
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux \
+// RUN: %p/Inputs/tls-in-archive.s -o %t1.o
+// RUN: llvm-ar cru %t.a %t1.o
+// RUN: ld.lld %t.o %t.a -o %t
+
+// Check that lld doesn't crash because we don't reference
+// the TLS phdr when it's not created.
+ .globl _start
+_start:
+ movq foo@gottpoff(%rip), %rax
+ .section .tbss,"awT",@nobits
+ .weak foo
diff --git a/test/ELF/tls.s b/test/ELF/tls.s
index 525890b087e2..a5b95597518b 100644
--- a/test/ELF/tls.s
+++ b/test/ELF/tls.s
@@ -90,9 +90,9 @@ d:
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// 0x1200C = TBSS_ADDR + 4
+// 0x20200C = TBSS_ADDR + 4
-// CHECK-NEXT: Address: 0x1200C
+// CHECK-NEXT: Address: 0x20200C
// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 4
// CHECK-NEXT: Link:
@@ -164,7 +164,7 @@ d:
// DIS: Disassembly of section .text:
// DIS-NEXT: _start:
-// DIS-NEXT: 11000: {{.+}} movl %fs:-8, %eax
-// DIS-NEXT: 11008: {{.+}} movl %fs:-16, %eax
-// DIS-NEXT: 11010: {{.+}} movl %fs:-4, %eax
-// DIS-NEXT: 11018: {{.+}} movl %fs:-12, %eax
+// DIS-NEXT: 201000: {{.+}} movl %fs:-8, %eax
+// DIS-NEXT: 201008: {{.+}} movl %fs:-16, %eax
+// DIS-NEXT: 201010: {{.+}} movl %fs:-4, %eax
+// DIS-NEXT: 201018: {{.+}} movl %fs:-12, %eax
diff --git a/test/ELF/ttext-tdata-tbss.s b/test/ELF/ttext-tdata-tbss.s
new file mode 100644
index 000000000000..c31c56e75692
--- /dev/null
+++ b/test/ELF/ttext-tdata-tbss.s
@@ -0,0 +1,63 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+
+## Show what regular output gives to us.
+# RUN: ld.lld %t.o -o %t1
+# RUN: llvm-readobj --elf-output-style=GNU -l -s %t1 | FileCheck %s
+# CHECK: .rodata PROGBITS 0000000000200158 000158 000008
+# CHECK-NEXT: .text PROGBITS 0000000000201000 001000 000001
+# CHECK-NEXT: .aw PROGBITS 0000000000202000 002000 000008
+# CHECK-NEXT: .data PROGBITS 0000000000202008 002008 000008
+# CHECK-NEXT: .bss NOBITS 0000000000202010 002010 000008
+# CHECK: PHDR
+# CHECK-NEXT: LOAD 0x000000 0x0000000000200000
+
+## With .text at 0 there is no space to allocate the headers.
+# RUN: ld.lld -Ttext 0x0 -Tdata 0x4000 -Tbss 0x8000 %t.o -o %t2
+# RUN: llvm-readobj --elf-output-style=GNU -l -s %t2 | FileCheck %s --check-prefix=USER1
+# USER1: .text PROGBITS 0000000000000000 001000 000001
+# USER1-NEXT: .data PROGBITS 0000000000004000 002000 000008
+# USER1-NEXT: .bss NOBITS 0000000000008000 002008 000008
+# USER1-NEXT: .rodata PROGBITS 0000000000009000 003000 000008
+# USER1-NEXT: .aw PROGBITS 000000000000a000 004000 000008
+# USER1: PHDR
+# USER1-NEXT: LOAD 0x001000 0x0000000000000000
+
+## With .text at 0x1000 there is space to allocate the headers.
+# RUN: ld.lld -Ttext 0x1000 -Tdata 0x4000 -Tbss 0x8000 %t.o -o %t3
+# RUN: llvm-readobj --elf-output-style=GNU -l -s %t3 | FileCheck %s --check-prefix=USER2
+# USER2: .text PROGBITS 0000000000001000 001000 000001
+# USER2-NEXT: .data PROGBITS 0000000000004000 002000 000008
+# USER2-NEXT: .bss NOBITS 0000000000008000 002008 000008
+# USER2-NEXT: .rodata PROGBITS 0000000000009000 003000 000008
+# USER2-NEXT: .aw PROGBITS 000000000000a000 004000 000008
+# USER2: PHDR
+# USER2-NEXT: LOAD 0x000000 0x0000000000000000
+
+## With .text well above 200000 we don't need to change the image base
+# RUN: ld.lld -Ttext 0x201000 %t.o -o %t4
+# RUN: llvm-readobj --elf-output-style=GNU -l -s %t4 | FileCheck %s --check-prefix=USER3
+# USER3: .text PROGBITS 0000000000201000 001000 000001
+# USER3-NEX: .rodata PROGBITS 0000000000202000 002000 000008
+# USER3-NEX: .aw PROGBITS 0000000000203000 003000 000008
+# USER3-NEX: .data PROGBITS 0000000000203008 003008 000008
+# USER3-NEX: .bss NOBITS 0000000000203010 003010 000008
+# USER3: PHDR
+# USER3-NEXT: LOAD 0x000000 0x0000000000200000
+
+.text
+.globl _start
+_start:
+ nop
+
+.section .rodata,"a"
+ .quad 0
+
+.section .aw,"aw"
+ .quad 0
+
+.section .data,"aw"
+ .quad 0
+
+.section .bss,"",@nobits
+ .quad 0
diff --git a/test/ELF/undef-shared.s b/test/ELF/undef-shared.s
index ed9103571443..3f01ec25f511 100644
--- a/test/ELF/undef-shared.s
+++ b/test/ELF/undef-shared.s
@@ -1,14 +1,19 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
# RUN: not ld.lld %t.o -o %t.so -shared 2>&1 | FileCheck %s
-# CHECK: undefined symbol: hidden in {{.*}}
+# CHECK: error: {{.*}}:(.data+0x0): undefined symbol 'hidden'
.global hidden
.hidden hidden
-# CHECK: undefined symbol: internal in {{.*}}
+# CHECK: error: {{.*}}:(.data+0x8): undefined symbol 'internal'
.global internal
.internal internal
-# CHECK: undefined symbol: protected in {{.*}}
+# CHECK: error: {{.*}}:(.data+0x10): undefined symbol 'protected'
.global protected
.protected protected
+
+.section .data, "a"
+ .quad hidden
+ .quad internal
+ .quad protected
diff --git a/test/ELF/undef-start.s b/test/ELF/undef-start.s
index bf1d89853f59..590d0a80096b 100644
--- a/test/ELF/undef-start.s
+++ b/test/ELF/undef-start.s
@@ -1,4 +1,3 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: not ld.lld %t -o %t2 2>&1 | FileCheck %s
-# CHECK: undefined symbol: _start
+# RUN: ld.lld %t -o %t2 2>&1
# REQUIRES: x86
diff --git a/test/ELF/undef-version-script.s b/test/ELF/undef-version-script.s
index 9f0a5a49e72e..529728328747 100644
--- a/test/ELF/undef-version-script.s
+++ b/test/ELF/undef-version-script.s
@@ -8,7 +8,7 @@
# CHECK: DynamicSymbols [
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: @ (0)
+# CHECK-NEXT: Name: @
# CHECK-NEXT: Value: 0x0
# CHECK-NEXT: Size: 0
# CHECK-NEXT: Binding: Local (0x0)
@@ -17,7 +17,7 @@
# CHECK-NEXT: Section: Undefined (0x0)
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: bar@ (1)
+# CHECK-NEXT: Name: bar@
# CHECK-NEXT: Value: 0x0
# CHECK-NEXT: Size: 0
# CHECK-NEXT: Binding: Weak (0x2)
@@ -26,7 +26,7 @@
# CHECK-NEXT: Section: Undefined (0x0)
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: foo@ (5)
+# CHECK-NEXT: Name: foo@
# CHECK-NEXT: Value: 0x0
# CHECK-NEXT: Size: 0
# CHECK-NEXT: Binding: Global (0x1)
diff --git a/test/ELF/undef-with-plt-addr.s b/test/ELF/undef-with-plt-addr.s
index 792d85f3da61..e30cd90d380f 100644
--- a/test/ELF/undef-with-plt-addr.s
+++ b/test/ELF/undef-with-plt-addr.s
@@ -19,13 +19,13 @@ movabsq $set_data, %rax
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_EXECINSTR
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x11010
+// CHECK-NEXT: Address: 0x201010
// CHECK: Section ({{.*}}) .rela.dyn {
-// CHECK-NEXT: 0x13000 R_X86_64_64 foo 0x0
+// CHECK-NEXT: 0x203000 R_X86_64_64 foo 0x0
// CHECK-NEXT: }
// CHECK-NEXT: Section ({{.*}}) .rela.plt {
-// CHECK-NEXT: 0x13020 R_X86_64_JUMP_SLOT set_data 0x0
+// CHECK-NEXT: 0x203020 R_X86_64_JUMP_SLOT set_data 0x0
// CHECK-NEXT: }
// CHECK: Name: foo
@@ -37,7 +37,7 @@ movabsq $set_data, %rax
// CHECK-NEXT: Section: Undefined
// CHECK: Name: set_data
-// CHECK-NEXT: Value: 0x11020
+// CHECK-NEXT: Value: 0x201020
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: Function
diff --git a/test/ELF/undef.s b/test/ELF/undef.s
index c8211c73866f..5fcd75f948b0 100644
--- a/test/ELF/undef.s
+++ b/test/ELF/undef.s
@@ -1,15 +1,27 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/undef.s -o %t2.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/undef-debug.s -o %t3.o
# RUN: llvm-ar rc %t2.a %t2.o
-# RUN: not ld.lld %t.o %t2.a -o %t.exe 2>&1 | FileCheck %s
-# RUN: not ld.lld -pie %t.o %t2.a -o %t.exe 2>&1 | FileCheck %s
-# CHECK: undefined symbol: bar in {{.*}}
-# CHECK: undefined symbol: foo in {{.*}}
-# CHECK: undefined symbol: zed2 in {{.*}}2.a({{.*}}.o)
+# RUN: not ld.lld %t.o %t2.a %t3.o -o %t.exe 2>&1 | FileCheck %s
+# RUN: not ld.lld -pie %t.o %t2.a %t3.o -o %t.exe 2>&1 | FileCheck %s
+# CHECK: error: undef.s:(.text+0x1): undefined symbol 'foo'
+# CHECK: error: undef.s:(.text+0x6): undefined symbol 'bar'
+# CHECK: error: undef.s:(.text+0x10): undefined symbol 'foo(int)'
+# CHECK: error: {{.*}}2.a({{.*}}.o):(.text+0x0): undefined symbol 'zed2'
+# CHECK: error: dir/undef-debug.s:3: undefined symbol 'zed3'
+# CHECK: error: dir/undef-debug.s:7: undefined symbol 'zed4'
+# CHECK: error: dir/undef-debug.s:11: undefined symbol 'zed5'
+
+# RUN: not ld.lld %t.o %t2.a -o %t.exe -no-demangle 2>&1 | \
+# RUN: FileCheck -check-prefix=NO-DEMANGLE %s
+# NO-DEMANGLE: error: undef.s:(.text+0x10): undefined symbol '_Z3fooi'
+
+.file "undef.s"
.globl _start
_start:
call foo
call bar
call zed1
+ call _Z3fooi
diff --git a/test/ELF/undefined-opt.s b/test/ELF/undefined-opt.s
index ddd34f49b748..d8b793d7bab4 100644
--- a/test/ELF/undefined-opt.s
+++ b/test/ELF/undefined-opt.s
@@ -31,6 +31,8 @@
# Use the option couple of times, both short and long forms
# RUN: ld.lld -o %t3 %t.o %tar.a -u bar --undefined=abs
# RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=TWO-UNDEFINED %s
+# RUN: ld.lld -o %t3 %t.o %tar.a -u bar --undefined abs
+# RUN: llvm-readobj --symbols %t3 | FileCheck --check-prefix=TWO-UNDEFINED %s
# TWO-UNDEFINED: Symbols [
# TWO-UNDEFINED: Name: abs
# TWO-UNDEFINED: Name: big
diff --git a/test/ELF/unresolved-symbols.s b/test/ELF/unresolved-symbols.s
index 2fa59cb0ffd7..3c33b9245786 100644
--- a/test/ELF/unresolved-symbols.s
+++ b/test/ELF/unresolved-symbols.s
@@ -6,7 +6,7 @@
## Check that %t2.o contains undefined symbol undef.
# RUN: not ld.lld %t1.o %t2.o -o %t 2>&1 | \
# RUN: FileCheck -check-prefix=UNDCHECK %s
-# UNDCHECK: undefined symbol: undef in {{.*}}2.o
+# UNDCHECK: error: {{.*}}2.o:(.text+0x1): undefined symbol 'undef'
## Error out if unknown option value was set.
# RUN: not ld.lld %t1.o %t2.o -o %t --unresolved-symbols=xxx 2>&1 | \
@@ -19,7 +19,7 @@
# RUN: llvm-readobj %t1_1 > /dev/null 2>&1
# RUN: not ld.lld %t2.o -o %t1_2 --unresolved-symbols=ignore-all --no-undefined 2>&1 | \
# RUN: FileCheck -check-prefix=ERRUND %s
-# ERRUND: undefined symbol: undef
+# ERRUND: error: {{.*}}:(.text+0x1): undefined symbol 'undef'
## Also ignore all should not produce error for symbols from DSOs.
# RUN: ld.lld %t1.o %t.so -o %t1_3 --unresolved-symbols=ignore-all
# RUN: llvm-readobj %t1_3 > /dev/null 2>&1
diff --git a/test/ELF/verdef-defaultver.s b/test/ELF/verdef-defaultver.s
index 4d847344f996..c37c90a66d92 100644
--- a/test/ELF/verdef-defaultver.s
+++ b/test/ELF/verdef-defaultver.s
@@ -1,12 +1,8 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/verdef-defaultver.s -o %t1
-# RUN: echo "LIBSAMPLE_1.0{ \
-# RUN: global: a; \
-# RUN: local: *; }; \
-# RUN: LIBSAMPLE_2.0{ \
-# RUN: global: b; c; \
-# RUN: }LIBSAMPLE_1.0;" > %t.script
+# RUN: echo "V1 { global: a; local: *; };" > %t.script
+# RUN: echo "V2 { global: b; c; } V1;" >> %t.script
# RUN: ld.lld -shared -soname shared %t1 --version-script %t.script -o %t.so
# RUN: llvm-readobj -V -dyn-symbols %t.so | FileCheck --check-prefix=DSO %s
@@ -21,7 +17,7 @@
# DSO-NEXT: Section: Undefined
# DSO-NEXT: }
# DSO-NEXT: Symbol {
-# DSO-NEXT: Name: a@@LIBSAMPLE_1.0
+# DSO-NEXT: Name: a@@V1
# DSO-NEXT: Value: 0x1000
# DSO-NEXT: Size: 0
# DSO-NEXT: Binding: Global
@@ -30,7 +26,7 @@
# DSO-NEXT: Section: .text
# DSO-NEXT: }
# DSO-NEXT: Symbol {
-# DSO-NEXT: Name: b@@LIBSAMPLE_2.0
+# DSO-NEXT: Name: b@@V2
# DSO-NEXT: Value: 0x1002
# DSO-NEXT: Size: 0
# DSO-NEXT: Binding: Global
@@ -39,7 +35,7 @@
# DSO-NEXT: Section: .text
# DSO-NEXT: }
# DSO-NEXT: Symbol {
-# DSO-NEXT: Name: b@LIBSAMPLE_1.0
+# DSO-NEXT: Name: b@V1
# DSO-NEXT: Value: 0x1001
# DSO-NEXT: Size: 0
# DSO-NEXT: Binding: Global
@@ -48,7 +44,7 @@
# DSO-NEXT: Section: .text
# DSO-NEXT: }
# DSO-NEXT: Symbol {
-# DSO-NEXT: Name: c@@LIBSAMPLE_2.0
+# DSO-NEXT: Name: c@@V2
# DSO-NEXT: Value: 0x1003
# DSO-NEXT: Size: 0
# DSO-NEXT: Binding: Global
@@ -69,19 +65,19 @@
# DSO-NEXT: }
# DSO-NEXT: Symbol {
# DSO-NEXT: Version: 2
-# DSO-NEXT: Name: a@@LIBSAMPLE_1.0
+# DSO-NEXT: Name: a@@V1
# DSO-NEXT: }
# DSO-NEXT: Symbol {
# DSO-NEXT: Version: 3
-# DSO-NEXT: Name: b@@LIBSAMPLE_2.0
+# DSO-NEXT: Name: b@@V2
# DSO-NEXT: }
# DSO-NEXT: Symbol {
# DSO-NEXT: Version: 2
-# DSO-NEXT: Name: b@LIBSAMPLE_1.0
+# DSO-NEXT: Name: b@V1
# DSO-NEXT: }
# DSO-NEXT: Symbol {
# DSO-NEXT: Version: 3
-# DSO-NEXT: Name: c@@LIBSAMPLE_2.0
+# DSO-NEXT: Name: c@@V2
# DSO-NEXT: }
# DSO-NEXT: ]
# DSO-NEXT: }
@@ -97,15 +93,15 @@
# DSO-NEXT: Version: 1
# DSO-NEXT: Flags: 0x0
# DSO-NEXT: Index: 2
-# DSO-NEXT: Hash: 98457184
-# DSO-NEXT: Name: LIBSAMPLE_1.0
+# DSO-NEXT: Hash: 1425
+# DSO-NEXT: Name: V1
# DSO-NEXT: }
# DSO-NEXT: Definition {
# DSO-NEXT: Version: 1
# DSO-NEXT: Flags: 0x0
# DSO-NEXT: Index: 3
-# DSO-NEXT: Hash: 98456416
-# DSO-NEXT: Name: LIBSAMPLE_2.0
+# DSO-NEXT: Hash: 1426
+# DSO-NEXT: Name: V2
# DSO-NEXT: }
# DSO-NEXT: }
@@ -125,8 +121,8 @@
# EXE-NEXT: Section: Undefined
# EXE-NEXT: }
# EXE-NEXT: Symbol {
-# EXE-NEXT: Name: a@LIBSAMPLE_1.0
-# EXE-NEXT: Value: 0x11020
+# EXE-NEXT: Name: a@V1
+# EXE-NEXT: Value: 0x201020
# EXE-NEXT: Size: 0
# EXE-NEXT: Binding: Global
# EXE-NEXT: Type: Function
@@ -134,8 +130,8 @@
# EXE-NEXT: Section: Undefined
# EXE-NEXT: }
# EXE-NEXT: Symbol {
-# EXE-NEXT: Name: b@LIBSAMPLE_2.0
-# EXE-NEXT: Value: 0x11030
+# EXE-NEXT: Name: b@V2
+# EXE-NEXT: Value: 0x201030
# EXE-NEXT: Size: 0
# EXE-NEXT: Binding: Global
# EXE-NEXT: Type: Function
@@ -143,8 +139,8 @@
# EXE-NEXT: Section: Undefined
# EXE-NEXT: }
# EXE-NEXT: Symbol {
-# EXE-NEXT: Name: c@LIBSAMPLE_2.0
-# EXE-NEXT: Value: 0x11040
+# EXE-NEXT: Name: c@V2
+# EXE-NEXT: Value: 0x201040
# EXE-NEXT: Size: 0
# EXE-NEXT: Binding: Global
# EXE-NEXT: Type: Function
@@ -154,7 +150,7 @@
# EXE-NEXT: ]
# EXE-NEXT: Version symbols {
# EXE-NEXT: Section Name: .gnu.version
-# EXE-NEXT: Address: 0x10228
+# EXE-NEXT: Address: 0x200228
# EXE-NEXT: Offset: 0x228
# EXE-NEXT: Link: 1
# EXE-NEXT: Symbols [
@@ -164,15 +160,15 @@
# EXE-NEXT: }
# EXE-NEXT: Symbol {
# EXE-NEXT: Version: 2
-# EXE-NEXT: Name: a@LIBSAMPLE_1.0
+# EXE-NEXT: Name: a@V1
# EXE-NEXT: }
# EXE-NEXT: Symbol {
# EXE-NEXT: Version: 3
-# EXE-NEXT: Name: b@LIBSAMPLE_2.0
+# EXE-NEXT: Name: b@V2
# EXE-NEXT: }
# EXE-NEXT: Symbol {
# EXE-NEXT: Version: 3
-# EXE-NEXT: Name: c@LIBSAMPLE_2.0
+# EXE-NEXT: Name: c@V2
# EXE-NEXT: }
# EXE-NEXT: ]
# EXE-NEXT: }
@@ -184,16 +180,16 @@
# EXE-NEXT: Count: 2
# EXE-NEXT: FileName: shared
# EXE-NEXT: Entry {
-# EXE-NEXT: Hash: 98457184
+# EXE-NEXT: Hash: 1425
# EXE-NEXT: Flags: 0x0
# EXE-NEXT: Index: 2
-# EXE-NEXT: Name: LIBSAMPLE_1.0
+# EXE-NEXT: Name: V1
# EXE-NEXT: }
# EXE-NEXT: Entry {
-# EXE-NEXT: Hash: 98456416
+# EXE-NEXT: Hash: 1426
# EXE-NEXT: Flags: 0x0
# EXE-NEXT: Index: 3
-# EXE-NEXT: Name: LIBSAMPLE_2.0
+# EXE-NEXT: Name: V2
# EXE-NEXT: }
# EXE-NEXT: }
# EXE-NEXT: }
diff --git a/test/ELF/verdef-dependency.s b/test/ELF/verdef-dependency.s
index 92627ddc5c0f..3f126856b10f 100644
--- a/test/ELF/verdef-dependency.s
+++ b/test/ELF/verdef-dependency.s
@@ -1,14 +1,8 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
-# RUN: echo "LIBSAMPLE_1.0{ \
-# RUN: global: a; \
-# RUN: local: *; }; \
-# RUN: LIBSAMPLE_2.0{ \
-# RUN: global: b; \
-# RUN: local: *; }LIBSAMPLE_1.0; \
-# RUN: LIBSAMPLE_3.0{ \
-# RUN: global: c; \
-# RUN: }LIBSAMPLE_2.0;" > %t.script
+# RUN: echo "LIBSAMPLE_1.0 { global: a; local: *; };" > %t.script
+# RUN: echo "LIBSAMPLE_2.0 { global: b; local: *; } LIBSAMPLE_1.0;" >> %t.script
+# RUN: echo "LIBSAMPLE_3.0 { global: c; } LIBSAMPLE_2.0;" >> %t.script
# RUN: ld.lld --version-script %t.script -shared -soname shared %t.o -o %t.so
# RUN: llvm-readobj -V -dyn-symbols %t.so | FileCheck --check-prefix=DSO %s
diff --git a/test/ELF/verdef.s b/test/ELF/verdef.s
index 9463de0c0a0c..7fd60a95d1df 100644
--- a/test/ELF/verdef.s
+++ b/test/ELF/verdef.s
@@ -1,14 +1,8 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
-# RUN: echo "LIBSAMPLE_1.0{ \
-# RUN: global: a; \
-# RUN: local: *; }; \
-# RUN: LIBSAMPLE_2.0{ \
-# RUN: global: b; \
-# RUN: local: *; }; \
-# RUN: LIBSAMPLE_3.0{ \
-# RUN: global: c; \
-# RUN: local: *; };" > %t.script
+# RUN: echo "LIBSAMPLE_1.0 { global: a; local: *; };" > %t.script
+# RUN: echo "LIBSAMPLE_2.0 { global: b; local: *; };" >> %t.script
+# RUN: echo "LIBSAMPLE_3.0 { global: c; local: *; };" >> %t.script
# RUN: ld.lld --version-script %t.script -shared -soname shared %t.o -o %t.so
# RUN: llvm-readobj -V -dyn-symbols %t.so | FileCheck --check-prefix=DSO %s
@@ -76,7 +70,7 @@
# MAIN: Version symbols {
# MAIN-NEXT: Section Name: .gnu.version
-# MAIN-NEXT: Address: 0x10228
+# MAIN-NEXT: Address: 0x200228
# MAIN-NEXT: Offset: 0x228
# MAIN-NEXT: Link: 1
# MAIN-NEXT: Symbols [
@@ -101,6 +95,14 @@
# MAIN-NEXT: SHT_GNU_verdef {
# MAIN-NEXT: }
+# RUN: echo "VERSION {" > %t.script
+# RUN: echo "LIBSAMPLE_1.0 { global: a; local: *; };" >> %t.script
+# RUN: echo "LIBSAMPLE_2.0 { global: b; local: *; };" >> %t.script
+# RUN: echo "LIBSAMPLE_3.0 { global: c; local: *; };" >> %t.script
+# RUN: echo "}" >> %t.script
+# RUN: ld.lld --script %t.script -shared -soname shared %t.o -o %t2.so
+# RUN: llvm-readobj -V -dyn-symbols %t2.so | FileCheck --check-prefix=DSO %s
+
.globl a
.type a,@function
a:
diff --git a/test/ELF/verneed-local.s b/test/ELF/verneed-local.s
index a50f670ed7bd..94336fddad48 100644
--- a/test/ELF/verneed-local.s
+++ b/test/ELF/verneed-local.s
@@ -2,7 +2,7 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
# RUN: not ld.lld %t.o %S/Inputs/verneed1.so -o %t 2>&1 | FileCheck %s
-# CHECK: undefined symbol: f3 in
+# CHECK: error: {{.*}}:(.text+0x1): undefined symbol 'f3'
.globl _start
_start:
call f3
diff --git a/test/ELF/verneed.s b/test/ELF/verneed.s
index e9d7c53ba334..d0d0067250aa 100644
--- a/test/ELF/verneed.s
+++ b/test/ELF/verneed.s
@@ -5,12 +5,12 @@
# CHECK: Section {
# CHECK: Index: 1
-# CHECK-NEXT: Name: .dynsym (1)
+# CHECK-NEXT: Name: .dynsym
# CHECK-NEXT: Type: SHT_DYNSYM (0xB)
# CHECK-NEXT: Flags [ (0x2)
# CHECK-NEXT: SHF_ALLOC (0x2)
# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x101C8
+# CHECK-NEXT: Address: 0x2001C8
# CHECK-NEXT: Offset: 0x1C8
# CHECK-NEXT: Size: 96
# CHECK-NEXT: Link: 5
@@ -19,12 +19,12 @@
# CHECK-NEXT: EntrySize: 24
# CHECK: Section {
# CHECK-NEXT: Index: 2
-# CHECK-NEXT: Name: .gnu.version (9)
+# CHECK-NEXT: Name: .gnu.version
# CHECK-NEXT: Type: SHT_GNU_versym (0x6FFFFFFF)
# CHECK-NEXT: Flags [ (0x2)
# CHECK-NEXT: SHF_ALLOC (0x2)
# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x10228
+# CHECK-NEXT: Address: 0x200228
# CHECK-NEXT: Offset: 0x228
# CHECK-NEXT: Size: 8
# CHECK-NEXT: Link: 1
@@ -33,12 +33,12 @@
# CHECK-NEXT: EntrySize: 2
# CHECK: Section {
# CHECK-NEXT: Index: 3
-# CHECK-NEXT: Name: .gnu.version_r (22)
+# CHECK-NEXT: Name: .gnu.version_r
# CHECK-NEXT: Type: SHT_GNU_verneed (0x6FFFFFFE)
# CHECK-NEXT: Flags [ (0x2)
# CHECK-NEXT: SHF_ALLOC (0x2)
# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x10230
+# CHECK-NEXT: Address: 0x200230
# CHECK-NEXT: Offset: 0x230
# CHECK-NEXT: Size: 80
# CHECK-NEXT: Link: 5
@@ -52,7 +52,7 @@
# CHECK-NEXT: Flags [ (0x2)
# CHECK-NEXT: SHF_ALLOC (0x2)
# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x102A8
+# CHECK-NEXT: Address: 0x2002A8
# CHECK-NEXT: Offset: 0x2A8
# CHECK-NEXT: Size: 47
# CHECK-NEXT: Link: 0
@@ -60,15 +60,15 @@
# CHECK-NEXT: AddressAlignment: 1
# CHECK-NEXT: EntrySize: 0
# CHECK-NEXT: SectionData (
-# CHECK-NEXT: 0000: 00663100 7665726E 65656431 2E736F2E |.f1.verneed1.so.|
-# CHECK-NEXT: 0010: 30007633 00663200 76320067 31007665 |0.v3.f2.v2.g1.ve|
-# CHECK-NEXT: 0020: 726E6565 64322E73 6F2E3000 763100 |rneed2.so.0.v1.|
+# CHECK-NEXT: 0000: 00766572 6E656564 312E736F 2E300076 |.verneed1.so.0.v|
+# CHECK-NEXT: 0010: 65726E65 6564322E 736F2E30 00663100 |erneed2.so.0.f1.|
+# CHECK-NEXT: 0020: 76330066 32007632 00673100 763100 |v3.f2.v2.g1.v1.|
# CHECK-NEXT: )
# CHECK-NEXT: }
# CHECK: DynamicSymbols [
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: @ (0)
+# CHECK-NEXT: Name: @
# CHECK-NEXT: Value: 0x0
# CHECK-NEXT: Size: 0
# CHECK-NEXT: Binding: Local (0x0)
@@ -77,7 +77,7 @@
# CHECK-NEXT: Section: Undefined (0x0)
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: f1@v3 (1)
+# CHECK-NEXT: Name: f1@v3
# CHECK-NEXT: Value: 0x0
# CHECK-NEXT: Size: 0
# CHECK-NEXT: Binding: Global (0x1)
@@ -86,7 +86,7 @@
# CHECK-NEXT: Section: Undefined (0x0)
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: f2@v2 (21)
+# CHECK-NEXT: Name: f2@v2
# CHECK-NEXT: Value: 0x0
# CHECK-NEXT: Size: 0
# CHECK-NEXT: Binding: Global (0x1)
@@ -95,7 +95,7 @@
# CHECK-NEXT: Section: Undefined (0x0)
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: g1@v1 (27)
+# CHECK-NEXT: Name: g1@v1
# CHECK-NEXT: Value: 0x0
# CHECK-NEXT: Size: 0
# CHECK-NEXT: Binding: Global (0x1)
@@ -105,13 +105,13 @@
# CHECK-NEXT: }
# CHECK-NEXT: ]
-# CHECK: 0x000000006FFFFFF0 VERSYM 0x10228
-# CHECK-NEXT: 0x000000006FFFFFFE VERNEED 0x10230
+# CHECK: 0x000000006FFFFFF0 VERSYM 0x200228
+# CHECK-NEXT: 0x000000006FFFFFFE VERNEED 0x200230
# CHECK-NEXT: 0x000000006FFFFFFF VERNEEDNUM 2
# CHECK: Version symbols {
# CHECK-NEXT: Section Name: .gnu.version
-# CHECK-NEXT: Address: 0x10228
+# CHECK-NEXT: Address: 0x200228
# CHECK-NEXT: Offset: 0x228
# CHECK-NEXT: Link: 1
# CHECK-NEXT: Symbols [
diff --git a/test/ELF/version-script-complex-wildcards.s b/test/ELF/version-script-complex-wildcards.s
new file mode 100644
index 000000000000..61e1069e2eac
--- /dev/null
+++ b/test/ELF/version-script-complex-wildcards.s
@@ -0,0 +1,62 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "FOO { global: extern \"C++\" { ab[c]*; }; };" > %t.script
+# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
+# RUN: llvm-readobj -V %t.so | FileCheck %s --check-prefix=ABC
+# ABC: Name: _Z3abbi@
+# ABC: Name: _Z3abci@@FOO
+
+# RUN: echo "FOO { global: extern \"C++\" { ab[b]*; }; };" > %t1.script
+# RUN: ld.lld --version-script %t1.script -shared %t.o -o %t1.so
+# RUN: llvm-readobj -V %t1.so | FileCheck %s --check-prefix=ABB
+# ABB: Name: _Z3abbi@@FOO
+# ABB: Name: _Z3abci@
+
+# RUN: echo "FOO { global: extern \"C++\" { ab[a-b]*; }; };" > %t2.script
+# RUN: ld.lld --version-script %t2.script -shared %t.o -o %t2.so
+# RUN: llvm-readobj -V %t2.so | FileCheck %s --check-prefix=ABB
+
+# RUN: echo "FOO { global: extern \"C++\" { ab[a-c]*; }; };" > %t3.script
+# RUN: ld.lld --version-script %t3.script -shared %t.o -o %t3.so
+# RUN: llvm-readobj -V %t3.so | FileCheck %s --check-prefix=ABBABC
+# ABBABC: Name: _Z3abbi@@FOO
+# ABBABC: Name: _Z3abci@@FOO
+
+# RUN: echo "FOO { global: extern \"C++\" { ab[a-bc-d]*; }; };" > %t4.script
+# RUN: ld.lld --version-script %t4.script -shared %t.o -o %t4.so
+# RUN: llvm-readobj -V %t4.so | FileCheck %s --check-prefix=ABBABC
+
+# RUN: echo "FOO { global: extern \"C++\" { ab[a-bd-e]*; }; };" > %t5.script
+# RUN: ld.lld --version-script %t5.script -shared %t.o -o %t5.so
+# RUN: llvm-readobj -V %t5.so | FileCheck %s --check-prefix=ABB
+
+# RUN: echo "FOO { global: extern \"C++\" { ab[^a-c]*; }; };" > %t6.script
+# RUN: ld.lld --version-script %t6.script -shared %t.o -o %t6.so
+# RUN: llvm-readobj -V %t6.so | FileCheck %s --check-prefix=NO
+# NO: Name: _Z3abbi@
+# NO: Name: _Z3abci@
+
+# RUN: echo "FOO { global: extern \"C++\" { ab[^c-z]*; }; };" > %t7.script
+# RUN: ld.lld --version-script %t7.script -shared %t.o -o %t7.so
+# RUN: llvm-readobj -V %t7.so | FileCheck %s --check-prefix=ABB
+
+# RUN: echo "FOO { global: extern \"C++\" { a[x-za-b][a-c]*; }; };" > %t8.script
+# RUN: ld.lld --version-script %t8.script -shared %t.o -o %t8.so
+# RUN: llvm-readobj -V %t8.so | FileCheck %s --check-prefix=ABBABC
+
+# RUN: echo "FOO { global: extern \"C++\" { a[; }; };" > %t9.script
+# RUN: not ld.lld --version-script %t9.script -shared %t.o -o %t9.so 2>&1 \
+# RUN: | FileCheck %s --check-prefix=ERROR
+# ERROR: invalid glob pattern: a[
+
+.text
+.globl _Z3abci
+.type _Z3abci,@function
+_Z3abci:
+retq
+
+.globl _Z3abbi
+.type _Z3abbi,@function
+_Z3abbi:
+retq
diff --git a/test/ELF/version-script-err.s b/test/ELF/version-script-err.s
index 15b69e98505b..ea3f664ea6ce 100644
--- a/test/ELF/version-script-err.s
+++ b/test/ELF/version-script-err.s
@@ -7,4 +7,5 @@
// RUN: echo "\"" > %terr1.script
// RUN: not ld.lld --version-script %terr1.script -shared %t.o -o %t.so 2>&1 | \
// RUN: FileCheck -check-prefix=ERR1 %s
-// ERR1: unclosed quote
+// ERR1: {{.*}}:1: unclosed quote
+// ERR1-NEXT: {{.*}}: unexpected EOF
diff --git a/test/ELF/version-script-extern-exact.s b/test/ELF/version-script-extern-exact.s
new file mode 100644
index 000000000000..1ac2f6395ab6
--- /dev/null
+++ b/test/ELF/version-script-extern-exact.s
@@ -0,0 +1,22 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "FOO { global: extern \"C++\" { \"aaa*\"; }; };" > %t.script
+# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
+# RUN: llvm-readobj -V -dyn-symbols %t.so | FileCheck %s
+
+# CHECK: Symbol {
+# CHECK: Name: _Z3aaaPf@
+# CHECK: Symbol {
+# CHECK: Name: _Z3aaaPi@
+
+.text
+.globl _Z3aaaPi
+.type _Z3aaaPi,@function
+_Z3aaaPi:
+retq
+
+.globl _Z3aaaPf
+.type _Z3aaaPf,@function
+_Z3aaaPf:
+retq
diff --git a/test/ELF/version-script-extern-wildcards-anon.s b/test/ELF/version-script-extern-wildcards-anon.s
new file mode 100644
index 000000000000..790d985e2b0c
--- /dev/null
+++ b/test/ELF/version-script-extern-wildcards-anon.s
@@ -0,0 +1,62 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo '{ \
+# RUN: global: \
+# RUN: _Z3bari; \
+# RUN: extern "C++" { \
+# RUN: "foo(int)"; \
+# RUN: z*; \
+# RUN: }; \
+# RUN: local: *; \
+# RUN: }; ' > %t.script
+# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
+# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck %s
+
+# CHECK: DynamicSymbols [
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name:
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Local
+# CHECK-NEXT: Type:
+# CHECK-NEXT: Other:
+# CHECK-NEXT: Section:
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: _Z3bari
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type:
+# CHECK-NEXT: Other:
+# CHECK-NEXT: Section:
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: _Z3fooi
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type:
+# CHECK-NEXT: Other:
+# CHECK-NEXT: Section:
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: _Z3zedi
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type:
+# CHECK-NEXT: Other:
+# CHECK-NEXT: Section:
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+.global _Z3fooi
+_Z3fooi:
+.global _Z3bari
+_Z3bari:
+.global _Z3zedi
+_Z3zedi:
+.global _Z3bazi
+_Z3bazi:
diff --git a/test/ELF/version-script-extern-wildcards.s b/test/ELF/version-script-extern-wildcards.s
new file mode 100644
index 000000000000..472fc1f7833e
--- /dev/null
+++ b/test/ELF/version-script-extern-wildcards.s
@@ -0,0 +1,29 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "FOO { global: extern \"C++\" { foo*; }; };" > %t.script
+# RUN: echo "BAR { global: extern \"C++\" { zed*; bar; }; };" >> %t.script
+# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
+# RUN: llvm-readobj -V -dyn-symbols %t.so | FileCheck %s
+
+# CHECK: Version symbols {
+# CHECK: Symbols [
+# CHECK: Name: _Z3bari@
+# CHECK: Name: _Z3fooi@@FOO
+# CHECK: Name: _Z3zedi@@BAR
+
+.text
+.globl _Z3fooi
+.type _Z3fooi,@function
+_Z3fooi:
+retq
+
+.globl _Z3bari
+.type _Z3bari,@function
+_Z3bari:
+retq
+
+.globl _Z3zedi
+.type _Z3zedi,@function
+_Z3zedi:
+retq
diff --git a/test/ELF/version-script-extern.s b/test/ELF/version-script-extern.s
index 439653487bc0..2b89839c369a 100644
--- a/test/ELF/version-script-extern.s
+++ b/test/ELF/version-script-extern.s
@@ -1,19 +1,12 @@
-# REQUIRES: shell
+# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-# RUN: echo "LIBSAMPLE_1.0 { \
-# RUN: global: \
-# RUN: extern "C++" { \
-# RUN: \"foo(int)\"; \
-# RUN: \"zed(int)\"; \
-# RUN: }; \
-# RUN: }; \
-# RUN: LIBSAMPLE_2.0 { \
-# RUN: global: \
-# RUN: extern "C++" { \
-# RUN: \"bar(int)\"; \
-# RUN: }; \
-# RUN: }; " > %t.script
+# RUN: echo "LIBSAMPLE_1.0 { global:" > %t.script
+# RUN: echo ' extern "C++" { "foo(int)"; "zed(int)"; "abc::abc()"; };' >> %t.script
+# RUN: echo "};" >> %t.script
+# RUN: echo "LIBSAMPLE_2.0 { global:" >> %t.script
+# RUN: echo ' extern "C" { _Z3bari; };' >> %t.script
+# RUN: echo "};" >> %t.script
# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
# RUN: llvm-readobj -V -dyn-symbols %t.so | FileCheck --check-prefix=DSO %s
@@ -54,11 +47,29 @@
# DSO-NEXT: Other: 0
# DSO-NEXT: Section: .text (0x6)
# DSO-NEXT: }
+# DSO-NEXT: Symbol {
+# DSO-NEXT: Name: _ZN3abcC1Ev@@LIBSAMPLE_1.0
+# DSO-NEXT: Value: 0x1003
+# DSO-NEXT: Size: 0
+# DSO-NEXT: Binding: Global (0x1)
+# DSO-NEXT: Type: Function (0x2)
+# DSO-NEXT: Other: 0
+# DSO-NEXT: Section: .text (0x6)
+# DSO-NEXT: }
+# DSO-NEXT: Symbol {
+# DSO-NEXT: Name: _ZN3abcC2Ev@@LIBSAMPLE_1.0
+# DSO-NEXT: Value: 0x1004
+# DSO-NEXT: Size: 0
+# DSO-NEXT: Binding: Global (0x1)
+# DSO-NEXT: Type: Function (0x2)
+# DSO-NEXT: Other: 0
+# DSO-NEXT: Section: .text (0x6)
+# DSO-NEXT: }
# DSO-NEXT: ]
# DSO-NEXT: Version symbols {
# DSO-NEXT: Section Name: .gnu.version
-# DSO-NEXT: Address: 0x228
-# DSO-NEXT: Offset: 0x228
+# DSO-NEXT: Address: 0x258
+# DSO-NEXT: Offset: 0x258
# DSO-NEXT: Link: 1
# DSO-NEXT: Symbols [
# DSO-NEXT: Symbol {
@@ -77,6 +88,14 @@
# DSO-NEXT: Version: 2
# DSO-NEXT: Name: _Z3zedi@@LIBSAMPLE_1.0
# DSO-NEXT: }
+# DSO-NEXT: Symbol {
+# DSO-NEXT: Version: 2
+# DSO-NEXT: Name: _ZN3abcC1Ev@@LIBSAMPLE_1.0
+# DSO-NEXT: }
+# DSO-NEXT: Symbol {
+# DSO-NEXT: Version: 2
+# DSO-NEXT: Name: _ZN3abcC2Ev@@LIBSAMPLE_1.0
+# DSO-NEXT: }
# DSO-NEXT: ]
# DSO-NEXT: }
@@ -95,3 +114,13 @@ retq
.type _Z3zedi,@function
_Z3zedi:
retq
+
+.globl _ZN3abcC1Ev
+.type _ZN3abcC1Ev,@function
+_ZN3abcC1Ev:
+retq
+
+.globl _ZN3abcC2Ev
+.type _ZN3abcC2Ev,@function
+_ZN3abcC2Ev:
+retq
diff --git a/test/ELF/version-script-glob.s b/test/ELF/version-script-glob.s
new file mode 100644
index 000000000000..330c068ba91d
--- /dev/null
+++ b/test/ELF/version-script-glob.s
@@ -0,0 +1,45 @@
+# REQUIRES: x86
+
+# RUN: echo "{ global: foo*; bar*; local: *; };" > %t.script
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld -shared --version-script %t.script %t.o -o %t.so
+# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck %s
+
+ .globl foo1
+foo1:
+
+ .globl bar1
+bar1:
+
+ .globl zed1
+zed1:
+
+# CHECK: DynamicSymbols [
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name:
+# CHECK-NEXT: Value: 0x0
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Local
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Undefined
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: bar1
+# CHECK-NEXT: Value: 0x1000
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .text
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: foo1
+# CHECK-NEXT: Value: 0x1000
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .text
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
diff --git a/test/ELF/version-script-locals-extern.s b/test/ELF/version-script-locals-extern.s
new file mode 100644
index 000000000000..12e8771b8eff
--- /dev/null
+++ b/test/ELF/version-script-locals-extern.s
@@ -0,0 +1,45 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "FOO { local: extern \"C++\" { \"abb(int)\"; }; };" > %t.script
+# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
+# RUN: llvm-readobj -V %t.so | FileCheck %s --check-prefix=ABB
+# ABB: Symbols [
+# ABB-NEXT: Symbol {
+# ABB-NEXT: Version: 0
+# ABB-NEXT: Name: @
+# ABB-NEXT: }
+# ABB-NEXT: Symbol {
+# ABB-NEXT: Version: 1
+# ABB-NEXT: Name: _Z3abci@
+# ABB-NEXT: }
+# ABB-NEXT: ]
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "FOO { local: extern \"C++\" { abb*; }; };" > %t.script
+# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
+# RUN: llvm-readobj -V %t.so | FileCheck %s --check-prefix=ABB
+
+# RUN: echo "FOO { local: extern \"C++\" { abc*; }; };" > %t.script
+# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
+# RUN: llvm-readobj -V %t.so | FileCheck %s --check-prefix=ABC
+# ABC: Symbols [
+# ABC-NEXT: Symbol {
+# ABC-NEXT: Version: 0
+# ABC-NEXT: Name: @
+# ABC-NEXT: }
+# ABC-NEXT: Symbol {
+# ABC-NEXT: Version: 1
+# ABC-NEXT: Name: _Z3abbi@
+# ABC-NEXT: }
+# ABC-NEXT: ]
+
+.globl _Z3abbi
+.type _Z3abbi,@function
+_Z3abbi:
+retq
+
+.globl _Z3abci
+.type _Z3abci,@function
+_Z3abci:
+retq
diff --git a/test/ELF/version-script-locals.s b/test/ELF/version-script-locals.s
new file mode 100644
index 000000000000..80110c396ecc
--- /dev/null
+++ b/test/ELF/version-script-locals.s
@@ -0,0 +1,45 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+
+# RUN: echo "VERSION_1.0 { local: foo1; };" > %t.script
+# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
+# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck --check-prefix=EXACT %s
+# EXACT: DynamicSymbols [
+# EXACT: _start
+# EXACT-NOT: foo1
+# EXACT: foo2
+# EXACT: foo3
+
+# RUN: echo "VERSION_1.0 { local: foo*; };" > %t.script
+# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
+# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck --check-prefix=WC %s
+# WC: DynamicSymbols [
+# WC: _start
+# WC-NOT: foo1
+# WC-NOT: foo2
+# WC-NOT: foo3
+
+# RUN: echo "VERSION_1.0 { global: *; local: foo*; };" > %t.script
+# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
+# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck --check-prefix=MIX %s
+# MIX: DynamicSymbols [
+# MIX: _start@@VERSION_1.0
+# MIX-NOT: foo1
+# MIX-NOT: foo2
+# MIX-NOT: foo3
+
+.globl foo1
+foo1:
+ ret
+
+.globl foo2
+foo2:
+ ret
+
+.globl foo3
+foo3:
+ ret
+
+.globl _start
+_start:
+ ret
diff --git a/test/ELF/version-script-missing.s b/test/ELF/version-script-missing.s
new file mode 100644
index 000000000000..ad4786e70cc5
--- /dev/null
+++ b/test/ELF/version-script-missing.s
@@ -0,0 +1,7 @@
+# REQUIRES: x86
+
+# We used to crash if a symbol in a version script was not in the symbol table.
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "{ foobar; };" > %t.script
+# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
diff --git a/test/ELF/version-script-no-warn.s b/test/ELF/version-script-no-warn.s
new file mode 100644
index 000000000000..6a897159d2d0
--- /dev/null
+++ b/test/ELF/version-script-no-warn.s
@@ -0,0 +1,12 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/shared.s -o %t2.o
+# RUN: ld.lld -shared %t2.o -soname shared -o %t2.so
+
+# RUN: echo "foo { global: bar; local: *; };" > %t.script
+# RUN: ld.lld --fatal-warnings --shared --version-script %t.script %t.o %t2.so
+
+.global bar
+bar:
+ nop
diff --git a/test/ELF/version-script-noundef.s b/test/ELF/version-script-noundef.s
index 4a251d6172da..247752cac844 100644
--- a/test/ELF/version-script-noundef.s
+++ b/test/ELF/version-script-noundef.s
@@ -1,19 +1,20 @@
# REQUIRES: x86
-# RUN: echo "VERSION_1.0{ \
-# RUN: global: bar; \
-# RUN: };" > %t.script
+# RUN: echo "VERSION_1.0 { global: bar; };" > %t.script
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
# RUN: not ld.lld --version-script %t.script -shared --no-undefined-version \
# RUN: %t.o -o %t.so 2>&1 | FileCheck -check-prefix=ERR1 %s
-# ERR1: version script assignment of VERSION_1.0 to symbol bar failed: symbol not defined
+# ERR1: version script assignment of 'VERSION_1.0' to symbol 'bar' failed: symbol not defined
-# RUN: echo "VERSION_1.0{ \
-# RUN: global: und; \
-# RUN: };" > %t2.script
+# RUN: echo "VERSION_1.0 { global: und; };" > %t2.script
# RUN: not ld.lld --version-script %t2.script -shared --no-undefined-version \
# RUN: %t.o -o %t.so 2>&1 | FileCheck -check-prefix=ERR2 %s
-# ERR2: version script assignment of VERSION_1.0 to symbol und failed: symbol not defined
+# ERR2: version script assignment of 'VERSION_1.0' to symbol 'und' failed: symbol not defined
+
+# RUN: echo "VERSION_1.0 { local: und; };" > %t3.script
+# RUN: not ld.lld --version-script %t3.script -shared --no-undefined-version \
+# RUN: %t.o -o %t.so 2>&1 | FileCheck -check-prefix=ERR3 %s
+# ERR3: version script assignment of 'local' to symbol 'und' failed: symbol not defined
.text
.globl foo
diff --git a/test/ELF/version-script.s b/test/ELF/version-script.s
index ba9c95a96fc3..7453981ab9cc 100644
--- a/test/ELF/version-script.s
+++ b/test/ELF/version-script.s
@@ -14,55 +14,36 @@
# RUN: ld.lld --version-script %t3.script -shared %t.o %t2.so -o %t3.so
# RUN: llvm-readobj -dyn-symbols %t3.so | FileCheck --check-prefix=DSO2 %s
-# --version-script filters --dynamic-list.
-# RUN: echo "{ foo1; foo2; };" > %t.list
-# RUN: ld.lld --version-script %t.script --dynamic-list %t.list %t.o %t2.so -o %t
-# RUN: llvm-readobj -dyn-symbols %t | FileCheck --check-prefix=EXE %s
-
-# RUN: echo "VERSION_1.0{ \
-# RUN: global: foo1; \
-# RUN: local: *; }; \
-# RUN: VERSION_2.0{ \
-# RUN: global: foo3; \
-# RUN: local: *; }; " > %t4.script
+# RUN: echo "VERSION_1.0 { global: foo1; local: *; };" > %t4.script
+# RUN: echo "VERSION_2.0 { global: foo3; local: *; };" >> %t4.script
# RUN: ld.lld --version-script %t4.script -shared %t.o %t2.so -o %t4.so
# RUN: llvm-readobj -dyn-symbols %t4.so | FileCheck --check-prefix=VERDSO %s
-# RUN: echo "VERSION_1.0{ \
-# RUN: global: foo1; \
-# RUN: local: *; }; \
-# RUN: { \
-# RUN: global: foo3; \
-# RUN: local: *; }; " > %t5.script
+# RUN: echo "VERSION_1.0 { global: foo1; local: *; };" > %t5.script
+# RUN: echo "{ global: foo3; local: *; };" >> %t5.script
# RUN: not ld.lld --version-script %t5.script -shared %t.o %t2.so -o %t5.so 2>&1 | \
-# RUN: FileCheck -check-prefix=ERR %s
-# ERR: anonymous version definition is used in combination with other version definitions
+# RUN: FileCheck -check-prefix=ERR1 %s
+# ERR1: anonymous version definition is used in combination with other version definitions
-# RUN: echo "{ \
-# RUN: global: foo1; \
-# RUN: local: *; }; \
-# RUN: VERSION_2.0 { \
-# RUN: global: foo3; \
-# RUN: local: *; }; " > %t5.script
+# RUN: echo "{ global: foo1; local: *; };" > %t5.script
+# RUN: echo "VERSION_2.0 { global: foo3; local: *; };" >> %t5.script
# RUN: not ld.lld --version-script %t5.script -shared %t.o %t2.so -o %t5.so 2>&1 | \
-# RUN: FileCheck -check-prefix=ERR %s
+# RUN: FileCheck -check-prefix=ERR2 %s
+# ERR2: EOF expected, but got VERSION_2.0
-# RUN: echo "VERSION_1.0{ \
-# RUN: global: foo1; \
-# RUN: local: *; }; \
-# RUN: VERSION_2.0 { \
-# RUN: global: foo1; \
-# RUN: local: *; }; " > %t6.script
+# RUN: echo "VERSION_1.0 { global: foo1; local: *; };" > %t6.script
+# RUN: echo "VERSION_2.0 { global: foo1; local: *; };" >> %t6.script
# RUN: ld.lld --version-script %t6.script -shared %t.o %t2.so -o %t6.so 2>&1 | \
# RUN: FileCheck -check-prefix=WARN2 %s
-# WARN2: duplicate symbol foo1 in version script
+# WARN2: duplicate symbol 'foo1' in version script
+# RUN: echo "{ foo1; foo2; };" > %t.list
# RUN: ld.lld --version-script %t.script --dynamic-list %t.list %t.o %t2.so -o %t2
# RUN: llvm-readobj %t2 > /dev/null
# DSO: DynamicSymbols [
# DSO-NEXT: Symbol {
-# DSO-NEXT: Name: @ (0)
+# DSO-NEXT: Name: @
# DSO-NEXT: Value: 0x0
# DSO-NEXT: Size: 0
# DSO-NEXT: Binding: Local (0x0)
@@ -71,7 +52,7 @@
# DSO-NEXT: Section: Undefined (0x0)
# DSO-NEXT: }
# DSO-NEXT: Symbol {
-# DSO-NEXT: Name: bar@ (1)
+# DSO-NEXT: Name: bar@
# DSO-NEXT: Value: 0x0
# DSO-NEXT: Size: 0
# DSO-NEXT: Binding: Global (0x1)
@@ -80,7 +61,7 @@
# DSO-NEXT: Section: Undefined (0x0)
# DSO-NEXT: }
# DSO-NEXT: Symbol {
-# DSO-NEXT: Name: foo1@ (5)
+# DSO-NEXT: Name: foo1@
# DSO-NEXT: Value: 0x1000
# DSO-NEXT: Size: 0
# DSO-NEXT: Binding: Global (0x1)
@@ -89,7 +70,7 @@
# DSO-NEXT: Section: .text
# DSO-NEXT: }
# DSO-NEXT: Symbol {
-# DSO-NEXT: Name: foo3@ (10)
+# DSO-NEXT: Name: foo3@
# DSO-NEXT: Value: 0x1007
# DSO-NEXT: Size: 0
# DSO-NEXT: Binding: Global (0x1)
@@ -101,7 +82,7 @@
# DSO2: DynamicSymbols [
# DSO2-NEXT: Symbol {
-# DSO2-NEXT: Name: @ (0)
+# DSO2-NEXT: Name: @
# DSO2-NEXT: Value: 0x0
# DSO2-NEXT: Size: 0
# DSO2-NEXT: Binding: Local (0x0)
@@ -110,7 +91,7 @@
# DSO2-NEXT: Section: Undefined (0x0)
# DSO2-NEXT: }
# DSO2-NEXT: Symbol {
-# DSO2-NEXT: Name: bar@ (1)
+# DSO2-NEXT: Name: bar@
# DSO2-NEXT: Value: 0x0
# DSO2-NEXT: Size: 0
# DSO2-NEXT: Binding: Global (0x1)
@@ -120,36 +101,6 @@
# DSO2-NEXT: }
# DSO2-NEXT: ]
-# EXE: DynamicSymbols [
-# EXE-NEXT: Symbol {
-# EXE-NEXT: Name: @ (0)
-# EXE-NEXT: Value: 0x0
-# EXE-NEXT: Size: 0
-# EXE-NEXT: Binding: Local (0x0)
-# EXE-NEXT: Type: None (0x0)
-# EXE-NEXT: Other: 0
-# EXE-NEXT: Section: Undefined (0x0)
-# EXE-NEXT: }
-# EXE-NEXT: Symbol {
-# EXE-NEXT: Name: bar@ (1)
-# EXE-NEXT: Value: 0x0
-# EXE-NEXT: Size: 0
-# EXE-NEXT: Binding: Global (0x1)
-# EXE-NEXT: Type: Function (0x2)
-# EXE-NEXT: Other: 0
-# EXE-NEXT: Section: Undefined (0x0)
-# EXE-NEXT: }
-# EXE-NEXT: Symbol {
-# EXE-NEXT: Name: foo1@ (5)
-# EXE-NEXT: Value: 0x11000
-# EXE-NEXT: Size: 0
-# EXE-NEXT: Binding: Global (0x1)
-# EXE-NEXT: Type: None (0x0)
-# EXE-NEXT: Other: 0
-# EXE-NEXT: Section: .text
-# EXE-NEXT: }
-# EXE-NEXT: ]
-
# VERDSO: DynamicSymbols [
# VERDSO-NEXT: Symbol {
# VERDSO-NEXT: Name: @
diff --git a/test/ELF/version-symbol-error.s b/test/ELF/version-symbol-error.s
new file mode 100644
index 000000000000..fb83b359485b
--- /dev/null
+++ b/test/ELF/version-symbol-error.s
@@ -0,0 +1,12 @@
+// REQUIRES: x86
+// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux
+// RUN: echo "V1 {};" > %t.script
+// RUN: not ld.lld -shared -version-script=%t.script %t.o -o %t.so 2>&1 \
+// RUN: | FileCheck %s
+
+// CHECK: .o: symbol foo@V2 has undefined version V2
+
+.globl foo@V2
+.text
+foo@V2:
+ ret
diff --git a/test/ELF/version-wildcard.test b/test/ELF/version-wildcard.test
index 80cb9cadf159..ac0b7edc6c51 100644
--- a/test/ELF/version-wildcard.test
+++ b/test/ELF/version-wildcard.test
@@ -1,9 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
-# RUN: echo "VERSION_1.0{ \
-# RUN: global: foo*; \
-# RUN: local: *; };" > %t.script
+# RUN: echo "VERSION_1.0 { global: foo*; local: *; };" > %t.script
# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck %s
@@ -47,12 +45,8 @@
# CHECK-NEXT: ]
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
-# RUN: echo "VERSION_1.0{ \
-# RUN: global: foo2; \
-# RUN: local: *; }; \
-# RUN: VERSION_2.0{ \
-# RUN: global: foo*; \
-# RUN: }; " > %t2.script
+# RUN: echo "VERSION_1.0 { global: foo2; local: *; };" > %t2.script
+# RUN: echo "VERSION_2.0 { global: foo*; };" >> %t2.script
# RUN: ld.lld --version-script %t2.script -shared %t.o -o %t2.so
# RUN: llvm-readobj -dyn-symbols %t2.so | FileCheck --check-prefix=MIX %s
diff --git a/test/ELF/visibility.s b/test/ELF/visibility.s
index 2043894d3d3d..7af29c957b52 100644
--- a/test/ELF/visibility.s
+++ b/test/ELF/visibility.s
@@ -82,7 +82,7 @@
// CHECK: DynamicSymbols [
// CHECK-NEXT: Symbol {
-// CHECK-NEXT: Name: @ (0)
+// CHECK-NEXT: Name: @
// CHECK-NEXT: Value: 0x0
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
diff --git a/test/ELF/weak-undef-shared.s b/test/ELF/weak-undef-shared.s
index 862a08632e26..668a05b658a1 100644
--- a/test/ELF/weak-undef-shared.s
+++ b/test/ELF/weak-undef-shared.s
@@ -6,7 +6,7 @@
// RUN: llvm-readobj -t %t.exe | FileCheck %s
// CHECK: Name: bar
-// CHECK-NEXT: Value: 0x11020
+// CHECK-NEXT: Value: 0x201020
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Weak
// CHECK-NEXT: Type: Function
diff --git a/test/ELF/weak-undef.s b/test/ELF/weak-undef.s
index 02555535927e..b6340339ebac 100644
--- a/test/ELF/weak-undef.s
+++ b/test/ELF/weak-undef.s
@@ -5,7 +5,7 @@
# CHECK: DynamicSymbols [
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: @ (0)
+# CHECK-NEXT: Name: @
# CHECK-NEXT: Value: 0x0
# CHECK-NEXT: Size: 0
# CHECK-NEXT: Binding: Local (0x0)
diff --git a/test/ELF/wildcards.s b/test/ELF/wildcards.s
deleted file mode 100644
index 2fe0f881bf46..000000000000
--- a/test/ELF/wildcards.s
+++ /dev/null
@@ -1,80 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-
-## Default case: abc and abx included in text.
-# RUN: echo "SECTIONS { \
-# RUN: .text : { *(.abc .abx) } }" > %t.script
-# RUN: ld.lld -o %t.out --script %t.script %t
-# RUN: llvm-objdump -section-headers %t.out | \
-# RUN: FileCheck -check-prefix=SEC-DEFAULT %s
-# SEC-DEFAULT: Sections:
-# SEC-DEFAULT-NEXT: Idx Name Size Address Type
-# SEC-DEFAULT-NEXT: 0 00000000 0000000000000000
-# SEC-DEFAULT-NEXT: 1 .text 00000008 0000000000000120 TEXT DATA
-# SEC-DEFAULT-NEXT: 2 .abcd 00000004 0000000000000128 TEXT DATA
-# SEC-DEFAULT-NEXT: 3 .ad 00000004 000000000000012c TEXT DATA
-# SEC-DEFAULT-NEXT: 4 .ag 00000004 0000000000000130 TEXT DATA
-# SEC-DEFAULT-NEXT: 5 .symtab 00000030 0000000000000000
-# SEC-DEFAULT-NEXT: 6 .shstrtab 0000002f 0000000000000000
-# SEC-DEFAULT-NEXT: 7 .strtab 00000008 0000000000000000
-
-## Now replace the symbol with '?' and check that results are the same.
-# RUN: echo "SECTIONS { \
-# RUN: .text : { *(.abc .ab?) } }" > %t.script
-# RUN: ld.lld -o %t.out --script %t.script %t
-# RUN: llvm-objdump -section-headers %t.out | \
-# RUN: FileCheck -check-prefix=SEC-DEFAULT %s
-
-## Now see how replacing '?' with '*' will consume whole abcd.
-# RUN: echo "SECTIONS { \
-# RUN: .text : { *(.abc .ab*) } }" > %t.script
-# RUN: ld.lld -o %t.out --script %t.script %t
-# RUN: llvm-objdump -section-headers %t.out | \
-# RUN: FileCheck -check-prefix=SEC-ALL %s
-# SEC-ALL: Sections:
-# SEC-ALL-NEXT: Idx Name Size Address Type
-# SEC-ALL-NEXT: 0 00000000 0000000000000000
-# SEC-ALL-NEXT: 1 .text 0000000c 0000000000000120 TEXT DATA
-# SEC-ALL-NEXT: 2 .ad 00000004 000000000000012c TEXT DATA
-# SEC-ALL-NEXT: 3 .ag 00000004 0000000000000130 TEXT DATA
-# SEC-ALL-NEXT: 4 .symtab 00000030 0000000000000000
-# SEC-ALL-NEXT: 5 .shstrtab 00000029 0000000000000000
-# SEC-ALL-NEXT: 6 .strtab 00000008 0000000000000000
-
-## All sections started with .a are merged.
-# RUN: echo "SECTIONS { \
-# RUN: .text : { *(.a*) } }" > %t.script
-# RUN: ld.lld -o %t.out --script %t.script %t
-# RUN: llvm-objdump -section-headers %t.out | \
-# RUN: FileCheck -check-prefix=SEC-NO %s
-# SEC-NO: Sections:
-# SEC-NO-NEXT: Idx Name Size Address Type
-# SEC-NO-NEXT: 0 00000000 0000000000000000
-# SEC-NO-NEXT: 1 .text 00000014 0000000000000120 TEXT DATA
-# SEC-NO-NEXT: 2 .symtab 00000030 0000000000000000
-# SEC-NO-NEXT: 3 .shstrtab 00000021 0000000000000000
-# SEC-NO-NEXT: 4 .strtab 00000008 0000000000000000
-
-.text
-.section .abc,"ax",@progbits
-.long 0
-
-.text
-.section .abx,"ax",@progbits
-.long 0
-
-.text
-.section .abcd,"ax",@progbits
-.long 0
-
-.text
-.section .ad,"ax",@progbits
-.long 0
-
-.text
-.section .ag,"ax",@progbits
-.long 0
-
-
-.globl _start
-_start:
diff --git a/test/ELF/writable-merge.s b/test/ELF/writable-merge.s
index 431cb6282d91..3006fa387fb5 100644
--- a/test/ELF/writable-merge.s
+++ b/test/ELF/writable-merge.s
@@ -4,3 +4,4 @@
// CHECK: writable SHF_MERGE section is not supported
.section .foo,"awM",@progbits,4
+.quad 0
diff --git a/test/ELF/x86-64-dyn-rel-error.s b/test/ELF/x86-64-dyn-rel-error.s
index c814fbeb1d9a..354fcc09e8a8 100644
--- a/test/ELF/x86-64-dyn-rel-error.s
+++ b/test/ELF/x86-64-dyn-rel-error.s
@@ -9,4 +9,4 @@ _start:
.data
.long bar
-// CHECK: R_X86_64_32 cannot be used against shared object; recompile with -fPIC.
+// CHECK: {{.*}}:(.data+0x0): relocation R_X86_64_32 cannot be used against shared object; recompile with -fPIC.
diff --git a/test/ELF/x86-64-dyn-rel-error2.s b/test/ELF/x86-64-dyn-rel-error2.s
index c1d3da3ae2ae..88a180da6274 100644
--- a/test/ELF/x86-64-dyn-rel-error2.s
+++ b/test/ELF/x86-64-dyn-rel-error2.s
@@ -9,4 +9,4 @@ _start:
.data
.long bar - .
-// CHECK: R_X86_64_PC32 cannot be used against shared object; recompile with -fPIC.
+// CHECK: {{.*}}:(.data+0x0): relocation R_X86_64_PC32 cannot be used against shared object; recompile with -fPIC.
diff --git a/test/ELF/x86-64-relax-got-abs.s b/test/ELF/x86-64-relax-got-abs.s
new file mode 100644
index 000000000000..c4291202f035
--- /dev/null
+++ b/test/ELF/x86-64-relax-got-abs.s
@@ -0,0 +1,16 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -relax-relocations -triple=x86_64-pc-linux %s \
+// RUN: -o %t.o
+// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: llvm-objdump -d %t.so | FileCheck %s
+
+// We used to fail trying to relax this into a pc relocation to an absolute
+// value.
+
+// CHECK: movq 4185(%rip), %rax
+
+ movq bar@GOTPCREL(%rip), %rax
+ .data
+ .global bar
+ .hidden bar
+ bar = 42
diff --git a/test/ELF/x86-64-reloc-32-fpic.s b/test/ELF/x86-64-reloc-32-fpic.s
index 0a0f1a09fda8..5726d36e9ad7 100644
--- a/test/ELF/x86-64-reloc-32-fpic.s
+++ b/test/ELF/x86-64-reloc-32-fpic.s
@@ -1,7 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
# RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-# CHECK: relocation R_X86_64_32 cannot be used against shared object; recompile with -fPIC.
+# CHECK: {{.*}}:(.data+0x0): relocation R_X86_64_32 cannot be used against shared object; recompile with -fPIC.
.data
.long _shared
diff --git a/test/ELF/x86-64-reloc-error.s b/test/ELF/x86-64-reloc-error.s
index 9b7e17de793b..ece1bd45aa4f 100644
--- a/test/ELF/x86-64-reloc-error.s
+++ b/test/ELF/x86-64-reloc-error.s
@@ -6,5 +6,5 @@
movl $big, %edx
movq $foo - 0x1000000000000, %rdx
-# CHECK: R_X86_64_32 out of range
-# CHECK: R_X86_64_32S out of range
+# CHECK: {{.*}}:(.text+0x1): relocation R_X86_64_32 out of range
+# CHECK: {{.*}}:(.text+0x8): relocation R_X86_64_32S out of range
diff --git a/test/ELF/x86-64-reloc-pc32-fpic.s b/test/ELF/x86-64-reloc-pc32-fpic.s
index ed9121583c18..3c9f3e0bdb38 100644
--- a/test/ELF/x86-64-reloc-pc32-fpic.s
+++ b/test/ELF/x86-64-reloc-pc32-fpic.s
@@ -1,7 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
# RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-# CHECK: relocation R_X86_64_PC32 cannot be used against shared object; recompile with -fPIC.
+# CHECK: {{.*}}:(.data+0x1): relocation R_X86_64_PC32 cannot be used against shared object; recompile with -fPIC.
.data
call _shared
diff --git a/test/ELF/x86-64-reloc-range.s b/test/ELF/x86-64-reloc-range.s
index 8319eaafa0ce..08f604ee6a30 100644
--- a/test/ELF/x86-64-reloc-range.s
+++ b/test/ELF/x86-64-reloc-range.s
@@ -1,7 +1,7 @@
// RUN: llvm-mc %s -o %t.o -triple x86_64-pc-linux -filetype=obj
// RUN: not ld.lld %t.o -o %t.so -shared 2>&1 | FileCheck %s
-// CHECK: relocation R_X86_64_PC32 out of range
+// CHECK: {{.*}}:(.text+0x3): relocation R_X86_64_PC32 out of range
// CHECK-NOT: relocation
lea foo(%rip), %rax
diff --git a/test/ELF/zdefs.s b/test/ELF/zdefs.s
index 410da1812db9..420f602c41ae 100644
--- a/test/ELF/zdefs.s
+++ b/test/ELF/zdefs.s
@@ -2,6 +2,6 @@
# RUN: ld.lld -shared %t.o -o %t1.so
# RUN: not ld.lld -z defs -shared %t.o -o %t1.so 2>&1 | FileCheck -check-prefix=ERR %s
-# ERR: undefined symbol: foo
+# ERR: error: {{.*}}:(.text+0x1): undefined symbol 'foo'
callq foo@PLT
diff --git a/test/ELF/zstack-size.s b/test/ELF/zstack-size.s
new file mode 100644
index 000000000000..36fbd03766ce
--- /dev/null
+++ b/test/ELF/zstack-size.s
@@ -0,0 +1,20 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: ld.lld -z stack-size=0x1000 %t -o %t1
+# RUN: llvm-readobj -program-headers %t1 | FileCheck %s
+
+.global _start
+_start:
+ nop
+
+# CHECK: Type: PT_GNU_STACK (0x6474E551)
+# CHECK-NEXT: Offset: 0x0
+# CHECK-NEXT: VirtualAddress: 0x0
+# CHECK-NEXT: PhysicalAddress: 0x0
+# CHECK-NEXT: FileSize: 0
+# CHECK-NEXT: MemSize: 4096
+# CHECK-NEXT: Flags [ (0x6)
+# CHECK-NEXT: PF_R (0x4)
+# CHECK-NEXT: PF_W (0x2)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 0
diff --git a/test/lit.cfg b/test/lit.cfg
index 5ff4fc4edcf2..a4105b34e122 100644
--- a/test/lit.cfg
+++ b/test/lit.cfg
@@ -150,15 +150,19 @@ if config.test_exec_root is None:
NoPreJunk = r"(?<!(-|\.|/))"
NoPostJunk = r"(?!(-|\.))"
+config.substitutions.append( (r"\bld.lld\b", 'ld.lld --full-shutdown') )
+
tool_patterns = [r"\bFileCheck\b",
r"\bnot\b",
NoPreJunk + r"\blld\b" + NoPostJunk,
r"\bld.lld\b",
r"\blld-link\b",
+ r"\bllvm-as\b",
r"\bllvm-mc\b",
r"\bllvm-nm\b",
r"\bllvm-objdump\b",
r"\bllvm-readobj\b",
+ r"\bobj2yaml\b",
r"\byaml2obj\b"]
for pattern in tool_patterns:
@@ -203,6 +207,10 @@ if platform.system() in ['Darwin']:
if platform.system() in ['FreeBSD', 'Linux']:
config.available_features.add('system-linker-elf')
+# Running on Windows
+if platform.system() in ['Windows']:
+ config.available_features.add('system-windows')
+
# Set if host-cxxabi's demangler can handle target's symbols.
if platform.system() not in ['Windows']:
config.available_features.add('demangler')
@@ -242,12 +250,15 @@ if re.search(r'AMDGPU', archs):
config.available_features.add('amdgpu')
llvm_config_cmd.wait()
+# Set a fake constant version so that we get consitent output.
+config.environment['LLD_VERSION'] = 'LLD 1.0'
+
# Check if Windows resource file compiler exists.
cvtres = lit.util.which('cvtres', config.environment['PATH'])
rc = lit.util.which('rc', config.environment['PATH'])
if cvtres and rc:
config.available_features.add('winres')
-# Check if "lib.exe" command exists.
-if lit.util.which('lib', config.environment['PATH']):
- config.available_features.add('winlib')
+# Check if "cpio" command exists.
+if lit.util.which('cpio', config.environment['PATH']):
+ config.available_features.add('cpio')
diff --git a/test/mach-o/Inputs/arm64/libSystem.yaml b/test/mach-o/Inputs/arm64/libSystem.yaml
new file mode 100644
index 000000000000..76cba1bc3255
--- /dev/null
+++ b/test/mach-o/Inputs/arm64/libSystem.yaml
@@ -0,0 +1,13 @@
+#
+# For use by test cases that create dynamic output types which may needs stubs
+# and therefore will need a dylib definition of dyld_stub_binder.
+#
+
+--- !mach-o
+arch: arm64
+file-type: MH_DYLIB
+install-name: /usr/lib/libSystem.B.dylib
+exports:
+ - name: dyld_stub_binder
+
+...
diff --git a/test/mach-o/Inputs/armv7/libSystem.yaml b/test/mach-o/Inputs/armv7/libSystem.yaml
new file mode 100644
index 000000000000..2539f9003540
--- /dev/null
+++ b/test/mach-o/Inputs/armv7/libSystem.yaml
@@ -0,0 +1,13 @@
+#
+# For use by test cases that create dynamic output types which may needs stubs
+# and therefore will need a dylib definition of dyld_stub_binder.
+#
+
+--- !mach-o
+arch: armv7
+file-type: MH_DYLIB
+install-name: /usr/lib/libSystem.B.dylib
+exports:
+ - name: dyld_stub_binder
+
+...
diff --git a/test/mach-o/Inputs/libSystem.yaml b/test/mach-o/Inputs/libSystem.yaml
deleted file mode 100644
index 2a7f46381dcc..000000000000
--- a/test/mach-o/Inputs/libSystem.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-#
-# For use by test cases that create dynamic output types which may needs stubs
-# and therefore will need a dylib definition of dyld_stub_binder.
-#
-
----
-shared-library-atoms:
- - name: dyld_stub_binder
- load-name: /usr/lib/libSystem.B.dylib
- type: code
- size: 0
-
-...
diff --git a/test/mach-o/Inputs/x86/libSystem.yaml b/test/mach-o/Inputs/x86/libSystem.yaml
new file mode 100644
index 000000000000..87a4895c9f4a
--- /dev/null
+++ b/test/mach-o/Inputs/x86/libSystem.yaml
@@ -0,0 +1,13 @@
+#
+# For use by test cases that create dynamic output types which may needs stubs
+# and therefore will need a dylib definition of dyld_stub_binder.
+#
+
+--- !mach-o
+arch: x86
+file-type: MH_DYLIB
+install-name: /usr/lib/libSystem.B.dylib
+exports:
+ - name: dyld_stub_binder
+
+...
diff --git a/test/mach-o/Inputs/x86_64/libSystem.yaml b/test/mach-o/Inputs/x86_64/libSystem.yaml
new file mode 100644
index 000000000000..fbbf794f3264
--- /dev/null
+++ b/test/mach-o/Inputs/x86_64/libSystem.yaml
@@ -0,0 +1,13 @@
+#
+# For use by test cases that create dynamic output types which may needs stubs
+# and therefore will need a dylib definition of dyld_stub_binder.
+#
+
+--- !mach-o
+arch: x86_64
+file-type: MH_DYLIB
+install-name: /usr/lib/libSystem.B.dylib
+exports:
+ - name: dyld_stub_binder
+
+...
diff --git a/test/mach-o/arm-interworking-movw.yaml b/test/mach-o/arm-interworking-movw.yaml
index 0423ed644576..b555112dde82 100644
--- a/test/mach-o/arm-interworking-movw.yaml
+++ b/test/mach-o/arm-interworking-movw.yaml
@@ -1,7 +1,7 @@
# REQUIRES: arm
# RUN: lld -flavor darwin -arch armv7 -r -print_atoms %s -o %t | FileCheck %s
# RUN: lld -flavor darwin -arch armv7 -dylib -print_atoms %t -o %t2 \
-# RUN: %p/Inputs/libSystem.yaml -sectalign __TEXT __text 0x1000 | FileCheck %s
+# RUN: %p/Inputs/armv7/libSystem.yaml -sectalign __TEXT __text 0x1000 | FileCheck %s
# RUN: llvm-objdump -d -macho -no-symbolic-operands %t2 | FileCheck -check-prefix=CODE %s
#
# Test thumb and arm branches round trip through -r.
diff --git a/test/mach-o/arm-interworking.yaml b/test/mach-o/arm-interworking.yaml
index 59c46050fab5..3988a1958458 100644
--- a/test/mach-o/arm-interworking.yaml
+++ b/test/mach-o/arm-interworking.yaml
@@ -1,7 +1,7 @@
# RUN: lld -flavor darwin -arch armv7 -r -print_atoms %s \
# RUN: %p/Inputs/arm-interworking.yaml -o %t | FileCheck %s \
# RUN: && lld -flavor darwin -arch armv7 -dylib -print_atoms \
-# RUN: %p/Inputs/libSystem.yaml %t -o %t2 | FileCheck %s \
+# RUN: %p/Inputs/armv7/libSystem.yaml %t -o %t2 | FileCheck %s \
# RUN: && llvm-readobj -s -sd %t2 | FileCheck -check-prefix=CODE %s
#
# Test thumb and arm branches round trip through -r.
diff --git a/test/mach-o/arm-shims.yaml b/test/mach-o/arm-shims.yaml
index 62739c967ab1..1b54de4f05aa 100644
--- a/test/mach-o/arm-shims.yaml
+++ b/test/mach-o/arm-shims.yaml
@@ -1,5 +1,5 @@
# RUN: lld -flavor darwin -arch armv7 %s %p/Inputs/arm-shims.yaml \
-# RUN: -dylib %p/Inputs/libSystem.yaml -o %t
+# RUN: -dylib %p/Inputs/armv7/libSystem.yaml -o %t
# RUN: llvm-readobj -s -sd %t | FileCheck %s
#
# Test b from arm to thumb or vice versa has shims added.s
diff --git a/test/mach-o/arm64-reloc-negDelta32-fixup.yaml b/test/mach-o/arm64-reloc-negDelta32-fixup.yaml
index 4da88097eb60..ee8686cabb62 100644
--- a/test/mach-o/arm64-reloc-negDelta32-fixup.yaml
+++ b/test/mach-o/arm64-reloc-negDelta32-fixup.yaml
@@ -21,18 +21,18 @@ compat-version: 0.0
current-version: 0.0
has-UUID: false
OS: unknown
-sections:
+sections:
- segment: __TEXT
section: __text
type: S_REGULAR
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
alignment: 4
address: 0x0000000000000000
- content: [ 0xFD, 0x7B, 0xBF, 0xA9, 0xFD, 0x03, 0x00, 0x91,
- 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x91,
- 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x80, 0x52,
+ content: [ 0xFD, 0x7B, 0xBF, 0xA9, 0xFD, 0x03, 0x00, 0x91,
+ 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x91,
+ 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x80, 0x52,
0xFD, 0x7B, 0xC1, 0xA8, 0xC0, 0x03, 0x5F, 0xD6 ]
- relocations:
+ relocations:
- offset: 0x00000010
type: ARM64_RELOC_BRANCH26
length: 2
@@ -56,7 +56,7 @@ sections:
type: S_CSTRING_LITERALS
attributes: [ ]
address: 0x0000000000000020
- content: [ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F,
+ content: [ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F,
0x72, 0x6C, 0x64, 0x00 ]
- segment: __LD
section: __compact_unwind
@@ -64,11 +64,11 @@ sections:
attributes: [ ]
alignment: 8
address: 0x0000000000000030
- content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
- relocations:
+ relocations:
- offset: 0x00000000
type: ARM64_RELOC_UNSIGNED
length: 3
@@ -81,14 +81,14 @@ sections:
attributes: [ ]
alignment: 8
address: 0x0000000000000050
- content: [ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x7A, 0x52, 0x00, 0x01, 0x78, 0x1E, 0x01,
- 0x10, 0x0C, 0x1F, 0x00, 0x20, 0x00, 0x00, 0x00,
- 0x18, 0x00, 0x00, 0x00, 0x94, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x0E, 0x10,
+ content: [ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x7A, 0x52, 0x00, 0x01, 0x78, 0x1E, 0x01,
+ 0x10, 0x0C, 0x1F, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x94, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x0E, 0x10,
0x9E, 0x01, 0x9D, 0x02, 0x00, 0x00, 0x00, 0x00 ]
-local-symbols:
+local-symbols:
- name: ltmp0
type: N_SECT
sect: 1
@@ -109,13 +109,13 @@ local-symbols:
type: N_SECT
sect: 4
value: 0x0000000000000050
-global-symbols:
+global-symbols:
- name: __Z3fooi
type: N_SECT
scope: [ N_EXT ]
sect: 1
value: 0x0000000000000000
-undefined-symbols:
+undefined-symbols:
- name: _puts
type: N_UNDF
scope: [ N_EXT ]
diff --git a/test/mach-o/arm64-relocs-errors-delta64-offset.yaml b/test/mach-o/arm64-relocs-errors-delta64-offset.yaml
index 08e41bc65fff..d238097f9695 100644
--- a/test/mach-o/arm64-relocs-errors-delta64-offset.yaml
+++ b/test/mach-o/arm64-relocs-errors-delta64-offset.yaml
@@ -14,11 +14,11 @@ sections:
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
alignment: 4
address: 0x0000000000000000
- content: [ 0xFF, 0x83, 0x00, 0xD1, 0xE0, 0x0B, 0x00, 0xF9,
- 0x08, 0x00, 0x40, 0xB9, 0x08, 0x0D, 0x00, 0x71,
- 0x08, 0x09, 0x00, 0x71, 0xE8, 0x0F, 0x00, 0xB9,
- 0xC8, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, 0x14,
- 0xE8, 0x03, 0x00, 0x32, 0x08, 0x01, 0x00, 0x12,
+ content: [ 0xFF, 0x83, 0x00, 0xD1, 0xE0, 0x0B, 0x00, 0xF9,
+ 0x08, 0x00, 0x40, 0xB9, 0x08, 0x0D, 0x00, 0x71,
+ 0x08, 0x09, 0x00, 0x71, 0xE8, 0x0F, 0x00, 0xB9,
+ 0xC8, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, 0x14,
+ 0xE8, 0x03, 0x00, 0x32, 0x08, 0x01, 0x00, 0x12,
0xE8, 0x7F, 0x00, 0x39, 0x02, 0x00, 0x00, 0x14 ]
- segment: __DATA
section: __data
@@ -34,7 +34,7 @@ sections:
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
- relocations:
+ relocations:
# Make sure that the offsets of the subtractor and unsigned both match.
# CHECK: bad relocation (paired relocs must have the same offset) in section __DATA/__data (r1_address=1, r1_type=1, r1_extern=1, r1_length=3, r1_pcrel=0, r1_symbolnum=1), (r2_address=0, r2_type=0, r2_extern=1, r2_length=3, r2_pcrel=0, r2_symbolnum=1)
diff --git a/test/mach-o/bind-opcodes.yaml b/test/mach-o/bind-opcodes.yaml
new file mode 100644
index 000000000000..ad8cd169a85c
--- /dev/null
+++ b/test/mach-o/bind-opcodes.yaml
@@ -0,0 +1,143 @@
+# RUN: lld -flavor darwin -arch arm64 %s %p/Inputs/hello-world-arm64.yaml -o %t
+# RUN: obj2yaml %t | FileCheck %s
+#
+
+--- !mach-o
+arch: arm64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 2
+ address: 0x0000000000000000
+ content: [ 0xFD, 0x7B, 0xBF, 0xA9, 0xFD, 0x03, 0x00, 0x91,
+ 0x08, 0x00, 0x00, 0x90, 0x08, 0x01, 0x40, 0xF9,
+ 0x00, 0x01, 0x40, 0xF9, 0x01, 0x00, 0x00, 0x90,
+ 0x21, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x94,
+ 0x00, 0x00, 0x80, 0x52, 0xFD, 0x7B, 0xC1, 0xA8,
+ 0xC0, 0x03, 0x5F, 0xD6 ]
+ relocations:
+ - offset: 0x0000001C
+ type: ARM64_RELOC_BRANCH26
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 5
+ - offset: 0x00000018
+ type: ARM64_RELOC_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 1
+ - offset: 0x00000014
+ type: ARM64_RELOC_PAGE21
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x0000000C
+ type: ARM64_RELOC_GOT_LOAD_PAGEOFF12
+ length: 2
+ pc-rel: false
+ extern: true
+ symbol: 4
+ - offset: 0x00000008
+ type: ARM64_RELOC_GOT_LOAD_PAGE21
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 4
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x000000000000002C
+ content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ]
+local-symbols:
+ - name: ltmp0
+ type: N_SECT
+ sect: 1
+ value: 0x0000000000000000
+ - name: l_.str
+ type: N_SECT
+ sect: 2
+ value: 0x000000000000002C
+ - name: ltmp1
+ type: N_SECT
+ sect: 2
+ value: 0x000000000000002C
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: ___stdoutp
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+ - name: _fprintf
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+
+# CHECK: BindOpcodes:
+# CHECK: - Opcode: BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
+# CHECK: Imm: 1
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
+# CHECK: Imm: 0
+# CHECK: Symbol: dyld_stub_binder
+# CHECK: - Opcode: BIND_OPCODE_SET_TYPE_IMM
+# CHECK: Imm: 1
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
+# CHECK: Imm: 2
+# CHECK: ULEBExtraData:
+# CHECK: - 0x0000000000000000
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_DO_BIND
+# CHECK: Imm: 0
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
+# CHECK: Imm: 0
+# CHECK: Symbol: ___stdoutp
+# CHECK: - Opcode: BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
+# CHECK: Imm: 2
+# CHECK: ULEBExtraData:
+# CHECK: - 0x0000000000000010
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_DO_BIND
+# CHECK: Imm: 0
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_DONE
+# CHECK: Imm: 0
+# CHECK: Symbol: ''
+
+# CHECK: LazyBindOpcodes:
+# CHECK: - Opcode: BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB
+# CHECK: Imm: 2
+# CHECK: ULEBExtraData:
+# CHECK: - 0x0000000000000018
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_SET_DYLIB_ORDINAL_IMM
+# CHECK: Imm: 1
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM
+# CHECK: Imm: 0
+# CHECK: Symbol: _fprintf
+# CHECK: - Opcode: BIND_OPCODE_DO_BIND
+# CHECK: Imm: 0
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_DONE
+# CHECK: Imm: 0
+# CHECK: Symbol: ''
+# CHECK: - Opcode: BIND_OPCODE_DONE
+# CHECK: Imm: 0
+# CHECK: Symbol: '' \ No newline at end of file
diff --git a/test/mach-o/data-in-code-load-command.yaml b/test/mach-o/data-in-code-load-command.yaml
index 604592afcd3f..0c84bd4d7452 100644
--- a/test/mach-o/data-in-code-load-command.yaml
+++ b/test/mach-o/data-in-code-load-command.yaml
@@ -1,11 +1,11 @@
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml && llvm-objdump -private-headers %t | FileCheck %s
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml -static -data_in_code_info && llvm-objdump -private-headers %t | FileCheck %s
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml -no_data_in_code_info && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_DATA_IN_CODE_INFO
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml -static -data_in_code_info -no_data_in_code_info && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_DATA_IN_CODE_INFO
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml -static && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_DATA_IN_CODE_INFO
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml -r && llvm-objdump -private-headers %t | FileCheck %s
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml -r -data_in_code_info && llvm-objdump -private-headers %t | FileCheck %s
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml -r -no_data_in_code_info && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_DATA_IN_CODE_INFO
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml && llvm-objdump -private-headers %t | FileCheck %s
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static -data_in_code_info && llvm-objdump -private-headers %t | FileCheck %s
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -no_data_in_code_info && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_DATA_IN_CODE_INFO
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static -data_in_code_info -no_data_in_code_info && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_DATA_IN_CODE_INFO
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_DATA_IN_CODE_INFO
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -r && llvm-objdump -private-headers %t | FileCheck %s
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -r -data_in_code_info && llvm-objdump -private-headers %t | FileCheck %s
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -r -no_data_in_code_info && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_DATA_IN_CODE_INFO
--- !mach-o
arch: x86_64
diff --git a/test/mach-o/data-only-dylib.yaml b/test/mach-o/data-only-dylib.yaml
index 8d2143047452..541e02f61ef9 100644
--- a/test/mach-o/data-only-dylib.yaml
+++ b/test/mach-o/data-only-dylib.yaml
@@ -1,4 +1,4 @@
-# RUN: lld -flavor darwin -arch x86_64 -dylib %s -o %t %p/Inputs/libSystem.yaml
+# RUN: lld -flavor darwin -arch x86_64 -dylib %s -o %t %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-nm %t | FileCheck %s
#
# Test that a data-only dylib can be built.
diff --git a/test/mach-o/dead-strip-globals.yaml b/test/mach-o/dead-strip-globals.yaml
index df5ff73aa441..45d919db3aa5 100644
--- a/test/mach-o/dead-strip-globals.yaml
+++ b/test/mach-o/dead-strip-globals.yaml
@@ -1,6 +1,9 @@
-# RUN: lld -flavor darwin -arch x86_64 -dead_strip -export_dynamic %s -dylib %p/Inputs/libSystem.yaml -o %t.dylib -print_atoms | FileCheck -check-prefix=CHECK1 %s
-# RUN: lld -flavor darwin -arch x86_64 -export_dynamic -dead_strip %s -dylib %p/Inputs/libSystem.yaml -o %t.dylib -print_atoms | FileCheck -check-prefix=CHECK1 %s
-# RUN: lld -flavor darwin -arch x86_64 -dead_strip %s -dylib %p/Inputs/libSystem.yaml -o %t2.dylib -print_atoms | FileCheck -check-prefix=CHECK2 %s
+# RUN: lld -flavor darwin -arch x86_64 -dead_strip -export_dynamic %s -dylib %p/Inputs/x86_64/libSystem.yaml -o %t.dylib -print_atoms | FileCheck -check-prefix=CHECK1 %s
+# RUN: lld -flavor darwin -arch x86_64 -export_dynamic -dead_strip %s -dylib %p/Inputs/x86_64/libSystem.yaml -o %t.dylib -print_atoms | FileCheck -check-prefix=CHECK1 %s
+# RUN: lld -flavor darwin -arch x86_64 -dead_strip %s -dylib %p/Inputs/x86_64/libSystem.yaml -o %t2.dylib -print_atoms | FileCheck -check-prefix=CHECK2 %s
+
+# RUN: lld -flavor darwin -arch x86_64 -r %s -dylib %p/Inputs/x86_64/libSystem.yaml -o %t3.o
+# RUN: llvm-nm -m %t3.o | FileCheck -check-prefix=RELOCATABLE_SYMBOLS %s
#
# Test that -export_dynamic -dead-strip from removing globals.
@@ -25,3 +28,4 @@ shared-library-atoms:
# CHECK2: name: def
# CHECK2-NOT: name: dead
+# RELOCATABLE_SYMBOLS: external def
diff --git a/test/mach-o/debug-syms.yaml b/test/mach-o/debug-syms.yaml
new file mode 100644
index 000000000000..28428724ffe7
--- /dev/null
+++ b/test/mach-o/debug-syms.yaml
@@ -0,0 +1,249 @@
+# RUN: lld -flavor darwin -arch x86_64 -o %t %s -dylib %p/Inputs/x86_64/libSystem.yaml && \
+# RUN: llvm-nm -no-sort -debug-syms %t | FileCheck %s
+
+# CHECK: 0000000000000000 - 00 0000 SO /Users/lhames/Projects/lld/lld-svn-tot/scratch/
+# CHECK-NEXT: 0000000000000000 - 00 0000 SO hw.c
+# CHECK-NEXT: {{[0-9a-f]+}} - 03 0001 OSO {{.*}}{{/|\\}}test{{/|\\}}mach-o{{/|\\}}debug-syms.yaml
+# CHECK-NEXT: 0000000000000fa0 - 01 0000 BNSYM
+# CHECK-NEXT: 0000000000000fa0 - 01 0000 FUN _main
+# CHECK-NEXT: 0000000000000016 - 00 0000 FUN
+# CHECK-NEXT: 0000000000000016 - 01 0000 ENSYM
+# CHECK-NEXT: 0000000000000000 - 01 0000 SO
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+compat-version: 0.0
+current-version: 0.0
+has-UUID: false
+OS: unknown
+min-os-version-kind: LC_VERSION_MIN_MACOSX
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ alignment: 16
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x31, 0xC0, 0xC7, 0x45,
+ 0xFC, 0x00, 0x00, 0x00, 0x00, 0x89, 0x7D, 0xF8,
+ 0x48, 0x89, 0x75, 0xF0, 0x5D, 0xC3 ]
+ - segment: __DWARF
+ section: __debug_str
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x0000000000000016
+ content: [ 0x41, 0x70, 0x70, 0x6C, 0x65, 0x20, 0x4C, 0x4C,
+ 0x56, 0x4D, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69,
+ 0x6F, 0x6E, 0x20, 0x38, 0x2E, 0x30, 0x2E, 0x30,
+ 0x20, 0x28, 0x63, 0x6C, 0x61, 0x6E, 0x67, 0x2D,
+ 0x38, 0x30, 0x30, 0x2E, 0x30, 0x2E, 0x32, 0x34,
+ 0x2E, 0x31, 0x29, 0x00, 0x68, 0x77, 0x2E, 0x63,
+ 0x00, 0x2F, 0x55, 0x73, 0x65, 0x72, 0x73, 0x2F,
+ 0x6C, 0x68, 0x61, 0x6D, 0x65, 0x73, 0x2F, 0x50,
+ 0x72, 0x6F, 0x6A, 0x65, 0x63, 0x74, 0x73, 0x2F,
+ 0x6C, 0x6C, 0x64, 0x2F, 0x6C, 0x6C, 0x64, 0x2D,
+ 0x73, 0x76, 0x6E, 0x2D, 0x74, 0x6F, 0x74, 0x2F,
+ 0x73, 0x63, 0x72, 0x61, 0x74, 0x63, 0x68, 0x00,
+ 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x69, 0x6E, 0x74,
+ 0x00, 0x61, 0x72, 0x67, 0x63, 0x00, 0x61, 0x72,
+ 0x67, 0x76, 0x00, 0x63, 0x68, 0x61, 0x72, 0x00 ]
+ - segment: __DWARF
+ section: __debug_loc
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x000000000000008E
+ - segment: __DWARF
+ section: __debug_abbrev
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x000000000000008E
+ content: [ 0x01, 0x11, 0x01, 0x25, 0x0E, 0x13, 0x05, 0x03,
+ 0x0E, 0x10, 0x06, 0x1B, 0x0E, 0x11, 0x01, 0x12,
+ 0x01, 0x00, 0x00, 0x02, 0x2E, 0x01, 0x11, 0x01,
+ 0x12, 0x01, 0x40, 0x0A, 0x03, 0x0E, 0x3A, 0x0B,
+ 0x3B, 0x0B, 0x27, 0x0C, 0x49, 0x13, 0x3F, 0x0C,
+ 0x00, 0x00, 0x03, 0x05, 0x00, 0x02, 0x0A, 0x03,
+ 0x0E, 0x3A, 0x0B, 0x3B, 0x0B, 0x49, 0x13, 0x00,
+ 0x00, 0x04, 0x24, 0x00, 0x03, 0x0E, 0x3E, 0x0B,
+ 0x0B, 0x0B, 0x00, 0x00, 0x05, 0x0F, 0x00, 0x49,
+ 0x13, 0x00, 0x00, 0x00 ]
+ - segment: __DWARF
+ section: __debug_info
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x00000000000000DA
+ content: [ 0x7F, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x56, 0x60, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x6A, 0x00, 0x00, 0x00, 0x01, 0x03, 0x02, 0x91,
+ 0x78, 0x69, 0x00, 0x00, 0x00, 0x01, 0x01, 0x6A,
+ 0x00, 0x00, 0x00, 0x03, 0x02, 0x91, 0x70, 0x6E,
+ 0x00, 0x00, 0x00, 0x01, 0x01, 0x71, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x65, 0x00, 0x00, 0x00, 0x05,
+ 0x04, 0x05, 0x76, 0x00, 0x00, 0x00, 0x05, 0x7B,
+ 0x00, 0x00, 0x00, 0x04, 0x73, 0x00, 0x00, 0x00,
+ 0x06, 0x01, 0x00 ]
+ relocations:
+ - offset: 0x00000037
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x0000002F
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x00000026
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - offset: 0x0000001E
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - segment: __DWARF
+ section: __debug_ranges
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x000000000000015D
+ - segment: __DWARF
+ section: __debug_macinfo
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x000000000000015D
+ content: [ 0x00 ]
+ - segment: __DWARF
+ section: __apple_names
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x000000000000015E
+ content: [ 0x48, 0x53, 0x41, 0x48, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x6A, 0x7F, 0x9A, 0x7C,
+ 0x2C, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00 ]
+ - segment: __DWARF
+ section: __apple_objc
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x000000000000019A
+ content: [ 0x48, 0x53, 0x41, 0x48, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF ]
+ - segment: __DWARF
+ section: __apple_namespac
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x00000000000001BE
+ content: [ 0x48, 0x53, 0x41, 0x48, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF ]
+ - segment: __DWARF
+ section: __apple_types
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x00000000000001E2
+ content: [ 0x48, 0x53, 0x41, 0x48, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00,
+ 0x03, 0x00, 0x05, 0x00, 0x04, 0x00, 0x0B, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x30, 0x80, 0x88, 0x0B, 0x63, 0x20, 0x95, 0x7C,
+ 0x40, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00,
+ 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x6A, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x24,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ - segment: __DWARF
+ section: __apple_exttypes
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x0000000000000248
+ content: [ 0x48, 0x53, 0x41, 0x48, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x06, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF ]
+ - segment: __LD
+ section: __compact_unwind
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ alignment: 8
+ address: 0x0000000000000270
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ relocations:
+ - offset: 0x00000000
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+ - segment: __TEXT
+ section: __eh_frame
+ type: S_COALESCED
+ attributes: [ ]
+ alignment: 8
+ address: 0x0000000000000290
+ content: [ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x7A, 0x52, 0x00, 0x01, 0x78, 0x10, 0x01,
+ 0x10, 0x0C, 0x07, 0x08, 0x90, 0x01, 0x00, 0x00,
+ 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00,
+ 0x50, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x41, 0x0E, 0x10, 0x86, 0x02, 0x43, 0x0D,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
+ - segment: __DWARF
+ section: __debug_line
+ type: S_REGULAR
+ attributes: [ S_ATTR_DEBUG ]
+ address: 0x00000000000002D0
+ content: [ 0x37, 0x00, 0x00, 0x00, 0x02, 0x00, 0x1B, 0x00,
+ 0x00, 0x00, 0x01, 0x01, 0xFB, 0x0E, 0x0D, 0x00,
+ 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x01, 0x00, 0x68, 0x77, 0x2E, 0x63,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x05, 0x03, 0x0A, 0x08, 0x3D, 0x02, 0x02,
+ 0x00, 0x01, 0x01 ]
+ relocations:
+ - offset: 0x00000028
+ type: X86_64_RELOC_UNSIGNED
+ length: 3
+ pc-rel: false
+ extern: false
+ symbol: 1
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+page-size: 0x00000000
+...
diff --git a/test/mach-o/demangle.yaml b/test/mach-o/demangle.yaml
index f8a76cddd29f..333a59eaa528 100644
--- a/test/mach-o/demangle.yaml
+++ b/test/mach-o/demangle.yaml
@@ -1,11 +1,11 @@
# REQUIRES: system-linker-mach-o
#
# RUN: not lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s \
-# RUN: -dylib -o %t %p/Inputs/libSystem.yaml 2> %t.err
+# RUN: -dylib -o %t %p/Inputs/x86_64/libSystem.yaml 2> %t.err
# RUN: FileCheck %s < %t.err
#
# RUN: not lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s \
-# RUN: -dylib -o %t %p/Inputs/libSystem.yaml -demangle 2> %t.err2
+# RUN: -dylib -o %t %p/Inputs/x86_64/libSystem.yaml -demangle 2> %t.err2
# RUN: FileCheck %s --check-prefix=DCHECK < %t.err2
#
# Test -demangle option works on undefined symbol errors.
diff --git a/test/mach-o/dso_handle.yaml b/test/mach-o/dso_handle.yaml
index 400e2c899c90..0796c2ee566d 100644
--- a/test/mach-o/dso_handle.yaml
+++ b/test/mach-o/dso_handle.yaml
@@ -1,13 +1,13 @@
-# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/libSystem.yaml -o %t1
+# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/x86_64/libSystem.yaml -o %t1
# RUN: llvm-nm -m -n %t1 | FileCheck %s
#
-# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/libSystem.yaml -dead_strip -o %t2
+# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/x86_64/libSystem.yaml -dead_strip -o %t2
# RUN: llvm-nm -m -n %t2 | FileCheck %s
#
-# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/libSystem.yaml -dylib -o %t3
+# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/x86_64/libSystem.yaml -dylib -o %t3
# RUN: llvm-nm -m -n %t3 | FileCheck %s
#
-# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/libSystem.yaml -bundle -o %t4
+# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/x86_64/libSystem.yaml -bundle -o %t4
# RUN: llvm-nm -m -n %t4 | FileCheck %s
#
# Test that ___dso_handle symbol is available for executables, bundles, and dylibs
diff --git a/test/mach-o/dylib-install-names.yaml b/test/mach-o/dylib-install-names.yaml
index 845085be42ab..af00adfd5908 100644
--- a/test/mach-o/dylib-install-names.yaml
+++ b/test/mach-o/dylib-install-names.yaml
@@ -1,25 +1,25 @@
# Check we accept -install_name correctly:
# RUN: lld -flavor darwin -arch x86_64 -install_name libwibble.dylib -dylib \
# RUN: -compatibility_version 2.0 -current_version 5.3 \
-# RUN: %p/Inputs/libSystem.yaml %s -o %t.dylib
+# RUN: %p/Inputs/x86_64/libSystem.yaml %s -o %t.dylib
# RUN: llvm-objdump -private-headers %t.dylib | FileCheck %s --check-prefix=CHECK-BINARY-WRITE
# Check we read LC_ID_DYLIB correctly:
# RUN: lld -flavor darwin -arch x86_64 %p/Inputs/use-dylib-install-names.yaml \
-# RUN: %p/Inputs/libSystem.yaml %t.dylib -dylib -o %t2.dylib
+# RUN: %p/Inputs/x86_64/libSystem.yaml %t.dylib -dylib -o %t2.dylib
# RUN: llvm-objdump -private-headers %t2.dylib | FileCheck %s --check-prefix=CHECK-BINARY-READ
# Check we default the install-name to the output file:
# RUN: lld -flavor darwin -arch x86_64 -dylib %s -o libwibble.dylib \
# RUN: -compatibility_version 2.0 -current_version 5.3 \
-# RUN: %p/Inputs/libSystem.yaml
+# RUN: %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-objdump -private-headers libwibble.dylib | FileCheck %s --check-prefix=CHECK-BINARY-WRITE
# RUN: rm -f libwibble.dylib
# Check -single_module does nothing
# RUN: lld -flavor darwin -arch x86_64 -dylib %s -install_name libwibble.dylib \
# RUN: -compatibility_version 2.0 -current_version 5.3 \
-# RUN: -single_module -o %t2.dylib %p/Inputs/libSystem.yaml
+# RUN: -single_module -o %t2.dylib %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-objdump -private-headers %t2.dylib | FileCheck %s --check-prefix=CHECK-BINARY-WRITE
--- !mach-o
@@ -63,8 +63,8 @@ global-symbols:
# CHECK-BINARY-READ-NEXT: cmdsize 56
# CHECK-BINARY-READ-NEXT: name /usr/lib/libSystem.B.dylib (offset 24)
# CHECK-BINARY-READ-NEXT: time stamp 2
-# CHECK-BINARY-READ-NEXT: current version 0.16.0
-# CHECK-BINARY-READ-NEXT: compatibility version 0.16.0
+# CHECK-BINARY-READ-NEXT: current version 1.0.0
+# CHECK-BINARY-READ-NEXT: compatibility version 1.0.0
# CHECK-BINARY-READ: cmd LC_LOAD_DYLIB
# CHECK-BINARY-READ-NEXT: cmdsize 40
diff --git a/test/mach-o/eh-frame-relocs-arm64.yaml b/test/mach-o/eh-frame-relocs-arm64.yaml
index 6681ded79498..e23dd7c48ed9 100644
--- a/test/mach-o/eh-frame-relocs-arm64.yaml
+++ b/test/mach-o/eh-frame-relocs-arm64.yaml
@@ -12,15 +12,15 @@ compat-version: 0.0
current-version: 0.0
has-UUID: false
OS: unknown
-sections:
+sections:
- segment: __TEXT
section: __text
type: S_REGULAR
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
alignment: 4
address: 0x0000000000000000
- content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xC0, 0x03, 0x5F, 0xD6, 0xC0, 0x03, 0x5F, 0xD6,
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xC0, 0x03, 0x5F, 0xD6, 0xC0, 0x03, 0x5F, 0xD6,
0xC0, 0x03, 0x5F, 0xD6 ]
- segment: __TEXT
section: __gcc_except_tab
@@ -40,15 +40,15 @@ sections:
attributes: [ ]
alignment: 8
address: 0x0000000000000020
- content: [ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ content: [ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
- relocations:
+ relocations:
- offset: 0x00000020
type: ARM64_RELOC_UNSIGNED
length: 3
@@ -67,25 +67,25 @@ sections:
attributes: [ ]
alignment: 8
address: 0x0000000000000060
- content: [ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x7A, 0x50, 0x4C, 0x52, 0x00, 0x01, 0x78,
- 0x1E, 0x07, 0x9B, 0xED, 0xFF, 0xFF, 0xFF, 0x10,
- 0x10, 0x0C, 0x1F, 0x00, 0x28, 0x00, 0x00, 0x00,
- 0x20, 0x00, 0x00, 0x00, 0xDC, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0xCB, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0E, 0x10, 0x9E,
- 0x01, 0x9D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x7A, 0x50, 0x4C, 0x52, 0x00, 0x01, 0x78,
- 0x1E, 0x07, 0x9B, 0xA9, 0xFF, 0xFF, 0xFF, 0x10,
- 0x10, 0x0C, 0x1F, 0x00, 0x28, 0x00, 0x00, 0x00,
- 0x20, 0x00, 0x00, 0x00, 0x94, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x83, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0E, 0x10, 0x9E,
+ content: [ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x7A, 0x50, 0x4C, 0x52, 0x00, 0x01, 0x78,
+ 0x1E, 0x07, 0x9B, 0xED, 0xFF, 0xFF, 0xFF, 0x10,
+ 0x10, 0x0C, 0x1F, 0x00, 0x28, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0xDC, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0xCB, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0E, 0x10, 0x9E,
+ 0x01, 0x9D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x7A, 0x50, 0x4C, 0x52, 0x00, 0x01, 0x78,
+ 0x1E, 0x07, 0x9B, 0xA9, 0xFF, 0xFF, 0xFF, 0x10,
+ 0x10, 0x0C, 0x1F, 0x00, 0x28, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x94, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x83, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0E, 0x10, 0x9E,
0x01, 0x9D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 ]
- relocations:
+ relocations:
- offset: 0x0000007D
type: ARM64_RELOC_SUBTRACTOR
length: 3
@@ -146,7 +146,7 @@ sections:
pc-rel: true
extern: true
symbol: 9
-local-symbols:
+local-symbols:
- name: ltmp0
type: N_SECT
sect: 1
@@ -175,7 +175,7 @@ local-symbols:
type: N_SECT
sect: 5
value: 0x0000000000000060
-global-symbols:
+global-symbols:
- name: __Z3fooi
type: N_SECT
scope: [ N_EXT ]
@@ -211,24 +211,24 @@ page-size: 0x00000000
# CHECK: --- !native
# CHECK: path: '<linker-internal>'
-# CHECK: defined-atoms:
+# CHECK: defined-atoms:
# CHECK: - ref-name: L000
# CHECK: type: unwind-cfi
-# CHECK: content: [ 18, 00, 00, 00, 00, 00, 00, 00, 03, 7A, 50, 4C,
-# CHECK: 52, 00, 01, 78, 1E, 07, 9B, {{..}}, {{..}}, {{..}}, {{..}}, 10,
+# CHECK: content: [ 18, 00, 00, 00, 00, 00, 00, 00, 03, 7A, 50, 4C,
+# CHECK: 52, 00, 01, 78, 1E, 07, 9B, {{..}}, {{..}}, {{..}}, {{..}}, 10,
# CHECK: 10, 0C, 1F, 00 ]
# CHECK: alignment: 8
-# CHECK: references:
+# CHECK: references:
# CHECK: - kind: unwindCIEToPersonalityFunction
# CHECK: offset: 19
# CHECK: target: __gxx_personality_v0
# CHECK: - type: unwind-cfi
-# CHECK: content: [ 28, 00, 00, 00, 20, 00, 00, 00, {{..}}, {{..}}, {{..}}, {{..}},
-# CHECK: {{..}}, {{..}}, {{..}}, {{..}}, 04, 00, 00, 00, 00, 00, 00, 00,
-# CHECK: 08, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, 0E, 10, 9E,
+# CHECK: content: [ 28, 00, 00, 00, 20, 00, 00, 00, {{..}}, {{..}}, {{..}}, {{..}},
+# CHECK: {{..}}, {{..}}, {{..}}, {{..}}, 04, 00, 00, 00, 00, 00, 00, 00,
+# CHECK: 08, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, 0E, 10, 9E,
# CHECK: 01, 9D, 02, 00, 00, 00, 00, 00 ]
# CHECK: alignment: 4 mod 8
-# CHECK: references:
+# CHECK: references:
# CHECK: - kind: negDelta32
# CHECK: offset: 4
# CHECK: target: L000
@@ -240,21 +240,21 @@ page-size: 0x00000000
# CHECK: target: _bar1
# CHECK: - ref-name: L001
# CHECK: type: unwind-cfi
-# CHECK: content: [ 18, 00, 00, 00, 00, 00, 00, 00, 03, 7A, 50, 4C,
-# CHECK: 52, 00, 01, 78, 1E, 07, 9B, {{..}}, {{..}}, {{..}}, {{..}}, 10,
+# CHECK: content: [ 18, 00, 00, 00, 00, 00, 00, 00, 03, 7A, 50, 4C,
+# CHECK: 52, 00, 01, 78, 1E, 07, 9B, {{..}}, {{..}}, {{..}}, {{..}}, 10,
# CHECK: 10, 0C, 1F, 00 ]
# CHECK: alignment: 8
-# CHECK: references:
+# CHECK: references:
# CHECK: - kind: unwindCIEToPersonalityFunction
# CHECK: offset: 19
# CHECK: target: __gxx_personality_v1
# CHECK: - type: unwind-cfi
-# CHECK: content: [ 28, 00, 00, 00, 20, 00, 00, 00, {{..}}, {{..}}, {{..}}, {{..}},
-# CHECK: {{..}}, {{..}}, {{..}}, {{..}}, 04, 00, 00, 00, 00, 00, 00, 00,
-# CHECK: 08, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, 0E, 10, 9E,
+# CHECK: content: [ 28, 00, 00, 00, 20, 00, 00, 00, {{..}}, {{..}}, {{..}}, {{..}},
+# CHECK: {{..}}, {{..}}, {{..}}, {{..}}, 04, 00, 00, 00, 00, 00, 00, 00,
+# CHECK: 08, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, {{..}}, 0E, 10, 9E,
# CHECK: 01, 9D, 02, 00, 00, 00, 00, 00 ]
# CHECK: alignment: 4 mod 8
-# CHECK: references:
+# CHECK: references:
# CHECK: - kind: negDelta32
# CHECK: offset: 4
# CHECK: target: L001
diff --git a/test/mach-o/exe-offsets.yaml b/test/mach-o/exe-offsets.yaml
index a751507432ee..6a0e35cedb46 100644
--- a/test/mach-o/exe-offsets.yaml
+++ b/test/mach-o/exe-offsets.yaml
@@ -1,4 +1,4 @@
-# RUN: lld -flavor darwin -arch x86_64 %s -o %t -e start %p/Inputs/libSystem.yaml
+# RUN: lld -flavor darwin -arch x86_64 %s -o %t -e start %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-readobj -sections %t | FileCheck %s
# Make sure data gets put at offset
diff --git a/test/mach-o/exe-segment-overlap.yaml b/test/mach-o/exe-segment-overlap.yaml
index a416ee3ca73b..47f0214efd6c 100644
--- a/test/mach-o/exe-segment-overlap.yaml
+++ b/test/mach-o/exe-segment-overlap.yaml
@@ -1,4 +1,4 @@
-# RUN: lld -flavor darwin -arch x86_64 %s -o %t %p/Inputs/libSystem.yaml
+# RUN: lld -flavor darwin -arch x86_64 %s -o %t %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-readobj -sections -section-data %t | FileCheck %s
--- !native
diff --git a/test/mach-o/executable-exports.yaml b/test/mach-o/executable-exports.yaml
index b14e2d229296..0b8717eb724e 100644
--- a/test/mach-o/executable-exports.yaml
+++ b/test/mach-o/executable-exports.yaml
@@ -1,5 +1,5 @@
# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 \
-# RUN: %s %p/Inputs/libSystem.yaml -o %t && \
+# RUN: %s %p/Inputs/x86_64/libSystem.yaml -o %t && \
# RUN: llvm-objdump -exports-trie %t | FileCheck %s
#
#
diff --git a/test/mach-o/export-trie-order.yaml b/test/mach-o/export-trie-order.yaml
new file mode 100644
index 000000000000..a11c998bdb06
--- /dev/null
+++ b/test/mach-o/export-trie-order.yaml
@@ -0,0 +1,62 @@
+# RUN: lld -flavor darwin -arch i386 %s %p/Inputs/hello-world-x86.yaml -o %t
+# RUN: llvm-objdump -exports-trie %t | FileCheck %s
+#
+# Test that the export trie is emitted in order.
+#
+
+--- !mach-o
+arch: x86
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x89, 0xE5, 0x83, 0xEC, 0x08, 0xE8, 0x00,
+ 0x00, 0x00, 0x00, 0x58, 0x8D, 0x80, 0x16, 0x00,
+ 0x00, 0x00, 0x89, 0x04, 0x24, 0xE8, 0xE6, 0xFF,
+ 0xFF, 0xFF, 0x31, 0xC0, 0x83, 0xC4, 0x08, 0x5D,
+ 0xC3 ]
+ relocations:
+ - offset: 0x00000016
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x0000000E
+ scattered: true
+ type: GENERIC_RELOC_LOCAL_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000021
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x0000000B
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x0000000000000021
+ content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _printf
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK: Exports trie:
+# CHECK-NEXT: __mh_execute_header
+# CHECK-NEXT: _main
diff --git a/test/mach-o/exported_symbols_list-dylib.yaml b/test/mach-o/exported_symbols_list-dylib.yaml
index f52614038ca4..f9de5fe976e2 100644
--- a/test/mach-o/exported_symbols_list-dylib.yaml
+++ b/test/mach-o/exported_symbols_list-dylib.yaml
@@ -1,20 +1,20 @@
# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 -dylib \
-# RUN: %s %p/Inputs/libSystem.yaml -o %t \
+# RUN: %s %p/Inputs/x86_64/libSystem.yaml -o %t \
# RUN: -exported_symbols_list %p/Inputs/exported_symbols_list.exp && \
# RUN: llvm-nm -m %t | FileCheck %s
#
# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 -dylib \
-# RUN: %s %p/Inputs/libSystem.yaml -o %t2 \
+# RUN: %s %p/Inputs/x86_64/libSystem.yaml -o %t2 \
# RUN: -exported_symbol _foo -exported_symbol _b && \
# RUN: llvm-nm -m %t2 | FileCheck %s
#
# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 -dylib \
-# RUN: %s %p/Inputs/libSystem.yaml -o %t3 \
+# RUN: %s %p/Inputs/x86_64/libSystem.yaml -o %t3 \
# RUN: -unexported_symbol _bar -unexported_symbol _a && \
# RUN: llvm-nm -m %t3 | FileCheck %s
#
# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 -dylib \
-# RUN: %s %p/Inputs/libSystem.yaml -dead_strip -o %t \
+# RUN: %s %p/Inputs/x86_64/libSystem.yaml -dead_strip -o %t \
# RUN: -exported_symbols_list %p/Inputs/exported_symbols_list.exp && \
# RUN: llvm-nm -m %t | FileCheck -check-prefix=CHECK_DEAD %s
#
diff --git a/test/mach-o/exported_symbols_list-undef.yaml b/test/mach-o/exported_symbols_list-undef.yaml
index 1e2fc821c365..377282f2a7c4 100644
--- a/test/mach-o/exported_symbols_list-undef.yaml
+++ b/test/mach-o/exported_symbols_list-undef.yaml
@@ -1,5 +1,5 @@
# RUN: not lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 -dylib \
-# RUN: %s %p/Inputs/libSystem.yaml -o %t -exported_symbol _foobar 2> %t2
+# RUN: %s %p/Inputs/x86_64/libSystem.yaml -o %t -exported_symbol _foobar 2> %t2
#
# Test -exported_symbol fails if exported symbol not found.
#
diff --git a/test/mach-o/fat-archive.yaml b/test/mach-o/fat-archive.yaml
index 86828d6a12b7..979ede30a72a 100644
--- a/test/mach-o/fat-archive.yaml
+++ b/test/mach-o/fat-archive.yaml
@@ -1,5 +1,5 @@
# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t \
-# RUN: -L %p/Inputs -lfoo %p/Inputs/libSystem.yaml
+# RUN: -L %p/Inputs -lfoo %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-nm -m -n %t | FileCheck %s
#
# Test that fat archives are handled.
diff --git a/test/mach-o/flat_namespace_undef_error.yaml b/test/mach-o/flat_namespace_undef_error.yaml
index 904b9c776e59..af84608aa5ff 100644
--- a/test/mach-o/flat_namespace_undef_error.yaml
+++ b/test/mach-o/flat_namespace_undef_error.yaml
@@ -1,4 +1,4 @@
-# RUN: not lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 -flat_namespace -undefined error %s -o %t %p/Inputs/libSystem.yaml 2>&1 | FileCheck %s
+# RUN: not lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 -flat_namespace -undefined error %s -o %t %p/Inputs/x86_64/libSystem.yaml 2>&1 | FileCheck %s
--- !native
defined-atoms:
diff --git a/test/mach-o/flat_namespace_undef_suppress.yaml b/test/mach-o/flat_namespace_undef_suppress.yaml
index 5152a1cd985c..e68fd998c1b2 100644
--- a/test/mach-o/flat_namespace_undef_suppress.yaml
+++ b/test/mach-o/flat_namespace_undef_suppress.yaml
@@ -1,4 +1,4 @@
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 -flat_namespace -undefined suppress %s -o %t %p/Inputs/libSystem.yaml
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 -flat_namespace -undefined suppress %s -o %t %p/Inputs/x86_64/libSystem.yaml
#
# Sanity check '-flat_namespace -undefined suppress'.
# This should pass without error, even though '_bar' is undefined.
diff --git a/test/mach-o/force_load-dylib.yaml b/test/mach-o/force_load-dylib.yaml
index c8f559bedd79..d32c63eab5c1 100644
--- a/test/mach-o/force_load-dylib.yaml
+++ b/test/mach-o/force_load-dylib.yaml
@@ -1,7 +1,7 @@
# RUN: lld -flavor darwin -arch x86_64 -dylib %p/Inputs/bar.yaml \
-# RUN: -install_name /usr/lib/libbar.dylib %p/Inputs/libSystem.yaml -o %t1.dylib
+# RUN: -install_name /usr/lib/libbar.dylib %p/Inputs/x86_64/libSystem.yaml -o %t1.dylib
# RUN: lld -flavor darwin -arch x86_64 -dylib %s -all_load %t1.dylib \
-# RUN: -install_name /usr/lib/libfoo.dylib %p/Inputs/libSystem.yaml -o %t
+# RUN: -install_name /usr/lib/libfoo.dylib %p/Inputs/x86_64/libSystem.yaml -o %t
# RUN: llvm-nm -m %t | FileCheck %s
#
#
diff --git a/test/mach-o/force_load-x86_64.yaml b/test/mach-o/force_load-x86_64.yaml
index 1be216ca3a74..5b37f4764e68 100644
--- a/test/mach-o/force_load-x86_64.yaml
+++ b/test/mach-o/force_load-x86_64.yaml
@@ -1,8 +1,8 @@
-# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/libSystem.yaml \
+# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/x86_64/libSystem.yaml \
# RUN: %p/Inputs/libfoo.a %p/Inputs/libbar.a -o %t1
# RUN: llvm-nm -m -n %t1 | FileCheck %s
#
-# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/libSystem.yaml \
+# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/x86_64/libSystem.yaml \
# RUN: -force_load %p/Inputs/libfoo.a %p/Inputs/libbar.a -o %t2
# RUN: llvm-nm -m -n %t2 | FileCheck --check-prefix=CHECKF %s
#
diff --git a/test/mach-o/function-starts-load-command.yaml b/test/mach-o/function-starts-load-command.yaml
index 9f251898f6e0..5cfe9dcac67d 100644
--- a/test/mach-o/function-starts-load-command.yaml
+++ b/test/mach-o/function-starts-load-command.yaml
@@ -1,8 +1,8 @@
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml && llvm-objdump -private-headers %t | FileCheck %s
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml -static -function_starts && llvm-objdump -private-headers %t | FileCheck %s
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml -no_function_starts && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_FUNCTION_STARTS
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml -static -function_starts -no_function_starts && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_FUNCTION_STARTS
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml -static && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_FUNCTION_STARTS
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml && llvm-objdump -private-headers %t | FileCheck %s
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static -function_starts && llvm-objdump -private-headers %t | FileCheck %s
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -no_function_starts && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_FUNCTION_STARTS
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static -function_starts -no_function_starts && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_FUNCTION_STARTS
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_FUNCTION_STARTS
--- !mach-o
arch: x86_64
diff --git a/test/mach-o/gcc_except_tab-got-arm64.yaml b/test/mach-o/gcc_except_tab-got-arm64.yaml
index 7d105ec17847..47b174d6cf29 100644
--- a/test/mach-o/gcc_except_tab-got-arm64.yaml
+++ b/test/mach-o/gcc_except_tab-got-arm64.yaml
@@ -1,16 +1,16 @@
# RUN: lld -flavor darwin -arch arm64 %s \
-# RUN: -dylib %p/Inputs/libSystem.yaml -o %t
+# RUN: -dylib %p/Inputs/arm64/libSystem.yaml -o %t
# RUN: llvm-objdump -section-headers %t | FileCheck %s
# Make sure that the GOT relocation from gcc_except_tab to the data
# is not removed.
--- !native
-defined-atoms:
+defined-atoms:
- name: _main
scope: global
- content: [ FD, 7B, BF, A9, FD, 03, 00, 91, FF, 43, 00, D1,
- BF, C3, 1F, B8, 00, 00, 00, 94, BF, 03, 00, 91,
+ content: [ FD, 7B, BF, A9, FD, 03, 00, 91, FF, 43, 00, D1,
+ BF, C3, 1F, B8, 00, 00, 00, 94, BF, 03, 00, 91,
FD, 7B, C1, A8, C0, 03, 5F, D6 ]
alignment: 4
- name: __ZTSP1A
@@ -20,25 +20,25 @@ defined-atoms:
merge: as-weak
- name: GCC_except_table0
type: unwind-lsda
- content: [ FF, 9B, E7, 80, 00, 03, 5B, 00, 00, 00, 00, 1C,
- 00, 00, 00, 00, 00, 00, 00, 00, 1C, 00, 00, 00,
- 18, 00, 00, 00, 84, 00, 00, 00, 03, 40, 00, 00,
- 00, 10, 00, 00, 00, 94, 00, 00, 00, 03, 60, 00,
- 00, 00, 20, 00, 00, 00, B4, 00, 00, 00, 05, 80,
- 00, 00, 00, 68, 00, 00, 00, 00, 00, 00, 00, 00,
- E8, 00, 00, 00, 08, 00, 00, 00, 28, 01, 00, 00,
- 00, F0, 00, 00, 00, 74, 00, 00, 00, 00, 00, 00,
+ content: [ FF, 9B, E7, 80, 00, 03, 5B, 00, 00, 00, 00, 1C,
+ 00, 00, 00, 00, 00, 00, 00, 00, 1C, 00, 00, 00,
+ 18, 00, 00, 00, 84, 00, 00, 00, 03, 40, 00, 00,
+ 00, 10, 00, 00, 00, 94, 00, 00, 00, 03, 60, 00,
+ 00, 00, 20, 00, 00, 00, B4, 00, 00, 00, 05, 80,
+ 00, 00, 00, 68, 00, 00, 00, 00, 00, 00, 00, 00,
+ E8, 00, 00, 00, 08, 00, 00, 00, 28, 01, 00, 00,
+ 00, F0, 00, 00, 00, 74, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 01, 7D, 01, 00, A8, FF, FF, FF ]
alignment: 4
- references:
+ references:
- kind: delta32ToGOT
offset: 104
target: __ZTIP1A
- name: __ZTIP1A
scope: hidden
type: data
- content: [ 10, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
- 00, 00, 00, 80, 00, 00, 00, 00, 00, 00, 00, 00,
+ content: [ 10, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
+ 00, 00, 00, 80, 00, 00, 00, 00, 00, 00, 00, 00,
00, 00, 00, 00, 00, 00, 00, 00 ]
merge: as-weak
alignment: 16
diff --git a/test/mach-o/got-order.yaml b/test/mach-o/got-order.yaml
index b69877ecdc39..2e8579cad6d2 100644
--- a/test/mach-o/got-order.yaml
+++ b/test/mach-o/got-order.yaml
@@ -1,5 +1,5 @@
# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/got-order.yaml \
-# RUN: %p/Inputs/got-order2.yaml -o %t %p/Inputs/libSystem.yaml
+# RUN: %p/Inputs/got-order2.yaml -o %t %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-objdump -bind %t | FileCheck %s
#
# Test that GOT slots are sorted by name
diff --git a/test/mach-o/hello-world-arm64.yaml b/test/mach-o/hello-world-arm64.yaml
index 75ac4f747795..138af5940997 100644
--- a/test/mach-o/hello-world-arm64.yaml
+++ b/test/mach-o/hello-world-arm64.yaml
@@ -1,5 +1,6 @@
# RUN: lld -flavor darwin -arch arm64 %s %p/Inputs/hello-world-arm64.yaml -o %t
# RUN: llvm-nm -m -n %t | FileCheck %s
+# RUN: llvm-objdump -private-headers %t | FileCheck %s --check-prefix=CHECK-PRIVATE-HEADER
#
# Test that arm64 hello-world can be linked into a mach-o executable
#
@@ -92,3 +93,10 @@ undefined-symbols:
# CHECK: (undefined) external _fprintf (from libSystem)
# CHECK: (undefined) external dyld_stub_binder (from libSystem)
# CHECK: {{[0-9a-f]+}} (__TEXT,__text) external _main
+
+# CHECK-PRIVATE-HEADER: sectname __stubs
+# CHECK-PRIVATE-HEADER-NEXT: segname __TEXT
+# CHECK-PRIVATE-HEADER-NEXT: addr
+# CHECK-PRIVATE-HEADER-NEXT: size
+# CHECK-PRIVATE-HEADER-NEXT: offset
+# CHECK-PRIVATE-HEADER-NEXT: align 2^1 (2)
diff --git a/test/mach-o/image-base.yaml b/test/mach-o/image-base.yaml
index d274621f8dd3..aa78fea1c338 100644
--- a/test/mach-o/image-base.yaml
+++ b/test/mach-o/image-base.yaml
@@ -1,4 +1,4 @@
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 %s -o %t -image_base 31415926000 %p/Inputs/libSystem.yaml
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 %s -o %t -image_base 31415926000 %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-readobj -macho-segment %t | FileCheck %s
# RUN: not lld -flavor darwin -arch x86_64 -image_base 0x31415926530 %s >/dev/null 2> %t
# RUN: FileCheck < %t %s --check-prefix=CHECK-ERROR-MISPAGED
diff --git a/test/mach-o/interposing-section.yaml b/test/mach-o/interposing-section.yaml
index 4f6bafc200fb..ec4eaa3f70ad 100644
--- a/test/mach-o/interposing-section.yaml
+++ b/test/mach-o/interposing-section.yaml
@@ -1,5 +1,5 @@
# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/interposing-section.yaml \
-# RUN: -dylib -o %t %p/Inputs/libSystem.yaml
+# RUN: -dylib -o %t %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-objdump -private-headers %t | FileCheck %s
#
# RUN: lld -flavor darwin -arch x86_64 %s -r -o %t1
diff --git a/test/mach-o/lazy-bind-x86_64.yaml b/test/mach-o/lazy-bind-x86_64.yaml
index ee3e2278b15b..5c588c571952 100644
--- a/test/mach-o/lazy-bind-x86_64.yaml
+++ b/test/mach-o/lazy-bind-x86_64.yaml
@@ -3,7 +3,7 @@
# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s \
# RUN: %p/Inputs/lazy-bind-x86_64.yaml %p/Inputs/lazy-bind-x86_64-2.yaml \
# RUN: %p/Inputs/lazy-bind-x86_64-3.yaml -o %t \
-# RUN: %p/Inputs/libSystem.yaml
+# RUN: %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-objdump -lazy-bind %t | FileCheck %s
# RUN: llvm-nm -m %t | FileCheck --check-prefix=CHECK-NM %s
# RUN: llvm-objdump -disassemble %t | FileCheck --check-prefix=CHECK-HELPERS %s
diff --git a/test/mach-o/lc_segment_filesize.yaml b/test/mach-o/lc_segment_filesize.yaml
new file mode 100644
index 000000000000..4413c7698767
--- /dev/null
+++ b/test/mach-o/lc_segment_filesize.yaml
@@ -0,0 +1,31 @@
+# RUN: lld -flavor darwin -arch x86_64 -r -o %t %s && llvm-objdump -private-headers %t | FileCheck %s
+
+# CHECK: filesize 19
+
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS ]
+ alignment: 16
+ address: 0x0000000000000000
+ content: [ 0x00, 0x00, 0x00 ]
+ - segment: __TEXT
+ section: __alt
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS ]
+ alignment: 16
+ address: 0x0000000000000010
+ content: [ 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+page-size: 0x00000000
+...
diff --git a/test/mach-o/library-order.yaml b/test/mach-o/library-order.yaml
index f9cd5a6c44d2..b53232dd398f 100644
--- a/test/mach-o/library-order.yaml
+++ b/test/mach-o/library-order.yaml
@@ -1,5 +1,5 @@
# RUN: lld -flavor darwin -arch x86_64 %p/Inputs/libfoo.a %s -o %t \
-# RUN: %p/Inputs/libSystem.yaml
+# RUN: %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-nm -m -n %t | FileCheck %s
#
# Test that if library is before object file on command line, it still is used.
diff --git a/test/mach-o/library-rescan.yaml b/test/mach-o/library-rescan.yaml
index a8a0ca8968b3..99c7b88c7e34 100644
--- a/test/mach-o/library-rescan.yaml
+++ b/test/mach-o/library-rescan.yaml
@@ -1,5 +1,5 @@
# RUN: lld -flavor darwin -arch x86_64 %p/Inputs/libfoo.a %p/Inputs/libbar.a \
-# RUN: %s -o %t %p/Inputs/libSystem.yaml
+# RUN: %s -o %t %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-nm -m -n %t | FileCheck %s
#
# Test that static libraries are automatically rescanned (bar needs foo).
diff --git a/test/mach-o/linker-as-ld.yaml b/test/mach-o/linker-as-ld.yaml
index f0bf189876a6..21afdb2ea02e 100644
--- a/test/mach-o/linker-as-ld.yaml
+++ b/test/mach-o/linker-as-ld.yaml
@@ -1,7 +1,7 @@
# REQUIRES: system-linker-mach-o
#
-# RUN: mkdir -p %t.dir && cp `which lld` %t.dir/ld \
-# RUN: && %t.dir/ld -arch x86_64 -macosx_version_min 10.8 %s \
+# RUN: rm -rf %T/ld && ln -s `which lld` %T/ld \
+# RUN: && %T/ld -arch x86_64 -macosx_version_min 10.8 %s \
# RUN: %p/Inputs/linker-as-ld.yaml -o %t \
# RUN: && llvm-nm %t | FileCheck %s
#
diff --git a/test/mach-o/lit.local.cfg b/test/mach-o/lit.local.cfg
index 739a0994fdda..ccbf4e12fbf0 100644
--- a/test/mach-o/lit.local.cfg
+++ b/test/mach-o/lit.local.cfg
@@ -1,4 +1,4 @@
# mach-o test cases encode input files in yaml and use .yaml extension
-config.suffixes = ['.yaml']
+config.suffixes = ['.yaml']
config.excludes = ['Inputs']
diff --git a/test/mach-o/mh_bundle_header.yaml b/test/mach-o/mh_bundle_header.yaml
index e440141c5689..d1b7d9ab2143 100644
--- a/test/mach-o/mh_bundle_header.yaml
+++ b/test/mach-o/mh_bundle_header.yaml
@@ -1,5 +1,5 @@
-# RUN: lld -flavor darwin -arch x86_64 %s -bundle -o %t %p/Inputs/libSystem.yaml && llvm-nm -m -n %t | FileCheck %s
-# RUN: lld -flavor darwin -arch x86_64 %s -bundle -dead_strip -o %t %p/Inputs/libSystem.yaml && llvm-nm -m -n %t | FileCheck %s
+# RUN: lld -flavor darwin -arch x86_64 %s -bundle -o %t %p/Inputs/x86_64/libSystem.yaml && llvm-nm -m -n %t | FileCheck %s
+# RUN: lld -flavor darwin -arch x86_64 %s -bundle -dead_strip -o %t %p/Inputs/x86_64/libSystem.yaml && llvm-nm -m -n %t | FileCheck %s
#
# Test that __mh_bundle_header symbol is available for bundles
#
diff --git a/test/mach-o/mh_dylib_header.yaml b/test/mach-o/mh_dylib_header.yaml
index 96b67aacae21..8222063ee161 100644
--- a/test/mach-o/mh_dylib_header.yaml
+++ b/test/mach-o/mh_dylib_header.yaml
@@ -1,4 +1,4 @@
-# RUN: lld -flavor darwin -arch x86_64 %s -dylib -o %t %p/Inputs/libSystem.yaml
+# RUN: lld -flavor darwin -arch x86_64 %s -dylib -o %t %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-nm -m -n %t | FileCheck %s
#
# Test that __mh_dylib_header symbol is available for dylibs
diff --git a/test/mach-o/objc-category-list-atom.yaml b/test/mach-o/objc-category-list-atom.yaml
index e06117124190..93974a646e06 100644
--- a/test/mach-o/objc-category-list-atom.yaml
+++ b/test/mach-o/objc-category-list-atom.yaml
@@ -17,9 +17,9 @@ sections:
attributes: [ S_ATTR_NO_DEAD_STRIP ]
alignment: 8
address: 0x00000000000003F8
- content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
- relocations:
+ relocations:
- offset: 0x00000008
type: X86_64_RELOC_UNSIGNED
length: 3
@@ -32,7 +32,7 @@ sections:
pc-rel: false
extern: true
symbol: 1
-undefined-symbols:
+undefined-symbols:
- name: __category1
type: N_UNDF
scope: [ N_EXT ]
@@ -47,12 +47,12 @@ page-size: 0x00000000
# Make sure we atomize the category list section by pointer sized atoms.
# CHECK: path: '<linker-internal>'
-# CHECK: defined-atoms:
+# CHECK: defined-atoms:
# CHECK: - type: objc-category-list
# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ]
# CHECK: merge: by-content
# CHECK: alignment: 8
-# CHECK: references:
+# CHECK: references:
# CHECK: - kind: pointer64
# CHECK: offset: 0
# CHECK: target: __category2
@@ -60,11 +60,11 @@ page-size: 0x00000000
# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00 ]
# CHECK: merge: by-content
# CHECK: alignment: 8
-# CHECK: references:
+# CHECK: references:
# CHECK: - kind: pointer64
# CHECK: offset: 0
# CHECK: target: __category1
-# CHECK: undefined-atoms:
+# CHECK: undefined-atoms:
# CHECK: - name: __category1
# CHECK: - name: __category2
# CHECK: ...
diff --git a/test/mach-o/objc_export_list.yaml b/test/mach-o/objc_export_list.yaml
index e67a4c7ece75..a2fcfa22ec07 100644
--- a/test/mach-o/objc_export_list.yaml
+++ b/test/mach-o/objc_export_list.yaml
@@ -1,5 +1,5 @@
# RUN: lld -flavor darwin -arch x86_64 -dylib %s -o %t \
-# RUN: -exported_symbol .objc_class_name_Foo %p/Inputs/libSystem.yaml
+# RUN: -exported_symbol .objc_class_name_Foo %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-nm -m %t | FileCheck %s
#
# Test that exported objc classes can be specificed using old naming
diff --git a/test/mach-o/order_file-basic.yaml b/test/mach-o/order_file-basic.yaml
index 3fea9be15601..f4d29fa17012 100644
--- a/test/mach-o/order_file-basic.yaml
+++ b/test/mach-o/order_file-basic.yaml
@@ -1,4 +1,4 @@
-# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/libSystem.yaml \
+# RUN: lld -flavor darwin -arch x86_64 %s %p/Inputs/x86_64/libSystem.yaml \
# RUN: -order_file %p/Inputs/order_file-basic.order \
# RUN: -force_load %p/Inputs/libfoo.a -o %t
# RUN: llvm-nm -m -n %t | FileCheck %s
diff --git a/test/mach-o/parse-data-in-code-armv7.yaml b/test/mach-o/parse-data-in-code-armv7.yaml
index 720f24d7b47f..163cb18fdc5f 100644
--- a/test/mach-o/parse-data-in-code-armv7.yaml
+++ b/test/mach-o/parse-data-in-code-armv7.yaml
@@ -1,6 +1,6 @@
# RUN: lld -flavor darwin -arch armv7 -r -print_atoms %s -o %t | FileCheck %s
# RUN: lld -flavor darwin -arch armv7 -r -print_atoms %t -o %t2 | FileCheck %s
-# RUN: lld -flavor darwin -arch armv7 -dylib %s -o %t3.dylib %p/Inputs/libSystem.yaml \
+# RUN: lld -flavor darwin -arch armv7 -dylib %s -o %t3.dylib %p/Inputs/armv7/libSystem.yaml \
# RUN: && llvm-objdump -macho -private-headers %t3.dylib | FileCheck --check-prefix=CHECK2 %s
#
# Test parsing LC_DATA_IN_CODE
diff --git a/test/mach-o/parse-data-relocs-x86_64.yaml b/test/mach-o/parse-data-relocs-x86_64.yaml
index 72a256ef1f02..6b5bb4b8fb59 100644
--- a/test/mach-o/parse-data-relocs-x86_64.yaml
+++ b/test/mach-o/parse-data-relocs-x86_64.yaml
@@ -60,7 +60,7 @@ sections:
type: S_REGULAR
attributes: [ ]
address: 0x000000000000000A
- content: [
+ content: [
# .quad _foo
# No addend is needed here as we are referencing _foo directly and that is
# encoded entirely in the X86_64_RELOC_UNSIGNED
@@ -301,7 +301,7 @@ page-size: 0x00000000
...
-# CHECK:defined-atoms:
+# CHECK:defined-atoms:
# CHECK: - name: _d
# CHECK: type: data
# CHECK: content: [ 00, 00, 00, 00, 00, 00, 00, 00, 04, 00, 00, 00,
@@ -313,7 +313,7 @@ page-size: 0x00000000
# CHECK: B8, FF, FF, FF, B4, FF, FF, FF, B4, FF, FF, FF,
# CHECK: {{..}}, {{..}}, {{..}}, {{..}} ]
# CHECK: dead-strip: never
-# CHECK: references:
+# CHECK: references:
# CHECK: - kind: pointer64
# CHECK: offset: 0
# CHECK: target: _foo
diff --git a/test/mach-o/rpath.yaml b/test/mach-o/rpath.yaml
index ce1234d4c78f..639149381293 100644
--- a/test/mach-o/rpath.yaml
+++ b/test/mach-o/rpath.yaml
@@ -1,6 +1,6 @@
# Check we handle -rpath correctly:
# RUN: lld -flavor darwin -arch x86_64 -rpath @loader_path/../Frameworks \
-# RUN: %p/Inputs/libSystem.yaml %s -o %t
+# RUN: %p/Inputs/x86_64/libSystem.yaml %s -o %t
# RUN: llvm-objdump -private-headers %t | FileCheck %s --check-prefix=CHECK-BINARY-WRITE
--- !mach-o
diff --git a/test/mach-o/run-tlv-pass-x86-64.yaml b/test/mach-o/run-tlv-pass-x86-64.yaml
index 0e648458645b..dd524c067d87 100644
--- a/test/mach-o/run-tlv-pass-x86-64.yaml
+++ b/test/mach-o/run-tlv-pass-x86-64.yaml
@@ -13,17 +13,17 @@ compat-version: 0.0
current-version: 0.0
has-UUID: false
OS: unknown
-sections:
+sections:
- segment: __TEXT
section: __text
type: S_REGULAR
attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
alignment: 16
address: 0x0000000000000000
- content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x3D, 0x00,
- 0x00, 0x00, 0x00, 0xFF, 0x17, 0x8B, 0x00, 0x5D,
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x3D, 0x00,
+ 0x00, 0x00, 0x00, 0xFF, 0x17, 0x8B, 0x00, 0x5D,
0xC3 ]
- relocations:
+ relocations:
- offset: 0x00000007
type: X86_64_RELOC_TLV
length: 2
@@ -42,10 +42,10 @@ sections:
type: S_THREAD_LOCAL_VARIABLES
attributes: [ ]
address: 0x0000000000000018
- content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
- relocations:
+ relocations:
- offset: 0x00000010
type: X86_64_RELOC_UNSIGNED
length: 3
@@ -67,12 +67,12 @@ sections:
content: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]
-local-symbols:
+local-symbols:
- name: '_x$tlv$init'
type: N_SECT
sect: 2
value: 0x0000000000000014
-global-symbols:
+global-symbols:
- name: _main
type: N_SECT
scope: [ N_EXT ]
diff --git a/test/mach-o/sectalign.yaml b/test/mach-o/sectalign.yaml
index 556fd52c96e1..f0df9f9c5485 100644
--- a/test/mach-o/sectalign.yaml
+++ b/test/mach-o/sectalign.yaml
@@ -1,6 +1,6 @@
# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -dylib \
# RUN: -sectalign __DATA __custom 0x800 -sectalign __TEXT __text 0x400 \
-# RUN: %p/Inputs/libSystem.yaml -o %t \
+# RUN: %p/Inputs/x86_64/libSystem.yaml -o %t \
# RUN: && llvm-readobj -sections %t | FileCheck %s
#
# Test -sectalign option on __text and a custom section.
diff --git a/test/mach-o/sectattrs.yaml b/test/mach-o/sectattrs.yaml
index 800ba55d1b3b..21113dcf8280 100644
--- a/test/mach-o/sectattrs.yaml
+++ b/test/mach-o/sectattrs.yaml
@@ -1,5 +1,5 @@
# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -dylib \
-# RUN: %p/Inputs/libSystem.yaml -o %t \
+# RUN: %p/Inputs/x86_64/libSystem.yaml -o %t \
# RUN: && llvm-objdump -private-headers %t | FileCheck %s
#
diff --git a/test/mach-o/source-version.yaml b/test/mach-o/source-version.yaml
index 169e97571357..4e0eaee394f1 100644
--- a/test/mach-o/source-version.yaml
+++ b/test/mach-o/source-version.yaml
@@ -1,5 +1,5 @@
# RUN: not lld -flavor darwin -arch x86_64 -source_version 10.blah %s -o %t 2>&1 | FileCheck %s --check-prefix=ERROR
-# RUN: lld -flavor darwin -arch x86_64 -source_version 10.1.2.3.4 %s -o %t -dylib %p/Inputs/libSystem.yaml && llvm-objdump -private-headers %t | FileCheck %s
+# RUN: lld -flavor darwin -arch x86_64 -source_version 10.1.2.3.4 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml && llvm-objdump -private-headers %t | FileCheck %s
--- !mach-o
arch: x86_64
diff --git a/test/mach-o/stack-size.yaml b/test/mach-o/stack-size.yaml
index 0e8edb2b5498..048282c504ef 100644
--- a/test/mach-o/stack-size.yaml
+++ b/test/mach-o/stack-size.yaml
@@ -1,6 +1,6 @@
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 %s -o %t %p/Inputs/libSystem.yaml
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 %s -o %t %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-objdump -private-headers %t | FileCheck --check-prefix=CHECK-DEFAULT %s
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 %s -o %t -stack_size 31415926000 %p/Inputs/libSystem.yaml
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 %s -o %t -stack_size 31415926000 %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-objdump -private-headers %t | FileCheck --check-prefix=CHECK-EXPLICIT %s
# RUN: not lld -flavor darwin -arch x86_64 -stack_size 0x31415926530 %s >/dev/null 2> %t
# RUN: FileCheck < %t %s --check-prefix=CHECK-ERROR-MISPAGED
diff --git a/test/mach-o/string-table.yaml b/test/mach-o/string-table.yaml
new file mode 100644
index 000000000000..eec2c77fe157
--- /dev/null
+++ b/test/mach-o/string-table.yaml
@@ -0,0 +1,66 @@
+# RUN: lld -flavor darwin -arch i386 %s %p/Inputs/hello-world-x86.yaml -o %t
+# RUN: obj2yaml %t | FileCheck %s
+#
+# Test that the string table contains a ' ' as its first symbol
+#
+
+--- !mach-o
+arch: x86
+file-type: MH_OBJECT
+flags: [ MH_SUBSECTIONS_VIA_SYMBOLS ]
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS, S_ATTR_SOME_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x89, 0xE5, 0x83, 0xEC, 0x08, 0xE8, 0x00,
+ 0x00, 0x00, 0x00, 0x58, 0x8D, 0x80, 0x16, 0x00,
+ 0x00, 0x00, 0x89, 0x04, 0x24, 0xE8, 0xE6, 0xFF,
+ 0xFF, 0xFF, 0x31, 0xC0, 0x83, 0xC4, 0x08, 0x5D,
+ 0xC3 ]
+ relocations:
+ - offset: 0x00000016
+ type: GENERIC_RELOC_VANILLA
+ length: 2
+ pc-rel: true
+ extern: true
+ symbol: 1
+ - offset: 0x0000000E
+ scattered: true
+ type: GENERIC_RELOC_LOCAL_SECTDIFF
+ length: 2
+ pc-rel: false
+ value: 0x00000021
+ - offset: 0x00000000
+ scattered: true
+ type: GENERIC_RELOC_PAIR
+ length: 2
+ pc-rel: false
+ value: 0x0000000B
+ - segment: __TEXT
+ section: __cstring
+ type: S_CSTRING_LITERALS
+ attributes: [ ]
+ address: 0x0000000000000021
+ content: [ 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0A, 0x00 ]
+global-symbols:
+ - name: _main
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+undefined-symbols:
+ - name: _printf
+ type: N_UNDF
+ scope: [ N_EXT ]
+ value: 0x0000000000000000
+...
+
+# CHECK: StringTable:
+# CHECK-NEXT: - ' '
+# CHECK-NEXT: - __mh_execute_header
+# CHECK-NEXT: - _main
+# CHECK-NEXT: - _printf
+# CHECK-NEXT: - dyld_stub_binder
+# CHECK-NEXT: - ''
diff --git a/test/mach-o/twolevel_namespace_undef_dynamic_lookup.yaml b/test/mach-o/twolevel_namespace_undef_dynamic_lookup.yaml
index a39a3e7f8c1b..b402ae3e17f2 100644
--- a/test/mach-o/twolevel_namespace_undef_dynamic_lookup.yaml
+++ b/test/mach-o/twolevel_namespace_undef_dynamic_lookup.yaml
@@ -1,4 +1,4 @@
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 -twolevel_namespace -undefined dynamic_lookup %s -o %t %p/Inputs/libSystem.yaml
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 -twolevel_namespace -undefined dynamic_lookup %s -o %t %p/Inputs/x86_64/libSystem.yaml
#
# Sanity check '-twolevel_namespace -undefined dynamic_lookup'.
# This should pass without error, even though '_bar' is undefined.
diff --git a/test/mach-o/twolevel_namespace_undef_warning_suppress.yaml b/test/mach-o/twolevel_namespace_undef_warning_suppress.yaml
index eeb01de5f707..1ac704cdf954 100644
--- a/test/mach-o/twolevel_namespace_undef_warning_suppress.yaml
+++ b/test/mach-o/twolevel_namespace_undef_warning_suppress.yaml
@@ -1,6 +1,6 @@
-# RUN: not lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 -twolevel_namespace -undefined warning %s -o %t %p/Inputs/libSystem.yaml 2>&1 | \
+# RUN: not lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 -twolevel_namespace -undefined warning %s -o %t %p/Inputs/x86_64/libSystem.yaml 2>&1 | \
# RUN: FileCheck --check-prefix=CHECK-WARNING %s
-# RUN: not lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 -twolevel_namespace -undefined suppress %s -o %t %p/Inputs/libSystem.yaml 2>&1 | \
+# RUN: not lld -flavor darwin -arch x86_64 -macosx_version_min 10.9 -twolevel_namespace -undefined suppress %s -o %t %p/Inputs/x86_64/libSystem.yaml 2>&1 | \
# RUN: FileCheck --check-prefix=CHECK-SUPPRESS %s
--- !native
diff --git a/test/mach-o/unwind-info-simple-arm64.yaml b/test/mach-o/unwind-info-simple-arm64.yaml
index 2ef6dda8f3af..4caaf3582630 100644
--- a/test/mach-o/unwind-info-simple-arm64.yaml
+++ b/test/mach-o/unwind-info-simple-arm64.yaml
@@ -1,5 +1,5 @@
# RUN: lld -flavor darwin -arch arm64 -o %t %s \
-# RUN: %p/Inputs/unwind-info-simple-arm64.yaml -e _main %p/Inputs/libSystem.yaml
+# RUN: %p/Inputs/unwind-info-simple-arm64.yaml -e _main %p/Inputs/arm64/libSystem.yaml
# RUN: llvm-objdump -unwind-info %t | FileCheck %s
--- !mach-o
diff --git a/test/mach-o/unwind-info-simple-x86_64.yaml b/test/mach-o/unwind-info-simple-x86_64.yaml
index 7dfae1b71581..797c5a319d2c 100644
--- a/test/mach-o/unwind-info-simple-x86_64.yaml
+++ b/test/mach-o/unwind-info-simple-x86_64.yaml
@@ -1,4 +1,4 @@
-# RUN: lld -flavor darwin -arch x86_64 %s -o %t -e _main %p/Inputs/libSystem.yaml
+# RUN: lld -flavor darwin -arch x86_64 %s -o %t -e _main %p/Inputs/x86_64/libSystem.yaml
# RUN: llvm-objdump -unwind-info %t | FileCheck %s
# CHECK: Contents of __unwind_info section:
diff --git a/test/mach-o/upward-dylib-load-command.yaml b/test/mach-o/upward-dylib-load-command.yaml
index 1383ad75b0a6..54e31f6960be 100644
--- a/test/mach-o/upward-dylib-load-command.yaml
+++ b/test/mach-o/upward-dylib-load-command.yaml
@@ -1,7 +1,7 @@
# RUN: lld -flavor darwin -arch x86_64 -dylib %p/Inputs/bar.yaml \
-# RUN: -install_name /usr/lib/libbar.dylib %p/Inputs/libSystem.yaml -o %t1.dylib
+# RUN: -install_name /usr/lib/libbar.dylib %p/Inputs/x86_64/libSystem.yaml -o %t1.dylib
# RUN: lld -flavor darwin -arch x86_64 -dylib %s -upward_library %t1.dylib \
-# RUN: -install_name /usr/lib/libfoo.dylib %p/Inputs/libSystem.yaml -o %t
+# RUN: -install_name /usr/lib/libfoo.dylib %p/Inputs/x86_64/libSystem.yaml -o %t
# RUN: llvm-objdump -private-headers %t | FileCheck %s
#
#
diff --git a/test/mach-o/use-dylib.yaml b/test/mach-o/use-dylib.yaml
new file mode 100644
index 000000000000..c173cc0af352
--- /dev/null
+++ b/test/mach-o/use-dylib.yaml
@@ -0,0 +1,39 @@
+# RUN: lld -flavor darwin -arch x86_64 %s \
+# RUN: %p/Inputs/use-simple-dylib.yaml %p/Inputs/x86_64/libSystem.yaml -dylib -o %t.dylib
+# RUN: llvm-objdump -private-headers %t.dylib | FileCheck %s
+
+# This test ensures that we have a LC_LOAD_DYLIB for libspecial.dylib even though we don't
+# use any atoms from it. This matches the ld64 behaviour.
+--- !mach-o
+arch: x86_64
+file-type: MH_OBJECT
+flags: [ ]
+has-UUID: false
+OS: unknown
+sections:
+ - segment: __TEXT
+ section: __text
+ type: S_REGULAR
+ attributes: [ S_ATTR_PURE_INSTRUCTIONS ]
+ address: 0x0000000000000000
+ content: [ 0x55, 0x48, 0x89, 0xE5, 0xE8, 0x00, 0x00, 0x00,
+ 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xE8, 0x00,
+ 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00,
+ 0xE8, 0x00, 0x00, 0x00, 0x00, 0x5D, 0xE9, 0x00,
+ 0x00, 0x00, 0x00 ]
+global-symbols:
+ - name: _foo
+ type: N_SECT
+ scope: [ N_EXT ]
+ sect: 1
+ value: 0x0000000000000000
+
+
+# CHECK: cmd LC_LOAD_DYLIB
+# CHECK: name libspecial.dylib (offset 24)
+# CHECK: current version 1.0.0
+# CHECK: compatibility version 1.0.0
+# CHECK: cmd LC_LOAD_DYLIB
+# CHECK: name /usr/lib/libSystem.B.dylib (offset 24)
+# CHECK: current version 1.0.0
+# CHECK: compatibility version 1.0.0
diff --git a/test/mach-o/version-min-load-command.yaml b/test/mach-o/version-min-load-command.yaml
index aa378a71fcf4..cb5331ec7f4b 100644
--- a/test/mach-o/version-min-load-command.yaml
+++ b/test/mach-o/version-min-load-command.yaml
@@ -1,11 +1,11 @@
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml && llvm-objdump -private-headers %t | FileCheck %s
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml 2>&1 | FileCheck %s --check-prefix=WARNING
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml -static -version_load_command && llvm-objdump -private-headers %t | FileCheck %s
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml -no_version_load_command && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_VERSION_MIN
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml -static -version_load_command -no_version_load_command && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_VERSION_MIN
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/libSystem.yaml -static && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_VERSION_MIN
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml && llvm-objdump -private-headers %t | FileCheck %s
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml 2>&1 | FileCheck %s --check-prefix=WARNING
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static -version_load_command && llvm-objdump -private-headers %t | FileCheck %s
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -no_version_load_command && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_VERSION_MIN
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static -version_load_command -no_version_load_command && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_VERSION_MIN
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml -static && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=NO_VERSION_MIN
-# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 -sdk_version 10.9 %s -o %t -dylib %p/Inputs/libSystem.yaml && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=SDK_VERSION
+# RUN: lld -flavor darwin -arch x86_64 -macosx_version_min 10.8 -sdk_version 10.9 %s -o %t -dylib %p/Inputs/x86_64/libSystem.yaml && llvm-objdump -private-headers %t | FileCheck %s --check-prefix=SDK_VERSION
--- !mach-o
arch: x86_64
diff --git a/tools/lld/CMakeLists.txt b/tools/lld/CMakeLists.txt
index 5951da6496c5..0c946a3f8ce0 100644
--- a/tools/lld/CMakeLists.txt
+++ b/tools/lld/CMakeLists.txt
@@ -1,4 +1,4 @@
-add_llvm_executable(lld
+add_lld_tool(lld
lld.cpp
)
@@ -17,8 +17,5 @@ if(NOT LLD_SYMLINKS_TO_CREATE)
endif()
foreach(link ${LLD_SYMLINKS_TO_CREATE})
- add_llvm_tool_symlink(${link} lld ALWAYS_GENERATE)
- # Always generate install targets
- llvm_install_symlink(${link} lld ALWAYS_GENERATE)
+ add_lld_symlink(${link} lld)
endforeach()
-
diff --git a/tools/lld/lld.cpp b/tools/lld/lld.cpp
index 8805e02e8c48..0c32ff192992 100644
--- a/tools/lld/lld.cpp
+++ b/tools/lld/lld.cpp
@@ -43,9 +43,7 @@ LLVM_ATTRIBUTE_NORETURN static void die(const Twine &S) {
static Flavor getFlavor(StringRef S) {
return StringSwitch<Flavor>(S)
- .Case("ld", Gnu)
- .Case("ld.lld", Gnu)
- .Case("gnu", Gnu)
+ .Cases("ld", "ld.lld", "gnu", Gnu)
.Case("link", WinLink)
.Case("darwin", Darwin)
.Default(Invalid);
@@ -103,7 +101,7 @@ int main(int Argc, const char **Argv) {
std::vector<const char *> Args(Argv, Argv + Argc);
switch (parseFlavor(Args)) {
case Gnu:
- return !elf::link(Args);
+ return !elf::link(Args, true);
case WinLink:
return !coff::link(Args);
case Darwin:
diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt
index bb651b5cfe62..9cd085398c37 100644
--- a/unittests/CMakeLists.txt
+++ b/unittests/CMakeLists.txt
@@ -1,6 +1,8 @@
add_custom_target(LLDUnitTests)
set_target_properties(LLDUnitTests PROPERTIES FOLDER "lld tests")
+set(CMAKE_BUILD_WITH_INSTALL_RPATH OFF)
+
# add_lld_unittest(test_dirname file1.cpp file2.cpp)
#
# Will compile the list of files together and link against lld
diff --git a/unittests/MachOTests/MachONormalizedFileBinaryWriterTests.cpp b/unittests/MachOTests/MachONormalizedFileBinaryWriterTests.cpp
index f44950a8d5ab..51196e6eeba6 100644
--- a/unittests/MachOTests/MachONormalizedFileBinaryWriterTests.cpp
+++ b/unittests/MachOTests/MachONormalizedFileBinaryWriterTests.cpp
@@ -219,6 +219,7 @@ TEST(BinaryWriterTest, obj_relocs_x86_64) {
EXPECT_EQ(signed4.isExtern, true);
EXPECT_EQ(signed4.symbol, 1U);
+ bufferOwner.reset(nullptr);
std::error_code ec = llvm::sys::fs::remove(Twine(tmpFl));
EXPECT_FALSE(ec);
}
@@ -329,6 +330,7 @@ TEST(BinaryWriterTest, obj_relocs_x86) {
EXPECT_EQ(tlv.symbol, 1U);
//llvm::errs() << "temp = " << tmpFl << "\n";
+ bufferOwner.reset(nullptr);
std::error_code ec = llvm::sys::fs::remove(Twine(tmpFl));
EXPECT_FALSE(ec);
}
@@ -458,6 +460,7 @@ TEST(BinaryWriterTest, obj_relocs_armv7) {
EXPECT_EQ(absPointer.symbol, 2U);
//llvm::errs() << "temp = " << tmpFl << "\n";
+ bufferOwner.reset(nullptr);
std::error_code ec = llvm::sys::fs::remove(Twine(tmpFl));
EXPECT_FALSE(ec);
}
@@ -687,6 +690,7 @@ TEST(BinaryWriterTest, obj_relocs_ppc) {
EXPECT_EQ(absloa2.length, 2);
EXPECT_EQ(absloa2.symbol, 0U);
+ bufferOwner.reset(nullptr);
std::error_code ec = llvm::sys::fs::remove(Twine(tmpFl));
EXPECT_FALSE(ec);
}