aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYi Kong <yikong@google.com>2017-11-13 16:15:47 -0800
committerYi Kong <yikong@google.com>2017-11-13 16:15:47 -0800
commita38dd38df4bafa4723031ea79543e01a68a3ebbb (patch)
treee91948028ed865d9cbaeb61f4706263b4e44e86f
parentfc51a9828c2bece1d3cec27b0b3edbcf3f35467b (diff)
parentc9e633dcb3997ab998bcedcf11d4e36d5af44a6c (diff)
downloadlld-llvm-r316199.tar.gz
Merge c9e633dcb for LLVM update to 316199llvm-r316199
Change-Id: Ia98560b5d3a898fddb952da4f918778a9f9f4db0
-rw-r--r--CMakeLists.txt6
-rw-r--r--COFF/CMakeLists.txt11
-rw-r--r--COFF/Chunks.cpp188
-rw-r--r--COFF/Chunks.h70
-rw-r--r--COFF/Config.h18
-rw-r--r--COFF/DLL.cpp65
-rw-r--r--COFF/Driver.cpp357
-rw-r--r--COFF/Driver.h37
-rw-r--r--COFF/DriverUtils.cpp311
-rw-r--r--COFF/Error.cpp5
-rw-r--r--COFF/Error.h3
-rw-r--r--COFF/ICF.cpp18
-rw-r--r--COFF/InputFiles.cpp77
-rw-r--r--COFF/InputFiles.h14
-rw-r--r--COFF/LTO.cpp48
-rw-r--r--COFF/LTO.h3
-rw-r--r--COFF/MapFile.cpp4
-rw-r--r--COFF/MarkLive.cpp7
-rw-r--r--COFF/MinGW.cpp119
-rw-r--r--COFF/MinGW.h38
-rw-r--r--COFF/Options.td11
-rw-r--r--COFF/PDB.cpp870
-rw-r--r--COFF/PDB.h6
-rw-r--r--COFF/Strings.cpp5
-rw-r--r--COFF/SymbolTable.cpp72
-rw-r--r--COFF/SymbolTable.h23
-rw-r--r--COFF/Symbols.cpp20
-rw-r--r--COFF/Symbols.h96
-rw-r--r--COFF/Writer.cpp262
-rw-r--r--COFF/Writer.h4
-rw-r--r--Common/CMakeLists.txt26
-rw-r--r--Common/Reproduce.cpp (renamed from lib/Core/Reproduce.cpp)8
-rw-r--r--Common/TargetOptionsCommandFlags.cpp (renamed from lib/Core/TargetOptionsCommandFlags.cpp)6
-rw-r--r--Common/Threads.cpp32
-rw-r--r--Common/Version.cpp (renamed from lib/Config/Version.cpp)4
-rw-r--r--ELF/Arch/AArch64.cpp54
-rw-r--r--ELF/Arch/AMDGPU.cpp13
-rw-r--r--ELF/Arch/ARM.cpp78
-rw-r--r--ELF/Arch/AVR.cpp16
-rw-r--r--ELF/Arch/Mips.cpp410
-rw-r--r--ELF/Arch/MipsArchTree.cpp (renamed from ELF/Mips.cpp)28
-rw-r--r--ELF/Arch/PPC.cpp30
-rw-r--r--ELF/Arch/PPC64.cpp14
-rw-r--r--ELF/Arch/SPARCV9.cpp148
-rw-r--r--ELF/Arch/X86.cpp126
-rw-r--r--ELF/Arch/X86_64.cpp45
-rw-r--r--ELF/CMakeLists.txt17
-rw-r--r--ELF/Config.h30
-rw-r--r--ELF/Driver.cpp269
-rw-r--r--ELF/Driver.h6
-rw-r--r--ELF/DriverUtils.cpp20
-rw-r--r--ELF/EhFrame.cpp3
-rw-r--r--ELF/EhFrame.h6
-rw-r--r--ELF/Error.cpp11
-rw-r--r--ELF/Error.h3
-rw-r--r--ELF/Filesystem.cpp9
-rw-r--r--ELF/Filesystem.h6
-rw-r--r--ELF/GdbIndex.cpp80
-rw-r--r--ELF/GdbIndex.h88
-rw-r--r--ELF/ICF.cpp82
-rw-r--r--ELF/ICF.h2
-rw-r--r--ELF/InputFiles.cpp265
-rw-r--r--ELF/InputFiles.h77
-rw-r--r--ELF/InputSection.cpp244
-rw-r--r--ELF/InputSection.h118
-rw-r--r--ELF/LTO.cpp66
-rw-r--r--ELF/LTO.h10
-rw-r--r--ELF/LinkerScript.cpp1167
-rw-r--r--ELF/LinkerScript.h194
-rw-r--r--ELF/MapFile.cpp89
-rw-r--r--ELF/MapFile.h9
-rw-r--r--ELF/MarkLive.cpp141
-rw-r--r--ELF/Memory.h4
-rw-r--r--ELF/Options.td158
-rw-r--r--ELF/OutputSections.cpp593
-rw-r--r--ELF/OutputSections.h81
-rw-r--r--ELF/Relocations.cpp573
-rw-r--r--ELF/Relocations.h34
-rw-r--r--ELF/ScriptLexer.cpp42
-rw-r--r--ELF/ScriptLexer.h3
-rw-r--r--ELF/ScriptParser.cpp274
-rw-r--r--ELF/ScriptParser.h2
-rw-r--r--ELF/Strings.cpp34
-rw-r--r--ELF/Strings.h9
-rw-r--r--ELF/SymbolTable.cpp502
-rw-r--r--ELF/SymbolTable.h68
-rw-r--r--ELF/Symbols.cpp138
-rw-r--r--ELF/Symbols.h119
-rw-r--r--ELF/SyntheticSections.cpp801
-rw-r--r--ELF/SyntheticSections.h165
-rw-r--r--ELF/Target.cpp33
-rw-r--r--ELF/Target.h83
-rw-r--r--ELF/Thunks.cpp142
-rw-r--r--ELF/Thunks.h8
-rw-r--r--ELF/Writer.cpp973
-rw-r--r--ELF/Writer.h16
-rw-r--r--MinGW/CMakeLists.txt22
-rw-r--r--MinGW/Driver.cpp221
-rw-r--r--MinGW/Options.td47
-rw-r--r--README.md10
-rw-r--r--docs/Driver.rst2
-rw-r--r--docs/NewLLD.rst2
-rw-r--r--docs/ReleaseNotes.rst6
-rw-r--r--docs/conf.py4
-rw-r--r--docs/index.rst39
-rw-r--r--docs/windows_support.rst5
-rw-r--r--include/lld/Common/Driver.h (renamed from include/lld/Driver/Driver.h)11
-rw-r--r--include/lld/Common/LLVM.h (renamed from include/lld/Core/LLVM.h)4
-rw-r--r--include/lld/Common/Reproduce.h (renamed from include/lld/Core/Reproduce.h)6
-rw-r--r--include/lld/Common/TargetOptionsCommandFlags.h (renamed from include/lld/Core/TargetOptionsCommandFlags.h)3
-rw-r--r--include/lld/Common/Threads.h (renamed from ELF/Threads.h)29
-rw-r--r--include/lld/Common/Version.h (renamed from include/lld/Config/Version.h)4
-rw-r--r--include/lld/Common/Version.inc.in (renamed from include/lld/Config/Version.inc.in)0
-rw-r--r--include/lld/Core/Atom.h2
-rw-r--r--include/lld/Core/DefinedAtom.h2
-rw-r--r--include/lld/Core/Error.h2
-rw-r--r--include/lld/Core/PassManager.h2
-rw-r--r--include/lld/Core/Reader.h2
-rw-r--r--include/lld/Core/SymbolTable.h2
-rw-r--r--include/lld/Core/Writer.h2
-rw-r--r--include/lld/ReaderWriter/YamlContext.h2
-rw-r--r--lib/CMakeLists.txt1
-rw-r--r--lib/Config/CMakeLists.txt9
-rw-r--r--lib/Core/CMakeLists.txt2
-rw-r--r--lib/Core/Resolver.cpp6
-rw-r--r--lib/Core/SymbolTable.cpp2
-rw-r--r--lib/Driver/CMakeLists.txt4
-rw-r--r--lib/Driver/DarwinLdDriver.cpp22
-rw-r--r--lib/ReaderWriter/CMakeLists.txt1
-rw-r--r--lib/ReaderWriter/FileArchive.cpp2
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler.h6
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_arm.cpp4
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_arm64.cpp4
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_x86.cpp4
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp4
-rw-r--r--lib/ReaderWriter/MachO/CMakeLists.txt2
-rw-r--r--lib/ReaderWriter/MachO/CompactUnwindPass.cpp2
-rw-r--r--lib/ReaderWriter/MachO/GOTPass.cpp2
-rw-r--r--lib/ReaderWriter/MachO/MachOLinkingContext.cpp2
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFile.h2
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp13
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h2
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp2
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp59
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp6
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp3
-rw-r--r--lib/ReaderWriter/MachO/ObjCPass.cpp10
-rw-r--r--lib/ReaderWriter/MachO/ShimPass.cpp2
-rw-r--r--lib/ReaderWriter/MachO/StubsPass.cpp2
-rw-r--r--test/CMakeLists.txt15
-rw-r--r--test/COFF/Inputs/alpha.ll9
-rw-r--r--test/COFF/Inputs/beta.ll7
-rw-r--r--test/COFF/Inputs/combined-resources-2.rc36
-rw-r--r--test/COFF/Inputs/combined-resources-2.resbin0 -> 452 bytes
-rw-r--r--test/COFF/Inputs/combined-resources-cursor.bmpbin0 -> 822 bytes
-rw-r--r--test/COFF/Inputs/combined-resources-okay.bmpbin0 -> 822 bytes
-rw-r--r--test/COFF/Inputs/combined-resources.rc50
-rw-r--r--test/COFF/Inputs/combined-resources.resbin0 -> 2332 bytes
-rw-r--r--test/COFF/Inputs/default.def2
-rw-r--r--test/COFF/Inputs/extension.def3
-rw-r--r--test/COFF/Inputs/gamma.ll14
-rw-r--r--test/COFF/Inputs/library-arm64.libbin0 -> 2000 bytes
-rw-r--r--test/COFF/Inputs/library2-arm64.libbin0 -> 1720 bytes
-rw-r--r--test/COFF/Inputs/library2.def3
-rw-r--r--test/COFF/Inputs/lto-cache.ll10
-rw-r--r--test/COFF/Inputs/named.def3
-rw-r--r--test/COFF/Inputs/object.s13
-rw-r--r--test/COFF/Inputs/pdb-diff-cl.pdbbin0 -> 102400 bytes
-rw-r--r--test/COFF/Inputs/pdb-diff.cpp10
-rw-r--r--test/COFF/Inputs/pdb-diff.objbin0 -> 8602 bytes
-rw-r--r--test/COFF/Inputs/pdb-global-gc.s4
-rw-r--r--test/COFF/Inputs/pdb-globals.yaml593
-rw-r--r--test/COFF/Inputs/pdb-import-gc.libbin0 -> 1614 bytes
-rw-r--r--test/COFF/Inputs/pdb-scopes-a.yaml425
-rw-r--r--test/COFF/Inputs/pdb-scopes-b.yaml365
-rw-r--r--test/COFF/Inputs/pdb-type-server-simple-a.yaml255
-rw-r--r--test/COFF/Inputs/pdb-type-server-simple-b.yaml173
-rw-r--r--test/COFF/Inputs/pdb-type-server-simple-ts.yaml147
-rw-r--r--test/COFF/Inputs/pdb1.yaml4
-rw-r--r--test/COFF/Inputs/pdb_comdat_bar.yaml440
-rw-r--r--test/COFF/Inputs/pdb_comdat_main.yaml446
-rw-r--r--test/COFF/Inputs/pdb_lines_1.yaml480
-rw-r--r--test/COFF/Inputs/pdb_lines_2.yaml209
-rw-r--r--test/COFF/arm64-import2.test85
-rw-r--r--test/COFF/arm64-magic.yaml46
-rw-r--r--test/COFF/arm64-relocs-imports.test238
-rw-r--r--test/COFF/combined-resources.test213
-rw-r--r--test/COFF/common-alignment.test78
-rw-r--r--test/COFF/common.test1
-rw-r--r--test/COFF/conflict.test1
-rw-r--r--test/COFF/constant.test1
-rw-r--r--test/COFF/def-export-stdcall.s4
-rw-r--r--test/COFF/delayimports-armnt.yaml106
-rw-r--r--test/COFF/delayimports32.test1
-rw-r--r--test/COFF/dllexport-mingw.s19
-rw-r--r--test/COFF/duplicate.test12
-rw-r--r--test/COFF/entry-inference.test8
-rw-r--r--test/COFF/entry-mangled.test1
-rw-r--r--test/COFF/entrylib.ll1
-rw-r--r--test/COFF/export-all.s56
-rw-r--r--test/COFF/export-arm64.yaml70
-rw-r--r--test/COFF/export-armnt.yaml72
-rw-r--r--test/COFF/export.test5
-rw-r--r--test/COFF/force.test7
-rw-r--r--test/COFF/guardcf.test74
-rw-r--r--test/COFF/hello32.test6
-rw-r--r--test/COFF/icf-associative.test8
-rw-r--r--test/COFF/implib-name.test71
-rw-r--r--test/COFF/imports.test1
-rw-r--r--test/COFF/include-lto.ll1
-rw-r--r--test/COFF/linkrepro-res.test12
-rw-r--r--test/COFF/lto-cache.ll21
-rw-r--r--test/COFF/lto-reloc-model.ll19
-rw-r--r--test/COFF/manifest.test23
-rw-r--r--test/COFF/manifestinput-error.test10
-rw-r--r--test/COFF/manifestinput-nowarning.test11
-rw-r--r--test/COFF/manifestinput.test24
-rw-r--r--test/COFF/msvclto-archive.ll1
-rw-r--r--test/COFF/msvclto-order.ll1
-rw-r--r--test/COFF/msvclto.ll1
-rw-r--r--test/COFF/nodefaultlib.test6
-rw-r--r--test/COFF/options.test8
-rw-r--r--test/COFF/pdb-comdat.test109
-rw-r--r--test/COFF/pdb-diff.test215
-rw-r--r--test/COFF/pdb-global-gc.yaml120
-rw-r--r--test/COFF/pdb-globals.test42
-rw-r--r--test/COFF/pdb-import-gc.yaml118
-rw-r--r--test/COFF/pdb-invalid-func-type.yaml146
-rw-r--r--test/COFF/pdb-lib.s12
-rw-r--r--test/COFF/pdb-linker-module.test24
-rw-r--r--test/COFF/pdb-none.test7
-rw-r--r--test/COFF/pdb-procid-remapping.test29
-rw-r--r--test/COFF/pdb-publics-import.test42
-rw-r--r--test/COFF/pdb-safeseh.yaml86
-rw-r--r--test/COFF/pdb-same-name.test23
-rw-r--r--test/COFF/pdb-scopes.test75
-rw-r--r--test/COFF/pdb-secrel-absolute.yaml85
-rw-r--r--test/COFF/pdb-source-lines.test125
-rw-r--r--test/COFF/pdb-symbol-types.yaml346
-rw-r--r--test/COFF/pdb-type-server-missing.yaml132
-rw-r--r--test/COFF/pdb-type-server-simple.test95
-rw-r--r--test/COFF/pdb.test178
-rw-r--r--test/COFF/reloc-discarded-dwarf.s17
-rw-r--r--test/COFF/reloc-discarded.s30
-rw-r--r--test/COFF/reloc-oob.yaml62
-rw-r--r--test/COFF/resource.test38
-rw-r--r--test/COFF/responsefile.test20
-rw-r--r--test/COFF/rsds.test40
-rw-r--r--test/COFF/safeseh-diag-feat.test (renamed from test/COFF/safeseh.test)0
-rw-r--r--test/COFF/safeseh.s60
-rw-r--r--test/COFF/savetemps.ll1
-rw-r--r--test/COFF/secidx-absolute.s33
-rw-r--r--test/COFF/secrel-absolute.s14
-rw-r--r--test/COFF/secrel-common.s41
-rw-r--r--test/COFF/section-size.s14
-rw-r--r--test/COFF/thinlto-archives.ll1
-rw-r--r--test/COFF/thinlto-mangled.ll1
-rw-r--r--test/COFF/thinlto.ll1
-rw-r--r--test/COFF/wholearchive.s19
-rw-r--r--test/ELF/Inputs/corrupt-version-reference.sobin0 -> 134272 bytes
-rw-r--r--test/ELF/Inputs/ctors_dtors_priority1.s4
-rw-r--r--test/ELF/Inputs/ctors_dtors_priority2.s4
-rw-r--r--test/ELF/Inputs/ctors_dtors_priority3.s4
-rw-r--r--test/ELF/Inputs/dso-undef-size.s4
-rw-r--r--test/ELF/Inputs/dynamic-list-weak-archive.s2
-rw-r--r--test/ELF/Inputs/eh-frame.s3
-rw-r--r--test/ELF/Inputs/exclude-libs.s3
-rw-r--r--test/ELF/Inputs/gdb-index-a.elfbin3040 -> 0 bytes
-rw-r--r--test/ELF/Inputs/gdb-index-b.elfbin3048 -> 0 bytes
-rw-r--r--test/ELF/Inputs/gdb-index.s73
-rw-r--r--test/ELF/Inputs/gnu-ifunc-dso.s3
-rw-r--r--test/ELF/Inputs/mips-micro.s12
-rw-r--r--test/ELF/Inputs/symver-archive1.s6
-rw-r--r--test/ELF/Inputs/symver-archive2.s1
-rw-r--r--test/ELF/Inputs/undefined-error.s1
-rw-r--r--test/ELF/Inputs/verdef-defaultver.s3
-rwxr-xr-xtest/ELF/Inputs/verneed.so.sh58
-rw-r--r--test/ELF/Inputs/verneed1.s32
-rwxr-xr-xtest/ELF/Inputs/verneed1.sobin2632 -> 0 bytes
-rw-r--r--test/ELF/Inputs/verneed2.s5
-rwxr-xr-xtest/ELF/Inputs/verneed2.sobin2200 -> 0 bytes
-rw-r--r--test/ELF/Inputs/version-script-no-warn2.s1
-rw-r--r--test/ELF/Inputs/version-script-weak.s4
-rw-r--r--test/ELF/Inputs/weak-undef-lazy.s3
-rw-r--r--test/ELF/Inputs/wrap-dynamic-undef.s2
-rw-r--r--test/ELF/Inputs/wrap-no-real.s3
-rw-r--r--test/ELF/Inputs/wrap-no-real2.s2
-rw-r--r--test/ELF/aarch64-gnu-ifunc-plt.s2
-rw-r--r--test/ELF/aarch64-gnu-ifunc.s2
-rw-r--r--test/ELF/aarch64-got-reloc.s4
-rw-r--r--test/ELF/aarch64-got-relocations.s2
-rw-r--r--test/ELF/aarch64-ldprel-lo19-invalid.s11
-rw-r--r--test/ELF/aarch64-lo12-alignment.s45
-rw-r--r--test/ELF/aarch64-load-alignment.s11
-rw-r--r--test/ELF/aarch64-tls-gdie.s2
-rw-r--r--test/ELF/aarch64-tls-ie.s2
-rw-r--r--test/ELF/aarch64-tls-static.s2
-rw-r--r--test/ELF/aarch64-tlsdesc.s2
-rw-r--r--test/ELF/aarch64-undefined-weak.s6
-rw-r--r--test/ELF/abs-hidden.s2
-rw-r--r--test/ELF/allow-multiple-definition.s5
-rw-r--r--test/ELF/allow-shlib-undefined.s1
-rw-r--r--test/ELF/amdgpu-relocs.s15
-rw-r--r--test/ELF/arm-copy.s2
-rw-r--r--test/ELF/arm-exidx-canunwind.s2
-rw-r--r--test/ELF/arm-exidx-gc.s5
-rw-r--r--test/ELF/arm-exidx-shared.s4
-rw-r--r--test/ELF/arm-gnu-ifunc-plt.s8
-rw-r--r--test/ELF/arm-got-relative.s18
-rw-r--r--test/ELF/arm-icf-exidx.s2
-rw-r--r--test/ELF/arm-mov-relocs.s9
-rw-r--r--test/ELF/arm-pie-relative.s2
-rw-r--r--test/ELF/arm-plt-reloc.s2
-rw-r--r--test/ELF/arm-thumb-branch.s2
-rw-r--r--test/ELF/arm-thumb-plt-reloc.s6
-rw-r--r--test/ELF/arm-thunk-edgecase.s37
-rw-r--r--test/ELF/arm-tls-gd-nonpreemptible.s2
-rw-r--r--test/ELF/arm-tls-gd32.s2
-rw-r--r--test/ELF/arm-tls-ie32.s2
-rw-r--r--test/ELF/arm-tls-ldm32.s2
-rw-r--r--test/ELF/arm-tls-norelax-gd-ie.s2
-rw-r--r--test/ELF/arm-tls-norelax-gd-le.s6
-rw-r--r--test/ELF/arm-tls-norelax-ie-le.s2
-rw-r--r--test/ELF/arm-tls-norelax-ld-le.s2
-rw-r--r--test/ELF/as-needed-no-reloc.s2
-rw-r--r--test/ELF/as-needed.s12
-rw-r--r--test/ELF/assignment-archive.s27
-rw-r--r--test/ELF/auxiliary.s1
-rw-r--r--test/ELF/avoid-empty-program-headers.s6
-rw-r--r--test/ELF/basic-aarch64.s14
-rw-r--r--test/ELF/basic-mips.s2
-rw-r--r--test/ELF/basic-ppc.s2
-rw-r--r--test/ELF/basic-sparcv9.s200
-rw-r--r--test/ELF/basic.s16
-rw-r--r--test/ELF/basic32.s14
-rw-r--r--test/ELF/build-id.s6
-rw-r--r--test/ELF/chroot.s12
-rw-r--r--test/ELF/comment-gc.s3
-rw-r--r--test/ELF/common-gc.s41
-rw-r--r--test/ELF/common-gc2.s15
-rw-r--r--test/ELF/common-gc3.s18
-rw-r--r--test/ELF/common.s10
-rw-r--r--test/ELF/compress-debug-sections.s7
-rw-r--r--test/ELF/compressed-debug-conflict.s29
-rw-r--r--test/ELF/compressed-debug-input.s16
-rw-r--r--test/ELF/copy-errors.s3
-rw-r--r--test/ELF/copy-in-shared.s2
-rw-r--r--test/ELF/copy-rel-pie.s2
-rw-r--r--test/ELF/corrupted-version-reference.s11
-rw-r--r--test/ELF/ctors_dtors_priority.s31
-rw-r--r--test/ELF/debug-gc.s4
-rw-r--r--test/ELF/debug-gnu-pubnames.s26
-rw-r--r--test/ELF/defsym.s2
-rw-r--r--test/ELF/dso-undef-size.s32
-rw-r--r--test/ELF/duplicated-synthetic-sym.s11
-rw-r--r--test/ELF/dynamic-got.s2
-rw-r--r--test/ELF/dynamic-list-empty.s18
-rw-r--r--test/ELF/dynamic-list-preempt.s76
-rw-r--r--test/ELF/dynamic-list-weak-archive.s18
-rw-r--r--test/ELF/dynamic-list.s22
-rw-r--r--test/ELF/dynamic-no-rosegment.s15
-rw-r--r--test/ELF/dynamic-reloc-in-ro.s6
-rw-r--r--test/ELF/dynamic-reloc.s4
-rw-r--r--test/ELF/dynstr-no-rosegment.s12
-rw-r--r--test/ELF/dynsym-no-rosegment.s27
-rw-r--r--test/ELF/dynsym-pie.s52
-rw-r--r--test/ELF/edata-etext.s2
-rw-r--r--test/ELF/eh-align-cie.s2
-rw-r--r--test/ELF/eh-frame-hdr-augmentation.s2
-rw-r--r--test/ELF/eh-frame-hdr.s12
-rw-r--r--test/ELF/eh-frame-merge.s2
-rw-r--r--test/ELF/eh-frame-padding-no-rosegment.s64
-rw-r--r--test/ELF/eh-frame.s12
-rw-r--r--test/ELF/emit-relocs-gc.s18
-rw-r--r--test/ELF/emit-relocs-shared.s2
-rw-r--r--test/ELF/exclude-libs.s30
-rw-r--r--test/ELF/executable-undefined-ignoreall.s13
-rw-r--r--test/ELF/executable-undefined-protected-ignoreall.s8
-rw-r--r--test/ELF/fill-trap.s25
-rw-r--r--test/ELF/filter.s19
-rw-r--r--test/ELF/format-binary-non-ascii.s15
-rw-r--r--test/ELF/gc-sections-shared.s11
-rw-r--r--test/ELF/gc-sections-undefined.s10
-rw-r--r--test/ELF/gdb-index-base-addr.s70
-rw-r--r--test/ELF/gdb-index-dup-types.s2
-rw-r--r--test/ELF/gdb-index-empty.s3
-rw-r--r--test/ELF/gdb-index-gc-sections.s3
-rw-r--r--test/ELF/gdb-index-ranges.s2
-rw-r--r--test/ELF/gdb-index-tls.s91
-rw-r--r--test/ELF/gdb-index.s131
-rw-r--r--test/ELF/global-offset-table-position-aarch64.s30
-rw-r--r--test/ELF/global-offset-table-position-arm.s35
-rw-r--r--test/ELF/global-offset-table-position-i386.s31
-rw-r--r--test/ELF/global-offset-table-position-mips.s33
-rw-r--r--test/ELF/global-offset-table-position.s31
-rw-r--r--test/ELF/global_offset_table_shared.s11
-rw-r--r--test/ELF/gnu-hash-table-copy.s30
-rw-r--r--test/ELF/gnu-hash-table.s75
-rw-r--r--test/ELF/gnu-ifunc-dso.s13
-rw-r--r--test/ELF/gnu-ifunc-dynsym.s19
-rw-r--r--test/ELF/gnu-ifunc-gotpcrel.s2
-rw-r--r--test/ELF/gnu-ifunc-i386.s2
-rw-r--r--test/ELF/gnu-ifunc-plt-i386.s2
-rw-r--r--test/ELF/gnu-ifunc-plt.s2
-rw-r--r--test/ELF/gnu-ifunc-shared.s2
-rw-r--r--test/ELF/gnu-ifunc.s2
-rw-r--r--test/ELF/got-aarch64.s2
-rw-r--r--test/ELF/got.s2
-rw-r--r--test/ELF/got32-i386.s2
-rw-r--r--test/ELF/got32x-i386.s4
-rw-r--r--test/ELF/gotpc-relax-nopic.s6
-rw-r--r--test/ELF/gotpc-relax-und-dso.s2
-rw-r--r--test/ELF/gotpcrelx.s2
-rw-r--r--test/ELF/help.s5
-rw-r--r--test/ELF/i386-got-and-copy.s2
-rw-r--r--test/ELF/i386-gotoff-shared.s2
-rw-r--r--test/ELF/i386-gotpc-dynamic.s2
-rw-r--r--test/ELF/i386-gotpc.s2
-rw-r--r--test/ELF/i386-reloc-large-addend.s1
-rw-r--r--test/ELF/i386-reloc-range.s1
-rw-r--r--test/ELF/i386-tls-ie-shared.s2
-rw-r--r--test/ELF/icf-non-mergeable.s2
-rw-r--r--test/ELF/icf-none.s22
-rw-r--r--test/ELF/icf-symbol-type.s20
-rw-r--r--test/ELF/image-base.s7
-rw-r--r--test/ELF/init_fini_priority.s24
-rw-r--r--test/ELF/invalid-z.s9
-rw-r--r--test/ELF/invalid/Inputs/invalid-relocation-x64.elfbin559 -> 0 bytes
-rw-r--r--test/ELF/invalid/invalid-debug-relocations.test40
-rw-r--r--test/ELF/invalid/invalid-relocation-x64.test27
-rw-r--r--test/ELF/invalid/tls-symbol.s4
-rw-r--r--test/ELF/libsearch.s1
-rw-r--r--test/ELF/linkerscript/Inputs/common-filespec1.s2
-rw-r--r--test/ELF/linkerscript/Inputs/common-filespec2.s2
-rw-r--r--test/ELF/linkerscript/Inputs/symbol-reserved.script5
-rw-r--r--test/ELF/linkerscript/absolute2.s17
-rw-r--r--test/ELF/linkerscript/align-section-offset.s11
-rw-r--r--test/ELF/linkerscript/align-section.s6
-rw-r--r--test/ELF/linkerscript/align.s37
-rw-r--r--test/ELF/linkerscript/arm-exidx-order.s19
-rw-r--r--test/ELF/linkerscript/at-addr.s4
-rw-r--r--test/ELF/linkerscript/at.s25
-rw-r--r--test/ELF/linkerscript/common-exclude.s86
-rw-r--r--test/ELF/linkerscript/common-filespec.s105
-rw-r--r--test/ELF/linkerscript/common.s8
-rw-r--r--test/ELF/linkerscript/compress-debug-sections.s4
-rw-r--r--test/ELF/linkerscript/data-commands-gc.s5
-rw-r--r--test/ELF/linkerscript/data-segment-relro.s4
-rw-r--r--test/ELF/linkerscript/diagnostic.s14
-rw-r--r--test/ELF/linkerscript/discard-section-err.s2
-rw-r--r--test/ELF/linkerscript/early-assign-symbol.s24
-rw-r--r--test/ELF/linkerscript/emit-reloc.s2
-rw-r--r--test/ELF/linkerscript/exidx-crash.s7
-rw-r--r--test/ELF/linkerscript/extend-pt-load.s6
-rw-r--r--test/ELF/linkerscript/filename-spec.s47
-rw-r--r--test/ELF/linkerscript/got-write-offset.s23
-rw-r--r--test/ELF/linkerscript/header-addr.s22
-rw-r--r--test/ELF/linkerscript/header-phdr.s13
-rw-r--r--test/ELF/linkerscript/image-base.s18
-rw-r--r--test/ELF/linkerscript/implicit-program-header.s2
-rw-r--r--test/ELF/linkerscript/include-cycle.s15
-rw-r--r--test/ELF/linkerscript/linkerscript.s2
-rw-r--r--test/ELF/linkerscript/locationcountererr2.s6
-rw-r--r--test/ELF/linkerscript/memory-at.s46
-rw-r--r--test/ELF/linkerscript/memory-err.s16
-rw-r--r--test/ELF/linkerscript/memory.s4
-rw-r--r--test/ELF/linkerscript/memory2.s14
-rw-r--r--test/ELF/linkerscript/no-space.s4
-rw-r--r--test/ELF/linkerscript/non-alloc-segment.s44
-rw-r--r--test/ELF/linkerscript/non-alloc.s2
-rw-r--r--test/ELF/linkerscript/operators.s2
-rw-r--r--test/ELF/linkerscript/orphan-end.s57
-rw-r--r--test/ELF/linkerscript/orphan-phdrs.s34
-rw-r--r--test/ELF/linkerscript/orphan-report.s34
-rw-r--r--test/ELF/linkerscript/out-of-order.s13
-rw-r--r--test/ELF/linkerscript/output-too-large.s1
-rw-r--r--test/ELF/linkerscript/phdr-check.s4
-rw-r--r--test/ELF/linkerscript/region-alias.s54
-rw-r--r--test/ELF/linkerscript/repsection-symbol.s2
-rw-r--r--test/ELF/linkerscript/sections-sort.s2
-rw-r--r--test/ELF/linkerscript/segment-headers.s26
-rw-r--r--test/ELF/linkerscript/segment-none.s39
-rw-r--r--test/ELF/linkerscript/segment-start.s2
-rw-r--r--test/ELF/linkerscript/sort-non-script.s2
-rw-r--r--test/ELF/linkerscript/symbol-assignexpr.s8
-rw-r--r--test/ELF/linkerscript/symbol-only-flags.s20
-rw-r--r--test/ELF/linkerscript/symbol-ordering-file.s23
-rw-r--r--test/ELF/linkerscript/symbol-reserved.s28
-rw-r--r--test/ELF/linkerscript/symbols.s13
-rw-r--r--test/ELF/linkerscript/ttext-script.s11
-rw-r--r--test/ELF/linkerscript/unused-synthetic.s18
-rw-r--r--test/ELF/lit.local.cfg1
-rw-r--r--test/ELF/local-got-pie.s2
-rw-r--r--test/ELF/local-got-shared.s2
-rw-r--r--test/ELF/local-got.s2
-rw-r--r--test/ELF/lto-plugin-ignore.s11
-rw-r--r--test/ELF/lto/Inputs/data-ordering-lto.ll6
-rw-r--r--test/ELF/lto/Inputs/linker-script-symbols-ipo.ll9
-rw-r--r--test/ELF/lto/Inputs/symbol-ordering-lto.ll10
-rw-r--r--test/ELF/lto/available-externally.ll1
-rw-r--r--test/ELF/lto/cache.ll11
-rw-r--r--test/ELF/lto/comdat2.ll1
-rw-r--r--test/ELF/lto/common2.ll1
-rw-r--r--test/ELF/lto/common3.ll1
-rw-r--r--test/ELF/lto/data-ordering-lto.s27
-rw-r--r--test/ELF/lto/defsym.ll14
-rw-r--r--test/ELF/lto/discard-value-names.ll1
-rw-r--r--test/ELF/lto/keep-undefined.ll20
-rw-r--r--test/ELF/lto/linker-script-symbols-assign.ll48
-rw-r--r--test/ELF/lto/linker-script-symbols-ipo.ll32
-rw-r--r--test/ELF/lto/linker-script-symbols.ll29
-rw-r--r--test/ELF/lto/opt-level.ll22
-rw-r--r--test/ELF/lto/opt-remarks.ll26
-rw-r--r--test/ELF/lto/relax-relocs.ll1
-rw-r--r--test/ELF/lto/relocatable.ll55
-rw-r--r--test/ELF/lto/save-temps.ll7
-rw-r--r--test/ELF/lto/section-name.ll35
-rw-r--r--test/ELF/lto/symbol-ordering-lto.s25
-rw-r--r--test/ELF/lto/thin-archivecollision.ll1
-rw-r--r--test/ELF/lto/thinlto.ll1
-rw-r--r--test/ELF/lto/type-merge2.ll1
-rw-r--r--test/ELF/lto/unnamed-addr-comdat.ll1
-rw-r--r--test/ELF/lto/unnamed-addr-drop.ll1
-rw-r--r--test/ELF/lto/unnamed-addr.ll1
-rw-r--r--test/ELF/lto/verify-invalid.ll2
-rw-r--r--test/ELF/lto/wrap-1.ll9
-rw-r--r--test/ELF/lto/wrap-2.ll18
-rw-r--r--test/ELF/many-alloc-sections.s4
-rw-r--r--test/ELF/many-sections.s8
-rw-r--r--test/ELF/map-file.s14
-rw-r--r--test/ELF/map-gc-sections.s9
-rw-r--r--test/ELF/merge-section-types.s1
-rw-r--r--test/ELF/merge-string.s8
-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-elf-flags.s29
-rw-r--r--test/ELF/mips-got-relocs.s4
-rw-r--r--test/ELF/mips-got16-relocatable.s8
-rw-r--r--test/ELF/mips-gp-disp.s2
-rw-r--r--test/ELF/mips-gp-ext.s7
-rw-r--r--test/ELF/mips-gp-local.s2
-rw-r--r--test/ELF/mips-gprel32-relocs-gp0.s2
-rw-r--r--test/ELF/mips-gprel32-relocs.s2
-rw-r--r--test/ELF/mips-hilo-gp-disp.s4
-rw-r--r--test/ELF/mips-hilo-hi-only.s2
-rw-r--r--test/ELF/mips-micro-got.s46
-rw-r--r--test/ELF/mips-micro-got64.s48
-rw-r--r--test/ELF/mips-micro-jal.s155
-rw-r--r--test/ELF/mips-micro-relocs.s61
-rw-r--r--test/ELF/mips-micro-thunks.s47
-rw-r--r--test/ELF/mips-n32-rels.s2
-rw-r--r--test/ELF/mips-npic-call-pic-os.s32
-rw-r--r--test/ELF/mips-npic-call-pic-script.s170
-rw-r--r--test/ELF/mips-npic-call-pic.s32
-rw-r--r--test/ELF/mips-out-of-bounds-call16-reloc.s29
-rw-r--r--test/ELF/mips-plt-r6.s4
-rw-r--r--test/ELF/new-dtags.test1
-rw-r--r--test/ELF/no-inhibit-exec.s4
-rw-r--r--test/ELF/no-obj.s1
-rw-r--r--test/ELF/no-soname.s9
-rw-r--r--test/ELF/no-symtab.s1
-rw-r--r--test/ELF/no-undefined.s1
-rw-r--r--test/ELF/non-abs-reloc.s2
-rw-r--r--test/ELF/noplt-pie.s2
-rw-r--r--test/ELF/pie-weak.s8
-rw-r--r--test/ELF/pr34660.s25
-rw-r--r--test/ELF/pr34872.s14
-rw-r--r--test/ELF/progname.s1
-rw-r--r--test/ELF/relative-dynamic-reloc-pie.s1
-rw-r--r--test/ELF/relative-dynamic-reloc.s1
-rw-r--r--test/ELF/relocatable-comdat2.s35
-rw-r--r--test/ELF/relocatable-compressed-input.s2
-rw-r--r--test/ELF/relocatable-reloc.s1
-rw-r--r--test/ELF/relocatable-script.s7
-rw-r--r--test/ELF/relocatable-section-symbol.s1
-rw-r--r--test/ELF/relocatable-sections.s1
-rw-r--r--test/ELF/relocatable-tls.s1
-rw-r--r--test/ELF/relocatable.s2
-rw-r--r--test/ELF/relocation-b-aarch64.test48
-rw-r--r--test/ELF/relocation-copy-alias.s4
-rw-r--r--test/ELF/relocation-copy-align-common.s2
-rw-r--r--test/ELF/relocation-copy-flags.s2
-rw-r--r--test/ELF/relocation-copy-relro.s2
-rw-r--r--test/ELF/relocation-i686.s2
-rw-r--r--test/ELF/relocation-relative-weak.s1
-rw-r--r--test/ELF/relocation-shared.s1
-rw-r--r--test/ELF/relocation.s2
-rw-r--r--test/ELF/relro-omagic.s2
-rw-r--r--test/ELF/reproduce-thin-archive.s6
-rw-r--r--test/ELF/reproduce.s19
-rw-r--r--test/ELF/resolution-end.s2
-rw-r--r--test/ELF/retain-symbols-file.s4
-rw-r--r--test/ELF/section-metadata-err.s2
-rw-r--r--test/ELF/shared-be.s2
-rw-r--r--test/ELF/shared-lazy.s16
-rw-r--r--test/ELF/shared.s8
-rw-r--r--test/ELF/soname.s2
-rw-r--r--test/ELF/soname2.s2
-rw-r--r--test/ELF/sort-norosegment.s2
-rw-r--r--test/ELF/startstop-gccollect.s12
-rw-r--r--test/ELF/startstop.s2
-rw-r--r--test/ELF/string-gc.s4
-rw-r--r--test/ELF/symbol-ordering-file2.s21
-rw-r--r--test/ELF/symver-archive.s15
-rw-r--r--test/ELF/synthetic-got.s4
-rw-r--r--test/ELF/sysroot.s2
-rw-r--r--test/ELF/tls-dynamic-i686.s2
-rw-r--r--test/ELF/tls-dynamic.s2
-rw-r--r--test/ELF/tls-got.s2
-rw-r--r--test/ELF/tls-i686.s2
-rw-r--r--test/ELF/tls-initial-exec-local.s2
-rw-r--r--test/ELF/tls-opt-gdie.s2
-rw-r--r--test/ELF/tls-opt-gdiele-i686.s2
-rw-r--r--test/ELF/tls-opt-iele-i686-nopic.s2
-rw-r--r--test/ELF/tls-static.s16
-rw-r--r--test/ELF/tls-two-relocs.s2
-rw-r--r--test/ELF/unresolved-symbols.s3
-rw-r--r--test/ELF/verdef-defaultver.s4
-rw-r--r--test/ELF/verdef.s6
-rw-r--r--test/ELF/verneed-as-needed-weak.s6
-rw-r--r--test/ELF/verneed-local.s6
-rw-r--r--test/ELF/verneed.s8
-rw-r--r--test/ELF/version-script-err.s1
-rw-r--r--test/ELF/version-script-extern.s2
-rw-r--r--test/ELF/version-script-no-warn2.s8
-rw-r--r--test/ELF/version-script-symver.s9
-rw-r--r--test/ELF/version-script-symver2.s28
-rw-r--r--test/ELF/version-script-twice.s18
-rw-r--r--test/ELF/version-script-undef-version.s12
-rw-r--r--test/ELF/version-script-weak.s28
-rw-r--r--test/ELF/version-script.s4
-rw-r--r--test/ELF/weak-entry.s13
-rw-r--r--test/ELF/weak-undef-export.s31
-rw-r--r--test/ELF/weak-undef-lazy.s11
-rw-r--r--test/ELF/weak-undef-val.s26
-rw-r--r--test/ELF/weak-undef.s9
-rw-r--r--test/ELF/wrap-dynamic-undef.s15
-rw-r--r--test/ELF/wrap-no-real.s31
-rw-r--r--test/ELF/wrap.s8
-rw-r--r--test/ELF/x86-64-dyn-rel-error.s2
-rw-r--r--test/ELF/x86-64-relax-got-abs.s2
-rw-r--r--test/ELF/x86-64-tls-gd-local.s2
-rw-r--r--test/MinGW/driver.test80
-rw-r--r--test/MinGW/lib.test21
-rw-r--r--test/Unit/lit.cfg.py (renamed from test/Unit/lit.cfg)18
-rw-r--r--test/Unit/lit.site.cfg.py.in (renamed from test/Unit/lit.site.cfg.in)4
-rw-r--r--test/lit.cfg267
-rw-r--r--test/lit.cfg.py84
-rw-r--r--test/lit.site.cfg.py.in (renamed from test/lit.site.cfg.in)5
-rw-r--r--test/mach-o/executable-exports.yaml4
-rw-r--r--test/mach-o/lazy-bind-x86_64.yaml4
-rw-r--r--tools/lld/CMakeLists.txt5
-rw-r--r--tools/lld/lld.cpp16
-rw-r--r--unittests/DriverTests/DarwinLdDriverTest.cpp2
654 files changed, 19924 insertions, 5543 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e2ab0e35f..b87b95a33 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -160,8 +160,8 @@ endif ()
# Configure the Version.inc file.
configure_file(
- ${CMAKE_CURRENT_SOURCE_DIR}/include/lld/Config/Version.inc.in
- ${CMAKE_CURRENT_BINARY_DIR}/include/lld/Config/Version.inc)
+ ${CMAKE_CURRENT_SOURCE_DIR}/include/lld/Common/Version.inc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/include/lld/Common/Version.inc)
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
@@ -210,6 +210,7 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
)
endif()
+add_subdirectory(Common)
add_subdirectory(lib)
add_subdirectory(tools/lld)
@@ -221,4 +222,5 @@ endif()
add_subdirectory(docs)
add_subdirectory(COFF)
add_subdirectory(ELF)
+add_subdirectory(MinGW)
diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt
index e56593497..503004272 100644
--- a/COFF/CMakeLists.txt
+++ b/COFF/CMakeLists.txt
@@ -17,6 +17,7 @@ add_lld_library(lldCOFF
LTO.cpp
MapFile.cpp
MarkLive.cpp
+ MinGW.cpp
PDB.cpp
Strings.cpp
SymbolTable.cpp
@@ -26,22 +27,20 @@ add_lld_library(lldCOFF
LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
BinaryFormat
- BitReader
Core
DebugInfoCodeView
DebugInfoMSF
DebugInfoPDB
- LTO
LibDriver
- Object
+ LTO
MC
- MCDisassembler
- Target
+ Object
Option
Support
+ WindowsManifest
LINK_LIBS
- lldCore
+ lldCommon
${LLVM_PTHREAD_LIB}
DEPENDS
diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp
index 1d5ae5571..0f0d622ea 100644
--- a/COFF/Chunks.cpp
+++ b/COFF/Chunks.cpp
@@ -11,6 +11,7 @@
#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
+#include "Writer.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/COFF.h"
#include "llvm/Object/COFF.h"
@@ -28,14 +29,14 @@ using llvm::support::ulittle32_t;
namespace lld {
namespace coff {
-SectionChunk::SectionChunk(ObjectFile *F, const coff_section *H)
+SectionChunk::SectionChunk(ObjFile *F, const coff_section *H)
: Chunk(SectionKind), Repl(this), Header(H), File(F),
Relocs(File->getCOFFObj()->getRelocations(Header)),
NumRelocs(std::distance(Relocs.begin(), Relocs.end())) {
// Initialize SectionName.
File->getCOFFObj()->getSectionName(Header, SectionName);
- Align = Header->getAlignment();
+ Alignment = Header->getAlignment();
// Chunks may be discarded during comdat merging.
Discarded = false;
@@ -51,10 +52,32 @@ static void add16(uint8_t *P, int16_t V) { write16le(P, read16le(P) + V); }
static void add32(uint8_t *P, int32_t V) { write32le(P, read32le(P) + V); }
static void add64(uint8_t *P, int64_t V) { write64le(P, read64le(P) + V); }
static void or16(uint8_t *P, uint16_t V) { write16le(P, read16le(P) | V); }
+static void or32(uint8_t *P, uint32_t V) { write32le(P, read32le(P) | V); }
+
+static void applySecRel(const SectionChunk *Sec, uint8_t *Off,
+ OutputSection *OS, uint64_t S) {
+ if (!OS) {
+ if (Sec->isCodeView())
+ return;
+ fatal("SECREL relocation cannot be applied to absolute symbols");
+ }
+ uint64_t SecRel = S - OS->getRVA();
+ if (SecRel > UINT32_MAX) {
+ error("overflow in SECREL relocation in section: " + Sec->getSectionName());
+ return;
+ }
+ add32(Off, SecRel);
+}
+
+static void applySecIdx(uint8_t *Off, OutputSection *OS) {
+ // If we have no output section, this must be an absolute symbol. Use the
+ // sentinel absolute symbol section index.
+ uint16_t SecIdx = OS ? OS->SectionIndex : DefinedAbsolute::OutputSectionIndex;
+ add16(Off, SecIdx);
+}
-void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, Defined *Sym,
- uint64_t P) const {
- uint64_t S = Sym->getRVA();
+void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS,
+ uint64_t S, uint64_t P) const {
switch (Type) {
case IMAGE_REL_AMD64_ADDR32: add32(Off, S + Config->ImageBase); break;
case IMAGE_REL_AMD64_ADDR64: add64(Off, S + Config->ImageBase); break;
@@ -65,23 +88,22 @@ void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, Defined *Sym,
case IMAGE_REL_AMD64_REL32_3: add32(Off, S - P - 7); break;
case IMAGE_REL_AMD64_REL32_4: add32(Off, S - P - 8); break;
case IMAGE_REL_AMD64_REL32_5: add32(Off, S - P - 9); break;
- case IMAGE_REL_AMD64_SECTION: add16(Off, Sym->getSectionIndex()); break;
- case IMAGE_REL_AMD64_SECREL: add32(Off, Sym->getSecrel()); break;
+ case IMAGE_REL_AMD64_SECTION: applySecIdx(Off, OS); break;
+ case IMAGE_REL_AMD64_SECREL: applySecRel(this, Off, OS, S); break;
default:
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
}
}
-void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, Defined *Sym,
- uint64_t P) const {
- uint64_t S = Sym->getRVA();
+void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, OutputSection *OS,
+ uint64_t S, uint64_t P) const {
switch (Type) {
case IMAGE_REL_I386_ABSOLUTE: break;
case IMAGE_REL_I386_DIR32: add32(Off, S + Config->ImageBase); break;
case IMAGE_REL_I386_DIR32NB: add32(Off, S); break;
case IMAGE_REL_I386_REL32: add32(Off, S - P - 4); break;
- case IMAGE_REL_I386_SECTION: add16(Off, Sym->getSectionIndex()); break;
- case IMAGE_REL_I386_SECREL: add32(Off, Sym->getSecrel()); break;
+ case IMAGE_REL_I386_SECTION: applySecIdx(Off, OS); break;
+ case IMAGE_REL_I386_SECREL: applySecRel(this, Off, OS, S); break;
default:
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
}
@@ -100,7 +122,7 @@ static uint16_t readMOV(uint8_t *Off) {
return Imm;
}
-static void applyMOV32T(uint8_t *Off, uint32_t V) {
+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);
@@ -117,7 +139,7 @@ static void applyBranch20T(uint8_t *Off, int32_t V) {
or16(Off + 2, (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff));
}
-static void applyBranch24T(uint8_t *Off, int32_t V) {
+void applyBranch24T(uint8_t *Off, int32_t V) {
if (!isInt<25>(V))
fatal("relocation out of range");
uint32_t S = V < 0 ? 1 : 0;
@@ -128,20 +150,80 @@ static void applyBranch24T(uint8_t *Off, int32_t V) {
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,
- uint64_t P) const {
- uint64_t S = Sym->getRVA();
+void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS,
+ uint64_t S, uint64_t P) const {
// Pointer to thumb code must have the LSB set.
- if (Sym->isExecutable())
- S |= 1;
+ uint64_t SX = S;
+ if (OS && (OS->getPermissions() & IMAGE_SCN_MEM_EXECUTE))
+ SX |= 1;
+ switch (Type) {
+ case IMAGE_REL_ARM_ADDR32: add32(Off, SX + Config->ImageBase); break;
+ case IMAGE_REL_ARM_ADDR32NB: add32(Off, SX); break;
+ case IMAGE_REL_ARM_MOV32T: applyMOV32T(Off, SX + Config->ImageBase); break;
+ case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(Off, SX - P - 4); break;
+ case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(Off, SX - P - 4); break;
+ case IMAGE_REL_ARM_BLX23T: applyBranch24T(Off, SX - P - 4); break;
+ case IMAGE_REL_ARM_SECTION: applySecIdx(Off, OS); break;
+ case IMAGE_REL_ARM_SECREL: applySecRel(this, Off, OS, S); break;
+ default:
+ fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
+ }
+}
+
+// Interpret the existing immediate value as a byte offset to the
+// target symbol, then update the instruction with the immediate as
+// the page offset from the current instruction to the target.
+static void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P) {
+ uint32_t Orig = read32le(Off);
+ uint64_t Imm = ((Orig >> 29) & 0x3) | ((Orig >> 3) & 0x1FFFFC);
+ S += Imm;
+ Imm = (S >> 12) - (P >> 12);
+ uint32_t ImmLo = (Imm & 0x3) << 29;
+ uint32_t ImmHi = (Imm & 0x1FFFFC) << 3;
+ uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3);
+ write32le(Off, (Orig & ~Mask) | ImmLo | ImmHi);
+}
+
+// Update the immediate field in a AARCH64 ldr, str, and add instruction.
+// Optionally limit the range of the written immediate by one or more bits
+// (RangeLimit).
+static void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit) {
+ uint32_t Orig = read32le(Off);
+ Imm += (Orig >> 10) & 0xFFF;
+ Orig &= ~(0xFFF << 10);
+ write32le(Off, Orig | ((Imm & (0xFFF >> RangeLimit)) << 10));
+}
+
+// Add the 12 bit page offset to the existing immediate.
+// Ldr/str instructions store the opcode immediate scaled
+// by the load/store size (giving a larger range for larger
+// loads/stores). The immediate is always (both before and after
+// fixing up the relocation) stored scaled similarly.
+// Even if larger loads/stores have a larger range, limit the
+// effective offset to 12 bit, since it is intended to be a
+// page offset.
+static void applyArm64Ldr(uint8_t *Off, uint64_t Imm) {
+ uint32_t Orig = read32le(Off);
+ uint32_t Size = Orig >> 30;
+ // 0x04000000 indicates SIMD/FP registers
+ // 0x00800000 indicates 128 bit
+ if ((Orig & 0x4800000) == 0x4800000)
+ Size += 4;
+ if ((Imm & ((1 << Size) - 1)) != 0)
+ fatal("misaligned ldr/str offset");
+ applyArm64Imm(Off, Imm >> Size, Size);
+}
+
+void SectionChunk::applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS,
+ uint64_t S, uint64_t P) const {
switch (Type) {
- case IMAGE_REL_ARM_ADDR32: add32(Off, S + Config->ImageBase); break;
- case IMAGE_REL_ARM_ADDR32NB: add32(Off, S); break;
- case IMAGE_REL_ARM_MOV32T: applyMOV32T(Off, S + Config->ImageBase); break;
- 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;
+ case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(Off, S, P); break;
+ case IMAGE_REL_ARM64_PAGEOFFSET_12A: applyArm64Imm(Off, S & 0xfff, 0); break;
+ case IMAGE_REL_ARM64_PAGEOFFSET_12L: applyArm64Ldr(Off, S & 0xfff); break;
+ case IMAGE_REL_ARM64_BRANCH26: or32(Off, ((S - P) & 0x0FFFFFFC) >> 2); break;
+ case IMAGE_REL_ARM64_ADDR32: add32(Off, S + Config->ImageBase); break;
+ case IMAGE_REL_ARM64_ADDR32NB: add32(Off, S); break;
+ case IMAGE_REL_ARM64_ADDR64: add64(Off, S + Config->ImageBase); break;
default:
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
}
@@ -155,20 +237,52 @@ void SectionChunk::writeTo(uint8_t *Buf) const {
memcpy(Buf + OutputSectionOff, A.data(), A.size());
// Apply relocations.
+ size_t InputSize = getSize();
for (const coff_relocation &Rel : Relocs) {
+ // Check for an invalid relocation offset. This check isn't perfect, because
+ // we don't have the relocation size, which is only known after checking the
+ // machine and relocation type. As a result, a relocation may overwrite the
+ // beginning of the following input section.
+ if (Rel.VirtualAddress >= InputSize)
+ fatal("relocation points beyond the end of its parent section");
+
uint8_t *Off = Buf + OutputSectionOff + Rel.VirtualAddress;
+
+ // Get the output section of the symbol for this relocation. The output
+ // section is needed to compute SECREL and SECTION relocations used in debug
+ // info.
SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex);
Defined *Sym = cast<Defined>(Body);
+ Chunk *C = Sym->getChunk();
+ OutputSection *OS = C ? C->getOutputSection() : nullptr;
+
+ // Only absolute and __ImageBase symbols lack an output section. For any
+ // other symbol, this indicates that the chunk was discarded. Normally
+ // relocations against discarded sections are an error. However, debug info
+ // sections are not GC roots and can end up with these kinds of relocations.
+ // Skip these relocations.
+ if (!OS && !isa<DefinedAbsolute>(Sym) && !isa<DefinedSynthetic>(Sym)) {
+ if (isCodeView() || isDWARF())
+ continue;
+ fatal("relocation against symbol in discarded section: " +
+ Sym->getName());
+ }
+ uint64_t S = Sym->getRVA();
+
+ // Compute the RVA of the relocation for relative relocations.
uint64_t P = RVA + Rel.VirtualAddress;
switch (Config->Machine) {
case AMD64:
- applyRelX64(Off, Rel.Type, Sym, P);
+ applyRelX64(Off, Rel.Type, OS, S, P);
break;
case I386:
- applyRelX86(Off, Rel.Type, Sym, P);
+ applyRelX86(Off, Rel.Type, OS, S, P);
break;
case ARMNT:
- applyRelARM(Off, Rel.Type, Sym, P);
+ applyRelARM(Off, Rel.Type, OS, S, P);
+ break;
+ case ARM64:
+ applyRelARM64(Off, Rel.Type, OS, S, P);
break;
default:
llvm_unreachable("unknown machine type");
@@ -196,6 +310,10 @@ static uint8_t getBaserelType(const coff_relocation &Rel) {
if (Rel.Type == IMAGE_REL_ARM_MOV32T)
return IMAGE_REL_BASED_ARM_MOV32T;
return IMAGE_REL_BASED_ABSOLUTE;
+ case ARM64:
+ if (Rel.Type == IMAGE_REL_ARM64_ADDR64)
+ return IMAGE_REL_BASED_DIR64;
+ return IMAGE_REL_BASED_ABSOLUTE;
default:
llvm_unreachable("unknown machine type");
}
@@ -260,7 +378,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), PowerOf2Ceil(Sym.getValue()));
+ Alignment = std::min(uint64_t(32), PowerOf2Ceil(Sym.getValue()));
}
uint32_t CommonChunk::getPermissions() const {
@@ -275,7 +393,7 @@ void StringChunk::writeTo(uint8_t *Buf) const {
ImportThunkChunkX64::ImportThunkChunkX64(Defined *S) : ImpSymbol(S) {
// Intel Optimization Manual says that all branch targets
// should be 16-byte aligned. MSVC linker does this too.
- Align = 16;
+ Alignment = 16;
}
void ImportThunkChunkX64::writeTo(uint8_t *Buf) const {
@@ -305,6 +423,13 @@ void ImportThunkChunkARM::writeTo(uint8_t *Buf) const {
applyMOV32T(Buf + OutputSectionOff, ImpSymbol->getRVA() + Config->ImageBase);
}
+void ImportThunkChunkARM64::writeTo(uint8_t *Buf) const {
+ int64_t Off = ImpSymbol->getRVA() & 0xfff;
+ memcpy(Buf + OutputSectionOff, ImportThunkARM64, sizeof(ImportThunkARM64));
+ applyArm64Addr(Buf + OutputSectionOff, ImpSymbol->getRVA(), RVA);
+ applyArm64Ldr(Buf + OutputSectionOff + 4, Off);
+}
+
void LocalImportChunk::getBaserels(std::vector<Baserel> *Res) {
Res->emplace_back(getRVA());
}
@@ -391,6 +516,7 @@ uint8_t Baserel::getDefaultType() {
case AMD64:
return IMAGE_REL_BASED_DIR64;
case I386:
+ case ARMNT:
return IMAGE_REL_BASED_HIGHLOW;
default:
llvm_unreachable("unknown machine type");
diff --git a/COFF/Chunks.h b/COFF/Chunks.h
index 3d4d7e787..e81b89240 100644
--- a/COFF/Chunks.h
+++ b/COFF/Chunks.h
@@ -12,7 +12,7 @@
#include "Config.h"
#include "InputFiles.h"
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
@@ -33,7 +33,7 @@ class Baserel;
class Defined;
class DefinedImportData;
class DefinedRegular;
-class ObjectFile;
+class ObjFile;
class OutputSection;
class SymbolBody;
@@ -62,9 +62,7 @@ public:
// The writer sets and uses the addresses.
uint64_t getRVA() const { return RVA; }
- uint32_t getAlign() const { return Align; }
void setRVA(uint64_t V) { RVA = V; }
- void setOutputSectionOff(uint64_t V) { OutputSectionOff = V; }
// Returns true if this has non-zero data. BSS chunks return
// false. If false is returned, the space occupied by this chunk
@@ -83,7 +81,7 @@ public:
// An output section has pointers to chunks in the section, and each
// chunk has a back pointer to an output section.
void setOutputSection(OutputSection *O) { Out = O; }
- OutputSection *getOutputSection() { return Out; }
+ OutputSection *getOutputSection() const { return Out; }
// Windows-specific.
// Collect all locations that contain absolute addresses for base relocations.
@@ -93,6 +91,9 @@ public:
// bytes, so this is used only for logging or debugging.
virtual StringRef getDebugName() { return ""; }
+ // The alignment of this chunk. The writer uses the value.
+ uint32_t Alignment = 1;
+
protected:
Chunk(Kind K = OtherKind) : ChunkKind(K) {}
const Kind ChunkKind;
@@ -100,18 +101,16 @@ protected:
// The RVA of this chunk in the output. The writer sets a value.
uint64_t RVA = 0;
- // The offset from beginning of the output section. The writer sets a value.
- uint64_t OutputSectionOff = 0;
-
// The output section for this chunk.
OutputSection *Out = nullptr;
- // The alignment of this chunk. The writer uses the value.
- uint32_t Align = 1;
+public:
+ // The offset from beginning of the output section. The writer sets a value.
+ uint64_t OutputSectionOff = 0;
};
// A chunk corresponding a section of an input file.
-class SectionChunk : public Chunk {
+class SectionChunk final : public Chunk {
// Identical COMDAT Folding feature accesses section internal data.
friend class ICF;
@@ -121,9 +120,9 @@ public:
std::random_access_iterator_tag, SymbolBody *> {
friend SectionChunk;
- ObjectFile *File;
+ ObjFile *File;
- symbol_iterator(ObjectFile *File, const coff_relocation *I)
+ symbol_iterator(ObjFile *File, const coff_relocation *I)
: symbol_iterator::iterator_adaptor_base(I), File(File) {}
public:
@@ -134,7 +133,7 @@ public:
}
};
- SectionChunk(ObjectFile *File, const coff_section *Header);
+ SectionChunk(ObjFile *File, const coff_section *Header);
static bool classof(const Chunk *C) { return C->kind() == SectionKind; }
size_t getSize() const override { return Header->SizeOfRawData; }
ArrayRef<uint8_t> getContents() const;
@@ -144,9 +143,14 @@ public:
StringRef getSectionName() const override { return SectionName; }
void getBaserels(std::vector<Baserel> *Res) override;
bool isCOMDAT() const;
- void applyRelX64(uint8_t *Off, uint16_t Type, Defined *Sym, uint64_t P) const;
- void applyRelX86(uint8_t *Off, uint16_t Type, Defined *Sym, uint64_t P) const;
- void applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym, uint64_t P) const;
+ void applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
+ uint64_t P) const;
+ void applyRelX86(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
+ uint64_t P) const;
+ void applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
+ uint64_t P) const;
+ void applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
+ uint64_t P) const;
// Called if the garbage collector decides to not include this chunk
// in a final output. It's supposed to print out a log message to stdout.
@@ -176,6 +180,17 @@ public:
// redundant when GC is enabled, as all comdat sections will start out dead.
void markDiscarded() { Discarded = true; }
+ // True if this is a codeview debug info chunk. These will not be laid out in
+ // the image. Instead they will end up in the PDB, if one is requested.
+ bool isCodeView() const {
+ return SectionName == ".debug" || SectionName.startswith(".debug$");
+ }
+
+ // True if this is a DWARF debug info or exception handling chunk.
+ bool isDWARF() const {
+ return SectionName.startswith(".debug_") || SectionName == ".eh_frame";
+ }
+
// Allow iteration over the bodies of this chunk's relocated symbols.
llvm::iterator_range<symbol_iterator> symbols() const {
return llvm::make_range(symbol_iterator(File, Relocs.begin()),
@@ -198,7 +213,7 @@ public:
const coff_section *Header;
// The file that this chunk was created from.
- ObjectFile *File;
+ ObjFile *File;
private:
StringRef SectionName;
@@ -254,6 +269,12 @@ static const uint8_t ImportThunkARM[] = {
0xdc, 0xf8, 0x00, 0xf0, // ldr.w pc, [ip]
};
+static const uint8_t ImportThunkARM64[] = {
+ 0x10, 0x00, 0x00, 0x90, // adrp x16, #0
+ 0x10, 0x02, 0x40, 0xf9, // ldr x16, [x16]
+ 0x00, 0x02, 0x1f, 0xd6, // br x16
+};
+
// Windows-specific.
// A chunk for DLL import jump table entry. In a final output, it's
// contents will be a JMP instruction to some __imp_ symbol.
@@ -289,6 +310,16 @@ private:
Defined *ImpSymbol;
};
+class ImportThunkChunkARM64 : public Chunk {
+public:
+ explicit ImportThunkChunkARM64(Defined *S) : ImpSymbol(S) {}
+ size_t getSize() const override { return sizeof(ImportThunkARM64); }
+ void writeTo(uint8_t *Buf) const override;
+
+private:
+ Defined *ImpSymbol;
+};
+
// Windows-specific.
// See comments for DefinedLocalImport class.
class LocalImportChunk : public Chunk {
@@ -338,6 +369,9 @@ public:
uint8_t Type;
};
+void applyMOV32T(uint8_t *Off, uint32_t V);
+void applyBranch24T(uint8_t *Off, int32_t V);
+
} // namespace coff
} // namespace lld
diff --git a/COFF/Config.h b/COFF/Config.h
index 9fcea96d6..995a398a3 100644
--- a/COFF/Config.h
+++ b/COFF/Config.h
@@ -12,6 +12,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/COFF.h"
+#include "llvm/Support/CachePruning.h"
#include <cstdint>
#include <map>
#include <set>
@@ -31,6 +32,7 @@ class SymbolBody;
// Short aliases.
static const auto AMD64 = llvm::COFF::IMAGE_FILE_MACHINE_AMD64;
+static const auto ARM64 = llvm::COFF::IMAGE_FILE_MACHINE_ARM64;
static const auto ARMNT = llvm::COFF::IMAGE_FILE_MACHINE_ARMNT;
static const auto I386 = llvm::COFF::IMAGE_FILE_MACHINE_I386;
@@ -73,7 +75,7 @@ enum class DebugType {
// Global configuration.
struct Configuration {
enum ManifestKind { SideBySide, Embed, No };
- bool is64() { return Machine == AMD64; }
+ bool is64() { return Machine == AMD64 || Machine == ARM64; }
llvm::COFF::MachineTypes Machine = IMAGE_FILE_MACHINE_UNKNOWN;
bool Verbose = false;
@@ -81,6 +83,7 @@ struct Configuration {
SymbolBody *Entry = nullptr;
bool NoEntry = false;
std::string OutputFile;
+ std::string ImportName;
bool ColorDiagnostics;
bool DoGC = true;
bool DoICF = true;
@@ -91,6 +94,7 @@ struct Configuration {
bool WriteSymtab = true;
unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
llvm::SmallString<128> PDBPath;
+ std::vector<llvm::StringRef> Argv;
// Symbols in this set are considered as live by the garbage collector.
std::set<SymbolBody *> GCRoot;
@@ -120,6 +124,11 @@ struct Configuration {
// Used for /opt:lldltopartitions=N
unsigned LTOPartitions = 1;
+ // Used for /opt:lldltocache=path
+ StringRef LTOCache;
+ // Used for /opt:lldltocachepolicy=policy
+ llvm::CachePruningPolicy LTOCachePolicy;
+
// Used for /merge:from=to (e.g. /merge:.rdata=.text)
std::map<StringRef, StringRef> Merge;
@@ -127,7 +136,7 @@ struct Configuration {
std::map<StringRef, uint32_t> Section;
// Options for manifest files.
- ManifestKind Manifest = SideBySide;
+ ManifestKind Manifest = No;
int ManifestID = 1;
StringRef ManifestDependency;
bool ManifestUAC = true;
@@ -136,6 +145,9 @@ struct Configuration {
StringRef ManifestUIAccess = "'false'";
StringRef ManifestFile;
+ // Used for /aligncomm.
+ std::map<std::string, int> AlignComm;
+
// Used for /failifmismatch.
std::map<StringRef, StringRef> MustMatch;
@@ -155,12 +167,14 @@ struct Configuration {
uint32_t MajorOSVersion = 6;
uint32_t MinorOSVersion = 0;
bool DynamicBase = true;
+ bool AllowBind = true;
bool NxCompat = true;
bool AllowIsolation = true;
bool TerminalServerAware = true;
bool LargeAddressAware = false;
bool HighEntropyVA = false;
bool AppContainer = false;
+ bool MinGW = false;
};
extern Configuration *Config;
diff --git a/COFF/DLL.cpp b/COFF/DLL.cpp
index d76410b67..847d15d85 100644
--- a/COFF/DLL.cpp
+++ b/COFF/DLL.cpp
@@ -61,7 +61,7 @@ private:
// A chunk for the import descriptor table.
class LookupChunk : public Chunk {
public:
- explicit LookupChunk(Chunk *C) : HintName(C) {}
+ explicit LookupChunk(Chunk *C) : HintName(C) { Alignment = ptrSize(); }
size_t getSize() const override { return ptrSize(); }
void writeTo(uint8_t *Buf) const override {
@@ -76,7 +76,7 @@ public:
// See Microsoft PE/COFF spec 7.1. Import Header for details.
class OrdinalOnlyChunk : public Chunk {
public:
- explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) {}
+ explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) { Alignment = ptrSize(); }
size_t getSize() const override { return ptrSize(); }
void writeTo(uint8_t *Buf) const override {
@@ -117,7 +117,6 @@ public:
explicit NullChunk(size_t N) : Size(N) {}
bool hasData() const override { return false; }
size_t getSize() const override { return Size; }
- void setAlign(size_t N) { Align = N; }
private:
size_t Size;
@@ -215,6 +214,22 @@ static const uint8_t ThunkX86[] = {
0xFF, 0xE0, // jmp eax
};
+static const uint8_t ThunkARM[] = {
+ 0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0 __imp_<FUNCNAME>
+ 0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 __imp_<FUNCNAME>
+ 0x2d, 0xe9, 0x0f, 0x48, // push.w {r0, r1, r2, r3, r11, lr}
+ 0x0d, 0xf2, 0x10, 0x0b, // addw r11, sp, #16
+ 0x2d, 0xed, 0x10, 0x0b, // vpush {d0, d1, d2, d3, d4, d5, d6, d7}
+ 0x61, 0x46, // mov r1, ip
+ 0x40, 0xf2, 0x00, 0x00, // mov.w r0, #0 DELAY_IMPORT_DESCRIPTOR
+ 0xc0, 0xf2, 0x00, 0x00, // mov.t r0, #0 DELAY_IMPORT_DESCRIPTOR
+ 0x00, 0xf0, 0x00, 0xd0, // bl #0 __delayLoadHelper2
+ 0x84, 0x46, // mov ip, r0
+ 0xbd, 0xec, 0x10, 0x0b, // vpop {d0, d1, d2, d3, d4, d5, d6, d7}
+ 0xbd, 0xe8, 0x0f, 0x48, // pop.w {r0, r1, r2, r3, r11, lr}
+ 0x60, 0x47, // bx ip
+};
+
// A chunk for the delay import thunk.
class ThunkChunkX64 : public Chunk {
public:
@@ -259,17 +274,45 @@ public:
Defined *Helper = nullptr;
};
+class ThunkChunkARM : public Chunk {
+public:
+ ThunkChunkARM(Defined *I, Chunk *D, Defined *H)
+ : Imp(I), Desc(D), Helper(H) {}
+
+ size_t getSize() const override { return sizeof(ThunkARM); }
+
+ void writeTo(uint8_t *Buf) const override {
+ memcpy(Buf + OutputSectionOff, ThunkARM, sizeof(ThunkARM));
+ applyMOV32T(Buf + OutputSectionOff + 0, Imp->getRVA() + Config->ImageBase);
+ applyMOV32T(Buf + OutputSectionOff + 22, Desc->getRVA() + Config->ImageBase);
+ applyBranch24T(Buf + OutputSectionOff + 30, Helper->getRVA() - RVA - 34);
+ }
+
+ void getBaserels(std::vector<Baserel> *Res) override {
+ Res->emplace_back(RVA + 0, IMAGE_REL_BASED_ARM_MOV32T);
+ Res->emplace_back(RVA + 22, IMAGE_REL_BASED_ARM_MOV32T);
+ }
+
+ Defined *Imp = nullptr;
+ Chunk *Desc = nullptr;
+ Defined *Helper = nullptr;
+};
+
// A chunk for the import descriptor table.
class DelayAddressChunk : public Chunk {
public:
- explicit DelayAddressChunk(Chunk *C) : Thunk(C) {}
+ explicit DelayAddressChunk(Chunk *C) : Thunk(C) { Alignment = ptrSize(); }
size_t getSize() const override { return ptrSize(); }
void writeTo(uint8_t *Buf) const override {
if (Config->is64()) {
write64le(Buf + OutputSectionOff, Thunk->getRVA() + Config->ImageBase);
} else {
- write32le(Buf + OutputSectionOff, Thunk->getRVA() + Config->ImageBase);
+ uint32_t Bit = 0;
+ // Pointer to thumb code must have the LSB set, so adjust it.
+ if (Config->Machine == ARMNT)
+ Bit = 1;
+ write32le(Buf + OutputSectionOff, (Thunk->getRVA() + Config->ImageBase) | Bit);
}
}
@@ -319,12 +362,16 @@ public:
size_t getSize() const override { return Size * 4; }
void writeTo(uint8_t *Buf) const override {
+ uint32_t Bit = 0;
+ // Pointer to thumb code must have the LSB set, so adjust it.
+ if (Config->Machine == ARMNT)
+ Bit = 1;
for (Export &E : Config->Exports) {
uint8_t *P = Buf + OutputSectionOff + E.Ordinal * 4;
if (E.ForwardChunk) {
- write32le(P, E.ForwardChunk->getRVA());
+ write32le(P, E.ForwardChunk->getRVA() | Bit);
} else {
- write32le(P, cast<Defined>(E.Sym)->getRVA());
+ write32le(P, cast<Defined>(E.Sym)->getRVA() | Bit);
}
}
}
@@ -487,7 +534,7 @@ void DelayLoadContents::create(Defined *H) {
for (int I = 0, E = Syms.size(); I < E; ++I)
Syms[I]->setLocation(Addresses[Base + I]);
auto *MH = make<NullChunk>(8);
- MH->setAlign(8);
+ MH->Alignment = 8;
ModuleHandles.push_back(MH);
// Fill the delay import table header fields.
@@ -506,6 +553,8 @@ Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *S, Chunk *Dir) {
return make<ThunkChunkX64>(S, Dir, Helper);
case I386:
return make<ThunkChunkX86>(S, Dir, Helper);
+ case ARMNT:
+ return make<ThunkChunkARM>(S, Dir, Helper);
default:
llvm_unreachable("unsupported machine type");
}
diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp
index 45a6d3276..c03974071 100644
--- a/COFF/Driver.cpp
+++ b/COFF/Driver.cpp
@@ -12,10 +12,11 @@
#include "Error.h"
#include "InputFiles.h"
#include "Memory.h"
+#include "MinGW.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Writer.h"
-#include "lld/Driver/Driver.h"
+#include "lld/Common/Driver.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/BinaryFormat/Magic.h"
@@ -55,10 +56,13 @@ std::vector<SpecificAllocBase *> SpecificAllocBase::Instances;
bool link(ArrayRef<const char *> Args, raw_ostream &Diag) {
ErrorCount = 0;
ErrorOS = &Diag;
- Argv0 = Args[0];
+
Config = make<Configuration>();
- Config->ColorDiagnostics =
- (ErrorOS == &llvm::errs() && Process::StandardErrHasColors());
+ Config->Argv = {Args.begin(), Args.end()};
+ Config->ColorDiagnostics = ErrorOS->has_colors();
+
+ Symtab = make<SymbolTable>();
+
Driver = make<LinkerDriver>();
Driver->link(Args);
return !ErrorCount;
@@ -107,30 +111,46 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> MB) {
return MBRef;
}
-void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> MB) {
+void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> MB,
+ bool WholeArchive) {
MemoryBufferRef MBRef = takeBuffer(std::move(MB));
+ FilePaths.push_back(MBRef.getBufferIdentifier());
// File type is detected by contents, not by file extension.
- file_magic Magic = identify_magic(MBRef.getBuffer());
- if (Magic == file_magic::windows_resource) {
+ switch (identify_magic(MBRef.getBuffer())) {
+ case file_magic::windows_resource:
Resources.push_back(MBRef);
- return;
- }
+ break;
- FilePaths.push_back(MBRef.getBufferIdentifier());
- if (Magic == file_magic::archive)
- return Symtab.addFile(make<ArchiveFile>(MBRef));
- if (Magic == file_magic::bitcode)
- return Symtab.addFile(make<BitcodeFile>(MBRef));
+ case file_magic::archive:
+ if (WholeArchive) {
+ std::unique_ptr<Archive> File =
+ check(Archive::create(MBRef),
+ MBRef.getBufferIdentifier() + ": failed to parse archive");
- if (Magic == file_magic::coff_cl_gl_object)
+ for (MemoryBufferRef M : getArchiveMembers(File.get()))
+ addArchiveBuffer(M, "<whole-archive>", MBRef.getBufferIdentifier());
+ return;
+ }
+ Symtab->addFile(make<ArchiveFile>(MBRef));
+ break;
+
+ case file_magic::bitcode:
+ Symtab->addFile(make<BitcodeFile>(MBRef));
+ break;
+
+ case file_magic::coff_cl_gl_object:
error(MBRef.getBufferIdentifier() + ": is not a native COFF file. "
"Recompile without /GL");
- else
- Symtab.addFile(make<ObjectFile>(MBRef));
+ break;
+
+ default:
+ Symtab->addFile(make<ObjFile>(MBRef));
+ break;
+ }
}
-void LinkerDriver::enqueuePath(StringRef Path) {
+void LinkerDriver::enqueuePath(StringRef Path, bool WholeArchive) {
auto Future =
std::make_shared<std::future<MBErrPair>>(createFutureForFile(Path));
std::string PathStr = Path;
@@ -139,7 +159,7 @@ void LinkerDriver::enqueuePath(StringRef Path) {
if (MBOrErr.second)
error("could not open " + PathStr + ": " + MBOrErr.second.message());
else
- Driver->addBuffer(std::move(MBOrErr.first));
+ Driver->addBuffer(std::move(MBOrErr.first), WholeArchive);
});
}
@@ -147,13 +167,13 @@ 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));
+ Symtab->addFile(make<ImportFile>(MB));
return;
}
InputFile *Obj;
if (Magic == file_magic::coff_object) {
- Obj = make<ObjectFile>(MB);
+ Obj = make<ObjFile>(MB);
} else if (Magic == file_magic::bitcode) {
Obj = make<BitcodeFile>(MB);
} else {
@@ -162,7 +182,7 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName,
}
Obj->ParentName = ParentName;
- Symtab.addFile(Obj);
+ Symtab->addFile(Obj);
log("Loaded " + toString(Obj) + " for " + SymName);
}
@@ -198,19 +218,30 @@ static bool isDecorated(StringRef Sym) {
// Parses .drectve section contents and returns a list of files
// specified by /defaultlib.
void LinkerDriver::parseDirectives(StringRef S) {
+ ArgParser Parser;
+ // .drectve is always tokenized using Windows shell rules.
opt::InputArgList Args = Parser.parse(S);
for (auto *Arg : Args) {
- switch (Arg->getOption().getID()) {
+ switch (Arg->getOption().getUnaliasedOption().getID()) {
+ case OPT_aligncomm:
+ parseAligncomm(Arg->getValue());
+ break;
case OPT_alternatename:
parseAlternateName(Arg->getValue());
break;
case OPT_defaultlib:
if (Optional<StringRef> Path = findLib(Arg->getValue()))
- enqueuePath(*Path);
+ enqueuePath(*Path, false);
break;
case OPT_export: {
Export E = parseExport(Arg->getValue());
+ if (Config->Machine == I386 && Config->MinGW) {
+ if (!isDecorated(E.Name))
+ E.Name = Saver.save("_" + E.Name);
+ if (!E.ExtName.empty() && !isDecorated(E.ExtName))
+ E.ExtName = Saver.save("_" + E.ExtName);
+ }
E.Directives = true;
Config->Exports.push_back(E);
break;
@@ -233,6 +264,7 @@ void LinkerDriver::parseDirectives(StringRef S) {
case OPT_editandcontinue:
case OPT_fastfail:
case OPT_guardsym:
+ case OPT_natvis:
case OPT_throwingnew:
break;
default:
@@ -247,7 +279,7 @@ StringRef LinkerDriver::doFindFile(StringRef Filename) {
bool HasPathSep = (Filename.find_first_of("/\\") != StringRef::npos);
if (HasPathSep)
return Filename;
- bool HasExt = (Filename.find('.') != StringRef::npos);
+ bool HasExt = Filename.contains('.');
for (StringRef Dir : SearchPaths) {
SmallString<128> Path = Dir;
sys::path::append(Path, Filename);
@@ -275,7 +307,7 @@ 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);
+ bool HasExt = Filename.contains('.');
if (!HasExt)
Filename = Saver.save(Filename + ".lib");
return doFindFile(Filename);
@@ -311,7 +343,7 @@ void LinkerDriver::addLibSearchPaths() {
}
SymbolBody *LinkerDriver::addUndefined(StringRef Name) {
- SymbolBody *B = Symtab.addUndefined(Name);
+ SymbolBody *B = Symtab->addUndefined(Name);
Config->GCRoot.insert(B);
return B;
}
@@ -334,8 +366,8 @@ StringRef LinkerDriver::findDefaultEntry() {
{"wWinMain", "wWinMainCRTStartup"},
};
for (auto E : Entries) {
- StringRef Entry = Symtab.findMangle(mangle(E[0]));
- if (!Entry.empty() && !isa<Undefined>(Symtab.find(Entry)->body()))
+ StringRef Entry = Symtab->findMangle(mangle(E[0]));
+ if (!Entry.empty() && !isa<Undefined>(Symtab->find(Entry)->body()))
return mangle(E[1]);
}
return "";
@@ -344,9 +376,9 @@ StringRef LinkerDriver::findDefaultEntry() {
WindowsSubsystem LinkerDriver::inferSubsystem() {
if (Config->DLL)
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
- if (Symtab.findUnderscore("main") || Symtab.findUnderscore("wmain"))
+ if (Symtab->findUnderscore("main") || Symtab->findUnderscore("wmain"))
return IMAGE_SUBSYSTEM_WINDOWS_CUI;
- if (Symtab.findUnderscore("WinMain") || Symtab.findUnderscore("wWinMain"))
+ if (Symtab->findUnderscore("WinMain") || Symtab->findUnderscore("wWinMain"))
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
return IMAGE_SUBSYSTEM_UNKNOWN;
}
@@ -429,12 +461,37 @@ static std::string getImplibPath() {
return Out.str();
}
-static void createImportLibrary() {
+//
+// The import name is caculated as the following:
+//
+// | LIBRARY w/ ext | LIBRARY w/o ext | no LIBRARY
+// -----+----------------+---------------------+------------------
+// LINK | {value} | {value}.{.dll/.exe} | {output name}
+// LIB | {value} | {value}.dll | {output name}.dll
+//
+static std::string getImportName(bool AsLib) {
+ SmallString<128> Out;
+
+ if (Config->ImportName.empty()) {
+ Out.assign(sys::path::filename(Config->OutputFile));
+ if (AsLib)
+ sys::path::replace_extension(Out, ".dll");
+ } else {
+ Out.assign(Config->ImportName);
+ if (!sys::path::has_extension(Out))
+ sys::path::replace_extension(Out,
+ (Config->DLL || AsLib) ? ".dll" : ".exe");
+ }
+
+ return Out.str();
+}
+
+static void createImportLibrary(bool AsLib) {
std::vector<COFFShortExport> Exports;
for (Export &E1 : Config->Exports) {
COFFShortExport E2;
- // Use SymbolName, which will have any stdcall or fastcall qualifiers.
- E2.Name = E1.SymbolName;
+ E2.Name = E1.Name;
+ E2.SymbolName = E1.SymbolName;
E2.ExtName = E1.ExtName;
E2.Ordinal = E1.Ordinal;
E2.Noname = E1.Noname;
@@ -444,9 +501,10 @@ static void createImportLibrary() {
Exports.push_back(E2);
}
- std::string DLLName = sys::path::filename(Config->OutputFile);
- std::string Path = getImplibPath();
- writeImportLibrary(DLLName, Path, Exports, Config->Machine);
+ auto E = writeImportLibrary(getImportName(AsLib), getImplibPath(), Exports,
+ Config->Machine, false);
+ handleAllErrors(std::move(E),
+ [&](ErrorInfoBase &EIB) { error(EIB.message()); });
}
static void parseModuleDefs(StringRef Path) {
@@ -457,6 +515,7 @@ static void parseModuleDefs(StringRef Path) {
if (Config->OutputFile.empty())
Config->OutputFile = Saver.save(M.OutputFile);
+ Config->ImportName = Saver.save(M.ImportName);
if (M.ImageBase)
Config->ImageBase = M.ImageBase;
if (M.StackReserve)
@@ -490,25 +549,6 @@ static void parseModuleDefs(StringRef Path) {
}
}
-std::vector<MemoryBufferRef> getArchiveMembers(Archive *File) {
- std::vector<MemoryBufferRef> V;
- Error Err = Error::success();
- for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
- Archive::Child C =
- check(COrErr,
- File->getFileName() + ": could not get the child of the archive");
- MemoryBufferRef MBRef =
- check(C.getMemoryBufferRef(),
- File->getFileName() +
- ": could not get the buffer for a child of the archive");
- V.push_back(MBRef);
- }
- if (Err)
- fatal(File->getFileName() +
- ": Archive::children failed: " + toString(std::move(Err)));
- return V;
-}
-
// A helper function for filterBitcodeFiles.
static bool needsRebuilding(MemoryBufferRef MB) {
// The MSVC linker doesn't support thin archives, so if it's a thin
@@ -568,12 +608,13 @@ filterBitcodeFiles(StringRef Path, std::vector<std::string> &TemporaryFiles) {
std::string Temp = S.str();
TemporaryFiles.push_back(Temp);
- std::pair<StringRef, std::error_code> Ret =
+ Error E =
llvm::writeArchive(Temp, New, /*WriteSymtab=*/true, Archive::Kind::K_GNU,
/*Deterministics=*/true,
/*Thin=*/false);
- if (Ret.second)
- error("failed to create a new archive " + S.str() + ": " + Ret.first);
+ handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {
+ error("failed to create a new archive " + S.str() + ": " + EI.message());
+ });
return Temp;
}
@@ -585,16 +626,16 @@ void LinkerDriver::invokeMSVC(opt::InputArgList &Args) {
// Write out archive members that we used in symbol resolution and pass these
// to MSVC before any archives, so that MSVC uses the same objects to satisfy
// references.
- for (const auto *O : Symtab.ObjectFiles) {
- if (O->ParentName.empty())
+ for (ObjFile *Obj : ObjFile::Instances) {
+ if (Obj->ParentName.empty())
continue;
SmallString<128> S;
int Fd;
if (auto EC = sys::fs::createTemporaryFile(
- "lld-" + sys::path::filename(O->ParentName), ".obj", Fd, S))
+ "lld-" + sys::path::filename(Obj->ParentName), ".obj", Fd, S))
fatal(EC, "cannot create a temporary file");
raw_fd_ostream OS(Fd, /*shouldClose*/ true);
- OS << O->MB.getBuffer();
+ OS << Obj->MB.getBuffer();
Temps.push_back(S.str());
Rsp += quote(S) + "\n";
}
@@ -626,8 +667,8 @@ void LinkerDriver::invokeMSVC(opt::InputArgList &Args) {
}
}
- std::vector<StringRef> ObjectFiles = Symtab.compileBitcodeFiles();
- runMSVCLinker(Rsp, ObjectFiles);
+ std::vector<StringRef> ObjFiles = Symtab->compileBitcodeFiles();
+ runMSVCLinker(Rsp, ObjFiles);
for (StringRef Path : Temps)
sys::fs::remove(Path);
@@ -664,6 +705,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
InitializeAllDisassemblers();
// Parse command line options.
+ ArgParser Parser;
opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1));
// Parse and evaluate -mllvm options.
@@ -688,6 +730,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
return;
}
+ // Handle /lldmingw early, since it can potentially affect how other
+ // options are handled.
+ Config->MinGW = Args.hasArg(OPT_lldmingw);
+
if (auto *Arg = Args.getLastArg(OPT_linkrepro)) {
SmallString<64> Path = StringRef(Arg->getValue());
sys::path::append(Path, "repro.tar");
@@ -703,8 +749,8 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
}
}
- if (!Args.hasArgNoClaim(OPT_INPUT)) {
- if (Args.hasArgNoClaim(OPT_deffile))
+ if (!Args.hasArg(OPT_INPUT)) {
+ if (Args.hasArg(OPT_deffile))
Config->NoEntry = true;
else
fatal("no input files");
@@ -731,10 +777,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Handle /debug
if (Args.hasArg(OPT_debug)) {
Config->Debug = true;
- Config->DebugTypes =
- Args.hasArg(OPT_debugtype)
- ? parseDebugType(Args.getLastArg(OPT_debugtype)->getValue())
- : getDefaultDebugType(Args);
+ if (auto *Arg = Args.getLastArg(OPT_debugtype))
+ Config->DebugTypes = parseDebugType(Arg->getValue());
+ else
+ Config->DebugTypes = getDefaultDebugType(Args);
}
// Create a dummy PDB file to satisfy build sytem rules.
@@ -825,7 +871,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
Config->DoICF = false;
continue;
}
- if (S == "icf" || StringRef(S).startswith("icf=")) {
+ if (S == "icf" || S.startswith("icf=")) {
Config->DoICF = true;
continue;
}
@@ -833,21 +879,21 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
Config->DoICF = false;
continue;
}
- if (StringRef(S).startswith("lldlto=")) {
- StringRef OptLevel = StringRef(S).substr(7);
+ if (S.startswith("lldlto=")) {
+ StringRef OptLevel = S.substr(7);
if (OptLevel.getAsInteger(10, Config->LTOOptLevel) ||
Config->LTOOptLevel > 3)
error("/opt:lldlto: invalid optimization level: " + OptLevel);
continue;
}
- if (StringRef(S).startswith("lldltojobs=")) {
- StringRef Jobs = StringRef(S).substr(11);
+ if (S.startswith("lldltojobs=")) {
+ StringRef Jobs = S.substr(11);
if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0)
error("/opt:lldltojobs: invalid job count: " + Jobs);
continue;
}
- if (StringRef(S).startswith("lldltopartitions=")) {
- StringRef N = StringRef(S).substr(17);
+ if (S.startswith("lldltopartitions=")) {
+ StringRef N = S.substr(17);
if (N.getAsInteger(10, Config->LTOPartitions) ||
Config->LTOPartitions == 0)
error("/opt:lldltopartitions: invalid partition count: " + N);
@@ -862,6 +908,16 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
if (Args.hasArg(OPT_lldsavetemps))
Config->SaveTemps = true;
+ // Handle /lldltocache
+ if (auto *Arg = Args.getLastArg(OPT_lldltocache))
+ Config->LTOCache = Arg->getValue();
+
+ // Handle /lldsavecachepolicy
+ if (auto *Arg = Args.getLastArg(OPT_lldltocachepolicy))
+ Config->LTOCachePolicy = check(
+ parseCachePruningPolicy(Arg->getValue()),
+ Twine("/lldltocachepolicy: invalid cache policy: ") + Arg->getValue());
+
// Handle /failifmismatch
for (auto *Arg : Args.filtered(OPT_failifmismatch))
checkFailIfMismatch(Arg->getValue());
@@ -874,18 +930,29 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
for (auto *Arg : Args.filtered(OPT_section))
parseSection(Arg->getValue());
- // Handle /manifest
- if (auto *Arg = Args.getLastArg(OPT_manifest_colon))
- parseManifest(Arg->getValue());
+ // Handle /aligncomm
+ for (auto *Arg : Args.filtered(OPT_aligncomm))
+ parseAligncomm(Arg->getValue());
+
+ // Handle /manifestdependency. This enables /manifest unless /manifest:no is
+ // also passed.
+ if (auto *Arg = Args.getLastArg(OPT_manifestdependency)) {
+ Config->ManifestDependency = Arg->getValue();
+ Config->Manifest = Configuration::SideBySide;
+ }
+
+ // Handle /manifest and /manifest:
+ if (auto *Arg = Args.getLastArg(OPT_manifest, OPT_manifest_colon)) {
+ if (Arg->getOption().getID() == OPT_manifest)
+ Config->Manifest = Configuration::SideBySide;
+ else
+ parseManifest(Arg->getValue());
+ }
// Handle /manifestuac
if (auto *Arg = Args.getLastArg(OPT_manifestuac))
parseManifestUAC(Arg->getValue());
- // Handle /manifestdependency
- if (auto *Arg = Args.getLastArg(OPT_manifestdependency))
- Config->ManifestDependency = Arg->getValue();
-
// Handle /manifestfile
if (auto *Arg = Args.getLastArg(OPT_manifestfile))
Config->ManifestFile = Arg->getValue();
@@ -894,7 +961,14 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
for (auto *Arg : Args.filtered(OPT_manifestinput))
Config->ManifestInput.push_back(Arg->getValue());
+ if (!Config->ManifestInput.empty() &&
+ Config->Manifest != Configuration::Embed) {
+ fatal("/MANIFESTINPUT: requires /MANIFEST:EMBED");
+ }
+
// Handle miscellaneous boolean flags.
+ if (Args.hasArg(OPT_allowbind_no))
+ Config->AllowBind = false;
if (Args.hasArg(OPT_allowisolation_no))
Config->AllowIsolation = false;
if (Args.hasArg(OPT_dynamicbase_no))
@@ -911,19 +985,29 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
if (ErrorCount)
return;
+ bool WholeArchiveFlag = Args.hasArg(OPT_wholearchive_flag);
// Create a list of input files. Files can be given as arguments
// for /defaultlib option.
std::vector<MemoryBufferRef> MBs;
- for (auto *Arg : Args.filtered(OPT_INPUT))
- if (Optional<StringRef> Path = findFile(Arg->getValue()))
- enqueuePath(*Path);
+ for (auto *Arg : Args.filtered(OPT_INPUT, OPT_wholearchive_file)) {
+ switch (Arg->getOption().getID()) {
+ case OPT_INPUT:
+ if (Optional<StringRef> Path = findFile(Arg->getValue()))
+ enqueuePath(*Path, WholeArchiveFlag);
+ break;
+ case OPT_wholearchive_file:
+ if (Optional<StringRef> Path = findFile(Arg->getValue()))
+ enqueuePath(*Path, true);
+ break;
+ }
+ }
for (auto *Arg : Args.filtered(OPT_defaultlib))
if (Optional<StringRef> Path = findLib(Arg->getValue()))
- enqueuePath(*Path);
+ enqueuePath(*Path, false);
// Windows specific -- Create a resource file containing a manifest file.
if (Config->Manifest == Configuration::Embed)
- addBuffer(createManifestRes());
+ addBuffer(createManifestRes(), false);
// Read all input files given via the command line.
run();
@@ -939,7 +1023,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// WindowsResource to convert resource files to a regular COFF file,
// then link the resulting file normally.
if (!Resources.empty())
- addBuffer(convertResToCOFF(Resources));
+ Symtab->addFile(make<ObjFile>(convertResToCOFF(Resources)));
if (Tar)
Tar->append("response.txt",
@@ -990,9 +1074,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
}
// Handle generation of import library from a def file.
- if (!Args.hasArgNoClaim(OPT_INPUT)) {
+ if (!Args.hasArg(OPT_INPUT)) {
fixupExports();
- createImportLibrary();
+ createImportLibrary(/*AsLib=*/true);
exit(0);
}
@@ -1026,17 +1110,21 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
if (Config->ImageBase == uint64_t(-1))
Config->ImageBase = getDefaultImageBase();
- Symtab.addRelative(mangle("__ImageBase"), 0);
+ Symtab->addSynthetic(mangle("__ImageBase"), nullptr);
if (Config->Machine == I386) {
- Config->SEHTable = Symtab.addRelative("___safe_se_handler_table", 0);
- Config->SEHCount = Symtab.addAbsolute("___safe_se_handler_count", 0);
+ Symtab->addAbsolute("___safe_se_handler_table", 0);
+ Symtab->addAbsolute("___safe_se_handler_count", 0);
}
// We do not support /guard:cf (control flow protection) yet.
// Define CFG symbols anyway so that we can link MSVC 2015 CRT.
- Symtab.addAbsolute(mangle("__guard_fids_table"), 0);
- Symtab.addAbsolute(mangle("__guard_fids_count"), 0);
- Symtab.addAbsolute(mangle("__guard_flags"), 0x100);
+ Symtab->addAbsolute(mangle("__guard_fids_count"), 0);
+ Symtab->addAbsolute(mangle("__guard_fids_table"), 0);
+ Symtab->addAbsolute(mangle("__guard_flags"), 0x100);
+ Symtab->addAbsolute(mangle("__guard_iat_count"), 0);
+ Symtab->addAbsolute(mangle("__guard_iat_table"), 0);
+ Symtab->addAbsolute(mangle("__guard_longjmp_count"), 0);
+ Symtab->addAbsolute(mangle("__guard_longjmp_table"), 0);
// 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
@@ -1045,7 +1133,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Windows specific -- if entry point is not found,
// search for its mangled names.
if (Config->Entry)
- Symtab.mangleMaybe(Config->Entry);
+ Symtab->mangleMaybe(Config->Entry);
// Windows specific -- Make sure we resolve all dllexported symbols.
for (Export &E : Config->Exports) {
@@ -1053,7 +1141,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
continue;
E.Sym = addUndefined(E.Name);
if (!E.Directives)
- Symtab.mangleMaybe(E.Sym);
+ Symtab->mangleMaybe(E.Sym);
}
// Add weak aliases. Weak aliases is a mechanism to give remaining
@@ -1061,16 +1149,16 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
for (auto Pair : Config->AlternateNames) {
StringRef From = Pair.first;
StringRef To = Pair.second;
- Symbol *Sym = Symtab.find(From);
+ Symbol *Sym = Symtab->find(From);
if (!Sym)
continue;
if (auto *U = dyn_cast<Undefined>(Sym->body()))
if (!U->WeakAlias)
- U->WeakAlias = Symtab.addUndefined(To);
+ U->WeakAlias = Symtab->addUndefined(To);
}
// Windows specific -- if __load_config_used can be resolved, resolve it.
- if (Symtab.findUnderscore("_load_config_used"))
+ if (Symtab->findUnderscore("_load_config_used"))
addUndefined(mangle("_load_config_used"));
} while (run());
@@ -1086,11 +1174,13 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Do LTO by compiling bitcode input files to a set of native COFF files then
// link those files.
- Symtab.addCombinedLTOObjects();
+ Symtab->addCombinedLTOObjects();
run();
// Make sure we have resolved all symbols.
- Symtab.reportRemainingUndefines();
+ Symtab->reportRemainingUndefines();
+ if (ErrorCount)
+ return;
// Windows specific -- if no /subsystem is given, we need to infer
// that from entry point name.
@@ -1102,35 +1192,80 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Handle /safeseh.
if (Args.hasArg(OPT_safeseh)) {
- for (ObjectFile *File : Symtab.ObjectFiles)
+ for (ObjFile *File : ObjFile::Instances)
if (!File->SEHCompat)
error("/safeseh: " + File->getName() + " is not compatible with SEH");
if (ErrorCount)
return;
}
+ // In MinGW, all symbols are automatically exported if no symbols
+ // are chosen to be exported.
+ if (Config->DLL && ((Config->MinGW && Config->Exports.empty()) ||
+ Args.hasArg(OPT_export_all_symbols))) {
+ AutoExporter Exporter;
+
+ Symtab->forEachSymbol([=](Symbol *S) {
+ auto *Def = dyn_cast<Defined>(S->body());
+ if (!Exporter.shouldExport(Def))
+ return;
+ Export E;
+ E.Name = Def->getName();
+ E.Sym = Def;
+ Config->Exports.push_back(E);
+ });
+ }
+
// Windows specific -- when we are creating a .dll file, we also
// need to create a .lib file.
if (!Config->Exports.empty() || Config->DLL) {
fixupExports();
- createImportLibrary();
+ createImportLibrary(/*AsLib=*/false);
assignExportOrdinals();
}
+ // Handle /output-def (MinGW specific).
+ if (auto *Arg = Args.getLastArg(OPT_output_def))
+ writeDefFile(Arg->getValue());
+
+ // Set extra alignment for .comm symbols
+ for (auto Pair : Config->AlignComm) {
+ StringRef Name = Pair.first;
+ uint32_t Alignment = Pair.second;
+
+ Symbol *Sym = Symtab->find(Name);
+ if (!Sym) {
+ warn("/aligncomm symbol " + Name + " not found");
+ continue;
+ }
+
+ auto *DC = dyn_cast<DefinedCommon>(Sym->body());
+ if (!DC) {
+ warn("/aligncomm symbol " + Name + " of wrong kind");
+ continue;
+ }
+
+ CommonChunk *C = DC->getChunk();
+ C->Alignment = std::max(C->Alignment, Alignment);
+ }
+
// Windows specific -- Create a side-by-side manifest file.
if (Config->Manifest == Configuration::SideBySide)
createSideBySideManifest();
// Identify unreferenced COMDAT sections.
if (Config->DoGC)
- markLive(Symtab.getChunks());
+ markLive(Symtab->getChunks());
// Identify identical COMDAT sections to merge them.
if (Config->DoICF)
- doICF(Symtab.getChunks());
+ doICF(Symtab->getChunks());
// Write the result.
- writeResult(&Symtab);
+ writeResult();
+
+ if (ErrorCount)
+ return;
// Call exit to avoid calling destructors.
exit(0);
diff --git a/COFF/Driver.h b/COFF/Driver.h
index 3eb950cca..ff21a4280 100644
--- a/COFF/Driver.h
+++ b/COFF/Driver.h
@@ -12,8 +12,8 @@
#include "Config.h"
#include "SymbolTable.h"
-#include "lld/Core/LLVM.h"
-#include "lld/Core/Reproduce.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Common/Reproduce.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/Archive.h"
@@ -34,7 +34,6 @@ extern LinkerDriver *Driver;
using llvm::COFF::MachineTypes;
using llvm::COFF::WindowsSubsystem;
using llvm::Optional;
-class InputFile;
// Implemented in MarkLive.cpp.
void markLive(const std::vector<Chunk *> &Chunks);
@@ -42,26 +41,30 @@ void markLive(const std::vector<Chunk *> &Chunks);
// Implemented in ICF.cpp.
void doICF(const std::vector<Chunk *> &Chunks);
-class ArgParser {
+class COFFOptTable : public llvm::opt::OptTable {
public:
- // Parses command line options.
- llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> Args);
+ COFFOptTable();
+};
- // Concatenate LINK environment varirable and given arguments and parse them.
+class ArgParser {
+public:
+ // Concatenate LINK environment variable and given arguments and parse them.
llvm::opt::InputArgList parseLINK(std::vector<const char *> Args);
// Tokenizes a given string and then parses as command line options.
llvm::opt::InputArgList parse(StringRef S) { return parse(tokenize(S)); }
private:
+ // Parses command line options.
+ llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> Args);
+
std::vector<const char *> tokenize(StringRef S);
- std::vector<const char *> replaceResponseFiles(std::vector<const char *>);
+ COFFOptTable Table;
};
class LinkerDriver {
public:
- LinkerDriver() { coff::Symtab = &Symtab; }
void link(llvm::ArrayRef<const char *> Args);
// Used by the resolver to parse .drectve section contents.
@@ -72,9 +75,6 @@ public:
StringRef ParentName);
private:
- ArgParser Parser;
- SymbolTable Symtab;
-
std::unique_ptr<llvm::TarWriter> Tar; // for /linkrepro
// Opens a file. Path has to be resolved already.
@@ -110,11 +110,11 @@ private:
void invokeMSVC(llvm::opt::InputArgList &Args);
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> MB);
- void addBuffer(std::unique_ptr<MemoryBuffer> MB);
+ void addBuffer(std::unique_ptr<MemoryBuffer> MB, bool WholeArchive);
void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName,
StringRef ParentName);
- void enqueuePath(StringRef Path);
+ void enqueuePath(StringRef Path, bool WholeArchive);
void enqueueTask(std::function<void()> Task);
bool run();
@@ -146,6 +146,7 @@ void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major,
void parseAlternateName(StringRef);
void parseMerge(StringRef);
void parseSection(StringRef);
+void parseAligncomm(StringRef);
// Parses a string in the form of "EMBED[,=<integer>]|NO".
void parseManifest(StringRef Arg);
@@ -168,17 +169,15 @@ void assignExportOrdinals();
// incompatible objects.
void checkFailIfMismatch(StringRef Arg);
-// Convert Windows resource files (.res files) to a .obj file
-// using cvtres.exe.
-std::unique_ptr<MemoryBuffer>
-convertResToCOFF(const std::vector<MemoryBufferRef> &MBs);
+// Convert Windows resource files (.res files) to a .obj file.
+MemoryBufferRef convertResToCOFF(const std::vector<MemoryBufferRef> &MBs);
void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects);
// Create enum with OPT_xxx values for each option in Options.td
enum {
OPT_INVALID = 0,
-#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11) OPT_##ID,
+#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID,
#include "Options.inc"
#undef OPTION
};
diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp
index 96891646a..a7e1b4eb6 100644
--- a/COFF/DriverUtils.cpp
+++ b/COFF/DriverUtils.cpp
@@ -20,6 +20,7 @@
#include "Symbols.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/BinaryFormat/COFF.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/WindowsResource.h"
#include "llvm/Option/Arg.h"
@@ -27,21 +28,24 @@
#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/MathExtras.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/WindowsManifest/WindowsManifestMerger.h"
#include <memory>
using namespace llvm::COFF;
using namespace llvm;
-using llvm::cl::ExpandResponseFiles;
-using llvm::cl::TokenizeWindowsCommandLine;
using llvm::sys::Process;
namespace lld {
namespace coff {
namespace {
+const uint16_t SUBLANG_ENGLISH_US = 0x0409;
+const uint16_t RT_MANIFEST = 24;
+
class Executor {
public:
explicit Executor(StringRef S) : Prog(Saver.save(S)) {}
@@ -53,7 +57,7 @@ public:
void run() {
ErrorOr<std::string> ExeOrErr = sys::findProgramByName(Prog);
if (auto EC = ExeOrErr.getError())
- fatal(EC, "unable to find " + Prog + " in PATH: ");
+ fatal(EC, "unable to find " + Prog + " in PATH");
StringRef Exe = Saver.save(*ExeOrErr);
Args.insert(Args.begin(), Exe);
@@ -80,6 +84,7 @@ MachineTypes getMachineType(StringRef S) {
.Cases("x64", "amd64", AMD64)
.Cases("x86", "i386", I386)
.Case("arm", ARMNT)
+ .Case("arm64", ARM64)
.Default(IMAGE_FILE_MACHINE_UNKNOWN);
if (MT != IMAGE_FILE_MACHINE_UNKNOWN)
return MT;
@@ -90,6 +95,8 @@ StringRef machineToStr(MachineTypes MT) {
switch (MT) {
case ARMNT:
return "arm";
+ case ARM64:
+ return "arm64";
case AMD64:
return "x64";
case I386:
@@ -213,6 +220,22 @@ void parseSection(StringRef S) {
Config->Section[Name] = parseSectionAttributes(Attrs);
}
+// Parses /aligncomm option argument.
+void parseAligncomm(StringRef S) {
+ StringRef Name, Align;
+ std::tie(Name, Align) = S.split(',');
+ if (Name.empty() || Align.empty()) {
+ error("/aligncomm: invalid argument: " + S);
+ return;
+ }
+ int V;
+ if (Align.getAsInteger(0, V)) {
+ error("/aligncomm: invalid argument: " + S);
+ return;
+ }
+ Config->AlignComm[Name] = std::max(Config->AlignComm[Name], 1 << V);
+}
+
// Parses a string in the form of "EMBED[,=<integer>]|NO".
// Results are directly written to Config.
void parseManifest(StringRef Arg) {
@@ -258,26 +281,6 @@ void parseManifestUAC(StringRef Arg) {
}
}
-// Quote each line with "". Existing double-quote is converted
-// to two double-quotes.
-static void quoteAndPrint(raw_ostream &Out, StringRef S) {
- while (!S.empty()) {
- StringRef Line;
- std::tie(Line, S) = S.split("\n");
- if (Line.empty())
- continue;
- Out << '\"';
- for (int I = 0, E = Line.size(); I != E; ++I) {
- if (Line[I] == '\"') {
- Out << "\"\"";
- } else {
- Out << Line[I];
- }
- }
- Out << "\"\n";
- }
-}
-
// An RAII temporary file class that automatically removes a temporary file.
namespace {
class TemporaryFile {
@@ -324,16 +327,9 @@ public:
};
}
-// Create the default manifest file as a temporary file.
-TemporaryFile createDefaultXml() {
- // Create a temporary file.
- TemporaryFile File("defaultxml", "manifest");
-
- // Open the temporary file for writing.
- std::error_code EC;
- raw_fd_ostream OS(File.Path, EC, sys::fs::F_Text);
- if (EC)
- fatal(EC, "failed to open " + File.Path);
+static std::string createDefaultXml() {
+ std::string Ret;
+ raw_string_ostream OS(Ret);
// Emit the XML. Note that we do *not* verify that the XML attributes are
// syntactically correct. This is intentional for link.exe compatibility.
@@ -349,80 +345,134 @@ TemporaryFile createDefaultXml() {
<< " </requestedPrivileges>\n"
<< " </security>\n"
<< " </trustInfo>\n";
- if (!Config->ManifestDependency.empty()) {
- OS << " <dependency>\n"
- << " <dependentAssembly>\n"
- << " <assemblyIdentity " << Config->ManifestDependency << " />\n"
- << " </dependentAssembly>\n"
- << " </dependency>\n";
- }
+ }
+ if (!Config->ManifestDependency.empty()) {
+ OS << " <dependency>\n"
+ << " <dependentAssembly>\n"
+ << " <assemblyIdentity " << Config->ManifestDependency << " />\n"
+ << " </dependentAssembly>\n"
+ << " </dependency>\n";
}
OS << "</assembly>\n";
- OS.close();
- return File;
+ return OS.str();
}
-static std::string readFile(StringRef Path) {
- std::unique_ptr<MemoryBuffer> MB =
- check(MemoryBuffer::getFile(Path), "could not open " + Path);
- return MB->getBuffer();
+static std::string createManifestXmlWithInternalMt(StringRef DefaultXml) {
+ std::unique_ptr<MemoryBuffer> DefaultXmlCopy =
+ MemoryBuffer::getMemBufferCopy(DefaultXml);
+
+ windows_manifest::WindowsManifestMerger Merger;
+ if (auto E = Merger.merge(*DefaultXmlCopy.get()))
+ fatal(E, "internal manifest tool failed on default xml");
+
+ for (StringRef Filename : Config->ManifestInput) {
+ std::unique_ptr<MemoryBuffer> Manifest =
+ check(MemoryBuffer::getFile(Filename));
+ if (auto E = Merger.merge(*Manifest.get()))
+ fatal(E, "internal manifest tool failed on file " + Filename);
+ }
+
+ return Merger.getMergedManifest().get()->getBuffer();
}
-static std::string createManifestXml() {
- // Create the default manifest file.
- TemporaryFile File1 = createDefaultXml();
- if (Config->ManifestInput.empty())
- return readFile(File1.Path);
+static std::string createManifestXmlWithExternalMt(StringRef DefaultXml) {
+ // Create the default manifest file as a temporary file.
+ TemporaryFile Default("defaultxml", "manifest");
+ std::error_code EC;
+ raw_fd_ostream OS(Default.Path, EC, sys::fs::F_Text);
+ if (EC)
+ fatal(EC, "failed to open " + Default.Path);
+ OS << DefaultXml;
+ OS.close();
- // If manifest files are supplied by the user using /MANIFESTINPUT
- // option, we need to merge them with the default manifest.
- TemporaryFile File2("user", "manifest");
+ // Merge user-supplied manifests if they are given. Since libxml2 is not
+ // enabled, we must shell out to Microsoft's mt.exe tool.
+ TemporaryFile User("user", "manifest");
Executor E("mt.exe");
E.add("/manifest");
- E.add(File1.Path);
+ E.add(Default.Path);
for (StringRef Filename : Config->ManifestInput) {
E.add("/manifest");
E.add(Filename);
}
E.add("/nologo");
- E.add("/out:" + StringRef(File2.Path));
+ E.add("/out:" + StringRef(User.Path));
E.run();
- return readFile(File2.Path);
+
+ return check(MemoryBuffer::getFile(User.Path), "could not open " + User.Path)
+ .get()
+ ->getBuffer();
+}
+
+static std::string createManifestXml() {
+ std::string DefaultXml = createDefaultXml();
+ if (Config->ManifestInput.empty())
+ return DefaultXml;
+
+ if (windows_manifest::isAvailable())
+ return createManifestXmlWithInternalMt(DefaultXml);
+
+ return createManifestXmlWithExternalMt(DefaultXml);
+}
+
+static std::unique_ptr<MemoryBuffer>
+createMemoryBufferForManifestRes(size_t ManifestSize) {
+ size_t ResSize = alignTo(
+ object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE +
+ sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) +
+ sizeof(object::WinResHeaderSuffix) + ManifestSize,
+ object::WIN_RES_DATA_ALIGNMENT);
+ return MemoryBuffer::getNewMemBuffer(ResSize,
+ Config->OutputFile + ".manifest.res");
+}
+
+static void writeResFileHeader(char *&Buf) {
+ memcpy(Buf, COFF::WinResMagic, sizeof(COFF::WinResMagic));
+ Buf += sizeof(COFF::WinResMagic);
+ memset(Buf, 0, object::WIN_RES_NULL_ENTRY_SIZE);
+ Buf += object::WIN_RES_NULL_ENTRY_SIZE;
+}
+
+static void writeResEntryHeader(char *&Buf, size_t ManifestSize) {
+ // Write the prefix.
+ auto *Prefix = reinterpret_cast<object::WinResHeaderPrefix *>(Buf);
+ Prefix->DataSize = ManifestSize;
+ Prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) +
+ sizeof(object::WinResIDs) +
+ sizeof(object::WinResHeaderSuffix);
+ Buf += sizeof(object::WinResHeaderPrefix);
+
+ // Write the Type/Name IDs.
+ auto *IDs = reinterpret_cast<object::WinResIDs *>(Buf);
+ IDs->setType(RT_MANIFEST);
+ IDs->setName(Config->ManifestID);
+ Buf += sizeof(object::WinResIDs);
+
+ // Write the suffix.
+ auto *Suffix = reinterpret_cast<object::WinResHeaderSuffix *>(Buf);
+ Suffix->DataVersion = 0;
+ Suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE;
+ Suffix->Language = SUBLANG_ENGLISH_US;
+ Suffix->Version = 0;
+ Suffix->Characteristics = 0;
+ Buf += sizeof(object::WinResHeaderSuffix);
}
// Create a resource file containing a manifest XML.
std::unique_ptr<MemoryBuffer> createManifestRes() {
- // Create a temporary file for the resource script file.
- TemporaryFile RCFile("manifest", "rc");
+ std::string Manifest = createManifestXml();
- // Open the temporary file for writing.
- std::error_code EC;
- raw_fd_ostream Out(RCFile.Path, EC, sys::fs::F_Text);
- if (EC)
- fatal(EC, "failed to open " + RCFile.Path);
-
- // Write resource script to the RC file.
- Out << "#define LANG_ENGLISH 9\n"
- << "#define SUBLANG_DEFAULT 1\n"
- << "#define APP_MANIFEST " << Config->ManifestID << "\n"
- << "#define RT_MANIFEST 24\n"
- << "LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT\n"
- << "APP_MANIFEST RT_MANIFEST {\n";
- quoteAndPrint(Out, createManifestXml());
- Out << "}\n";
- Out.close();
-
- // Create output resource file.
- TemporaryFile ResFile("output-resource", "res");
-
- Executor E("rc.exe");
- E.add("/fo");
- E.add(ResFile.Path);
- E.add("/nologo");
- E.add(RCFile.Path);
- E.run();
- return ResFile.getMemoryBuffer();
+ std::unique_ptr<MemoryBuffer> Res =
+ createMemoryBufferForManifestRes(Manifest.size());
+
+ char *Buf = const_cast<char *>(Res->getBufferStart());
+ writeResFileHeader(Buf);
+ writeResEntryHeader(Buf, Manifest.size());
+
+ // Copy the manifest data into the .res file.
+ std::copy(Manifest.begin(), Manifest.end(), Buf);
+ return Res;
}
void createSideBySideManifest() {
@@ -447,12 +497,12 @@ Export parseExport(StringRef Arg) {
if (E.Name.empty())
goto err;
- if (E.Name.find('=') != StringRef::npos) {
+ if (E.Name.contains('=')) {
StringRef X, Y;
std::tie(X, Y) = E.Name.split("=");
// If "<name>=<dllname>.<name>".
- if (Y.find(".") != StringRef::npos) {
+ if (Y.contains(".")) {
E.Name = X;
E.ForwardTo = Y;
return E;
@@ -589,10 +639,8 @@ void checkFailIfMismatch(StringRef Arg) {
Config->MustMatch[K] = V;
}
-// Convert Windows resource files (.res files) to a .obj file
-// using cvtres.exe.
-std::unique_ptr<MemoryBuffer>
-convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) {
+// Convert Windows resource files (.res files) to a .obj file.
+MemoryBufferRef convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) {
object::WindowsResourceParser Parser;
for (MemoryBufferRef MB : MBs) {
@@ -604,12 +652,14 @@ convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) {
fatal(EC, "failed to parse .res file");
}
- std::unique_ptr<MemoryBuffer> MB;
- if (auto EC =
- llvm::object::writeWindowsResourceCOFF(MB, Config->Machine, Parser))
- fatal(EC, "failed to write resources to buffer");
+ Expected<std::unique_ptr<MemoryBuffer>> E =
+ llvm::object::writeWindowsResourceCOFF(Config->Machine, Parser);
+ if (!E)
+ fatal(errorToErrorCode(E.takeError()), "failed to write .res to COFF");
- return MB;
+ MemoryBufferRef MBRef = **E;
+ make<std::unique_ptr<MemoryBuffer>>(std::move(*E)); // take ownership
+ return MBRef;
}
// Run MSVC link.exe for given in-memory object files.
@@ -640,36 +690,50 @@ void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects) {
#undef PREFIX
// Create table mapping all options defined in Options.td
-static const llvm::opt::OptTable::Info infoTable[] = {
-#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X6, X7, X8, X9, X10) \
- { \
- X1, X2, X9, X10, OPT_##ID, llvm::opt::Option::KIND##Class, X8, X7, \
- OPT_##GROUP, OPT_##ALIAS, X6 \
- },
+static const llvm::opt::OptTable::Info InfoTable[] = {
+#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
+ {X1, X2, X10, X11, OPT_##ID, llvm::opt::Option::KIND##Class, \
+ X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12},
#include "Options.inc"
#undef OPTION
};
-class COFFOptTable : public llvm::opt::OptTable {
-public:
- COFFOptTable() : OptTable(infoTable, true) {}
-};
+COFFOptTable::COFFOptTable() : OptTable(InfoTable, true) {}
-// Parses a given list of options.
-opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) {
- // First, replace respnose files (@<file>-style options).
- std::vector<const char *> Argv = replaceResponseFiles(ArgsArr);
+static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) {
+ if (auto *Arg = Args.getLastArg(OPT_rsp_quoting)) {
+ StringRef S = Arg->getValue();
+ if (S != "windows" && S != "posix")
+ error("invalid response file quoting: " + S);
+ if (S == "windows")
+ return cl::TokenizeWindowsCommandLine;
+ return cl::TokenizeGNUCommandLine;
+ }
+ // The COFF linker always defaults to Windows quoting.
+ return cl::TokenizeWindowsCommandLine;
+}
+// Parses a given list of options.
+opt::InputArgList ArgParser::parse(ArrayRef<const char *> Argv) {
// Make InputArgList from string vectors.
- COFFOptTable Table;
unsigned MissingIndex;
unsigned MissingCount;
- opt::InputArgList Args = Table.ParseArgs(Argv, MissingIndex, MissingCount);
+ SmallVector<const char *, 256> Vec(Argv.data(), Argv.data() + Argv.size());
+
+ // We need to get the quoting style for response files before parsing all
+ // options so we parse here before and ignore all the options but
+ // --rsp-quoting.
+ opt::InputArgList Args = Table.ParseArgs(Vec, MissingIndex, MissingCount);
+
+ // Expand response files (arguments in the form of @<filename>)
+ // and then parse the argument again.
+ cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec);
+ Args = Table.ParseArgs(Vec, MissingIndex, MissingCount);
// Print the real command line if response files are expanded.
- if (Args.hasArg(OPT_verbose) && ArgsArr.size() != Argv.size()) {
+ if (Args.hasArg(OPT_verbose) && Argv.size() != Vec.size()) {
std::string Msg = "Command line:";
- for (const char *S : Argv)
+ for (const char *S : Vec)
Msg += " " + std::string(S);
message(Msg);
}
@@ -684,17 +748,17 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) {
// link.exe has an interesting feature. If LINK or _LINK_ environment
// variables exist, their contents are handled as command line strings.
// So you can pass extra arguments using them.
-opt::InputArgList ArgParser::parseLINK(std::vector<const char *> Args) {
+opt::InputArgList ArgParser::parseLINK(std::vector<const char *> Argv) {
// Concatenate LINK env and command line arguments, and then parse them.
if (Optional<std::string> S = Process::GetEnv("LINK")) {
std::vector<const char *> V = tokenize(*S);
- Args.insert(Args.begin(), V.begin(), V.end());
+ Argv.insert(Argv.begin(), V.begin(), V.end());
}
if (Optional<std::string> S = Process::GetEnv("_LINK_")) {
std::vector<const char *> V = tokenize(*S);
- Args.insert(Args.begin(), V.begin(), V.end());
+ Argv.insert(Argv.begin(), V.begin(), V.end());
}
- return parse(Args);
+ return parse(Argv);
}
std::vector<const char *> ArgParser::tokenize(StringRef S) {
@@ -703,15 +767,6 @@ std::vector<const char *> ArgParser::tokenize(StringRef S) {
return std::vector<const char *>(Tokens.begin(), Tokens.end());
}
-// Creates a new command line by replacing options starting with '@'
-// character. '@<filename>' is replaced by the file's contents.
-std::vector<const char *>
-ArgParser::replaceResponseFiles(std::vector<const char *> Argv) {
- SmallVector<const char *, 256> Tokens(Argv.data(), Argv.data() + Argv.size());
- ExpandResponseFiles(Saver, TokenizeWindowsCommandLine, Tokens);
- return std::vector<const char *>(Tokens.begin(), Tokens.end());
-}
-
void printHelp(const char *Argv0) {
COFFOptTable Table;
Table.PrintHelp(outs(), Argv0, "LLVM Linker", false);
diff --git a/COFF/Error.cpp b/COFF/Error.cpp
index 166b1971e..34abc280f 100644
--- a/COFF/Error.cpp
+++ b/COFF/Error.cpp
@@ -29,7 +29,6 @@ namespace lld {
static std::mutex Mu;
namespace coff {
-StringRef Argv0;
uint64_t ErrorCount;
raw_ostream *ErrorOS;
@@ -45,7 +44,7 @@ static LLVM_ATTRIBUTE_NORETURN void exitLld(int Val) {
}
static void print(StringRef S, raw_ostream::Colors C) {
- *ErrorOS << Argv0 + ": ";
+ *ErrorOS << Config->Argv[0] << ": ";
if (Config->ColorDiagnostics) {
ErrorOS->changeColor(C, true);
*ErrorOS << S;
@@ -58,7 +57,7 @@ static void print(StringRef S, raw_ostream::Colors C) {
void log(const Twine &Msg) {
if (Config->Verbose) {
std::lock_guard<std::mutex> Lock(Mu);
- outs() << Argv0 << ": " << Msg << "\n";
+ outs() << Config->Argv[0] << ": " << Msg << "\n";
outs().flush();
}
}
diff --git a/COFF/Error.h b/COFF/Error.h
index a4f44fb1e..1cc2dd4ed 100644
--- a/COFF/Error.h
+++ b/COFF/Error.h
@@ -10,7 +10,7 @@
#ifndef LLD_COFF_ERROR_H
#define LLD_COFF_ERROR_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/Support/Error.h"
namespace lld {
@@ -18,7 +18,6 @@ namespace coff {
extern uint64_t ErrorCount;
extern llvm::raw_ostream *ErrorOS;
-extern llvm::StringRef Argv0;
void log(const Twine &Msg);
void message(const Twine &Msg);
diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp
index da8ca3605..1032fb0ec 100644
--- a/COFF/ICF.cpp
+++ b/COFF/ICF.cpp
@@ -61,12 +61,9 @@ private:
// Returns a hash value for S.
uint32_t ICF::getHash(SectionChunk *C) {
- return hash_combine(C->getPermissions(),
- hash_value(C->SectionName),
- C->NumRelocs,
- C->getAlign(),
- uint32_t(C->Header->SizeOfRawData),
- C->Checksum);
+ return hash_combine(C->getPermissions(), C->SectionName, C->NumRelocs,
+ C->Alignment, uint32_t(C->Header->SizeOfRawData),
+ C->Checksum, C->getContents());
}
// Returns true if section S is subject of ICF.
@@ -137,11 +134,9 @@ bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
// Compare section attributes and contents.
return A->getPermissions() == B->getPermissions() &&
- A->SectionName == B->SectionName &&
- A->getAlign() == B->getAlign() &&
+ A->SectionName == B->SectionName && A->Alignment == B->Alignment &&
A->Header->SizeOfRawData == B->Header->SizeOfRawData &&
- A->Checksum == B->Checksum &&
- A->getContents() == B->getContents();
+ A->Checksum == B->Checksum && A->getContents() == B->getContents();
}
// Compare "moving" part of two sections, namely relocation targets.
@@ -215,9 +210,10 @@ void ICF::run(const std::vector<Chunk *> &Vec) {
}
// Initially, we use hash values to partition sections.
- for (SectionChunk *SC : Chunks)
+ for_each(parallel::par, Chunks.begin(), Chunks.end(), [&](SectionChunk *SC) {
// Set MSB to 1 to avoid collisions with non-hash classs.
SC->Class[0] = getHash(SC) | (1 << 31);
+ });
// From now on, sections in Chunks are ordered so that sections in
// the same group are consecutive in the vector.
diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp
index 8271dfed0..ef2938697 100644
--- a/COFF/InputFiles.cpp
+++ b/COFF/InputFiles.cpp
@@ -43,6 +43,10 @@ using llvm::support::ulittle32_t;
namespace lld {
namespace coff {
+std::vector<ObjFile *> ObjFile::Instances;
+std::vector<ImportFile *> ImportFile::Instances;
+std::vector<BitcodeFile *> BitcodeFile::Instances;
+
/// Checks that Source is compatible with being a weak alias to Target.
/// If Source is Undefined and has no weak alias set, makes it a weak
/// alias to Target.
@@ -79,7 +83,26 @@ void ArchiveFile::addMember(const Archive::Symbol *Sym) {
Driver->enqueueArchiveMember(C, Sym->getName(), getName());
}
-void ObjectFile::parse() {
+std::vector<MemoryBufferRef> getArchiveMembers(Archive *File) {
+ std::vector<MemoryBufferRef> V;
+ Error Err = Error::success();
+ for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
+ Archive::Child C =
+ check(COrErr,
+ File->getFileName() + ": could not get the child of the archive");
+ MemoryBufferRef MBRef =
+ check(C.getMemoryBufferRef(),
+ File->getFileName() +
+ ": could not get the buffer for a child of the archive");
+ V.push_back(MBRef);
+ }
+ if (Err)
+ fatal(File->getFileName() +
+ ": Archive::children failed: " + toString(std::move(Err)));
+ return V;
+}
+
+void ObjFile::parse() {
// Parse a memory buffer as a COFF file.
std::unique_ptr<Binary> Bin = check(createBinary(MB), toString(this));
@@ -96,7 +119,7 @@ void ObjectFile::parse() {
initializeSEH();
}
-void ObjectFile::initializeChunks() {
+void ObjFile::initializeChunks() {
uint32_t NumSections = COFFObj->getNumberOfSections();
Chunks.reserve(NumSections);
SparseChunks.resize(NumSections + 1);
@@ -132,22 +155,22 @@ void ObjectFile::initializeChunks() {
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(make<SectionChunk>(this, Sec));
- continue;
- }
-
if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
continue;
auto *C = make<SectionChunk>(this, Sec);
- Chunks.push_back(C);
+
+ // CodeView sections are stored to a different vector because they are not
+ // linked in the regular manner.
+ if (C->isCodeView())
+ DebugChunks.push_back(C);
+ else
+ Chunks.push_back(C);
+
SparseChunks[I] = C;
}
}
-void ObjectFile::initializeSymbols() {
+void ObjFile::initializeSymbols() {
uint32_t NumSymbols = COFFObj->getNumberOfSymbols();
SymbolBodies.reserve(NumSymbols);
SparseSymbolBodies.resize(NumSymbols);
@@ -157,14 +180,11 @@ void ObjectFile::initializeSymbols() {
for (uint32_t I = 0; I < NumSymbols; ++I) {
// Get a COFFSymbolRef object.
- ErrorOr<COFFSymbolRef> SymOrErr = COFFObj->getSymbol(I);
- if (!SymOrErr)
- fatal(SymOrErr.getError(), "broken object file: " + toString(this));
- COFFSymbolRef Sym = *SymOrErr;
+ COFFSymbolRef Sym = check(COFFObj->getSymbol(I));
const void *AuxP = nullptr;
if (Sym.getNumberOfAuxSymbols())
- AuxP = COFFObj->getSymbol(I + 1)->getRawPtr();
+ AuxP = check(COFFObj->getSymbol(I + 1)).getRawPtr();
bool IsFirst = (LastSectionNumber != Sym.getSectionNumber());
SymbolBody *Body = nullptr;
@@ -193,14 +213,14 @@ void ObjectFile::initializeSymbols() {
}
}
-SymbolBody *ObjectFile::createUndefined(COFFSymbolRef Sym) {
+SymbolBody *ObjFile::createUndefined(COFFSymbolRef Sym) {
StringRef Name;
COFFObj->getSymbolName(Sym, Name);
return Symtab->addUndefined(Name, this, Sym.isWeakExternal())->body();
}
-SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
- bool IsFirst) {
+SymbolBody *ObjFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
+ bool IsFirst) {
StringRef Name;
if (Sym.isCommon()) {
auto *C = make<CommonChunk>(Sym);
@@ -273,7 +293,7 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
return B;
}
-void ObjectFile::initializeSEH() {
+void ObjFile::initializeSEH() {
if (!SEHCompat || !SXData)
return;
ArrayRef<uint8_t> A;
@@ -286,7 +306,7 @@ void ObjectFile::initializeSEH() {
SEHandlers.insert(SparseSymbolBodies[*I]);
}
-MachineTypes ObjectFile::getMachineType() {
+MachineTypes ObjFile::getMachineType() {
if (COFFObj)
return static_cast<MachineTypes>(COFFObj->getMachine());
return IMAGE_FILE_MACHINE_UNKNOWN;
@@ -332,19 +352,16 @@ void ImportFile::parse() {
this->Hdr = Hdr;
ExternalName = ExtName;
- ImpSym = cast<DefinedImportData>(
- Symtab->addImportData(ImpName, this)->body());
+ ImpSym = Symtab->addImportData(ImpName, this);
+
if (Hdr->getType() == llvm::COFF::IMPORT_CONST)
- ConstSym =
- cast<DefinedImportData>(Symtab->addImportData(Name, this)->body());
+ static_cast<void>(Symtab->addImportData(Name, this));
// 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 = cast<DefinedImportThunk>(
- Symtab->addImportThunk(Name, ImpSym, Hdr->Machine)->body());
+ if (Hdr->getType() == llvm::COFF::IMPORT_CODE)
+ ThunkSym = Symtab->addImportThunk(Name, ImpSym, Hdr->Machine);
}
void BitcodeFile::parse() {
@@ -380,6 +397,8 @@ MachineTypes BitcodeFile::getMachineType() {
return I386;
case Triple::arm:
return ARMNT;
+ case Triple::aarch64:
+ return ARM64;
default:
return IMAGE_FILE_MACHINE_UNKNOWN;
}
diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h
index 99868d999..aa1a0e1dd 100644
--- a/COFF/InputFiles.h
+++ b/COFF/InputFiles.h
@@ -11,7 +11,7 @@
#define LLD_COFF_INPUT_FILES_H
#include "Config.h"
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/LTO/LTO.h"
@@ -31,6 +31,8 @@ class DbiModuleDescriptorBuilder;
namespace lld {
namespace coff {
+std::vector<MemoryBufferRef> getArchiveMembers(llvm::object::Archive *File);
+
using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN;
using llvm::COFF::MachineTypes;
using llvm::object::Archive;
@@ -101,9 +103,9 @@ private:
};
// .obj or .o file. This may be a member of an archive file.
-class ObjectFile : public InputFile {
+class ObjFile : public InputFile {
public:
- explicit ObjectFile(MemoryBufferRef M) : InputFile(ObjectKind, M) {}
+ explicit ObjFile(MemoryBufferRef M) : InputFile(ObjectKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == ObjectKind; }
void parse() override;
MachineTypes getMachineType() override;
@@ -120,6 +122,8 @@ public:
// Returns the underying COFF file.
COFFObjectFile *getCOFFObj() { return COFFObj.get(); }
+ static std::vector<ObjFile *> Instances;
+
// True if this object file is compatible with SEH.
// COFF-specific and x86-only.
bool SEHCompat = false;
@@ -179,8 +183,9 @@ public:
static bool classof(const InputFile *F) { return F->kind() == ImportKind; }
+ static std::vector<ImportFile *> Instances;
+
DefinedImportData *ImpSym = nullptr;
- DefinedImportData *ConstSym = nullptr;
DefinedImportThunk *ThunkSym = nullptr;
std::string DLLName;
@@ -208,6 +213,7 @@ public:
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
std::vector<SymbolBody *> &getSymbols() { return SymbolBodies; }
MachineTypes getMachineType() override;
+ static std::vector<BitcodeFile *> Instances;
std::unique_ptr<llvm::lto::InputFile> Obj;
private:
diff --git a/COFF/LTO.cpp b/COFF/LTO.cpp
index 6883b3b4c..f3c8bfbea 100644
--- a/COFF/LTO.cpp
+++ b/COFF/LTO.cpp
@@ -12,12 +12,13 @@
#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
-#include "lld/Core/TargetOptionsCommandFlags.h"
+#include "lld/Common/TargetOptionsCommandFlags.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/LTO/Caching.h"
#include "llvm/LTO/Config.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Object/SymbolicFile.h"
@@ -48,10 +49,8 @@ static void diagnosticHandler(const DiagnosticInfo &DI) {
}
static void checkError(Error E) {
- handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) -> Error {
- error(EIB.message());
- return Error::success();
- });
+ handleAllErrors(std::move(E),
+ [&](ErrorInfoBase &EIB) { error(EIB.message()); });
}
static void saveBuffer(StringRef Buffer, const Twine &Path) {
@@ -65,7 +64,13 @@ static void saveBuffer(StringRef Buffer, const Twine &Path) {
static std::unique_ptr<lto::LTO> createLTO() {
lto::Config Conf;
Conf.Options = InitTargetOptionsFromCodeGenFlags();
- Conf.RelocModel = Reloc::PIC_;
+ // Use static reloc model on 32-bit x86 because it usually results in more
+ // compact code, and because there are also known code generation bugs when
+ // using the PIC model (see PR34306).
+ if (Config->Machine == COFF::IMAGE_FILE_MACHINE_I386)
+ Conf.RelocModel = Reloc::Static;
+ else
+ Conf.RelocModel = Reloc::PIC_;
Conf.DisableVerify = true;
Conf.DiagHandler = diagnosticHandler;
Conf.OptLevel = Config->LTOOptLevel;
@@ -118,11 +123,27 @@ void BitcodeCompiler::add(BitcodeFile &F) {
std::vector<StringRef> BitcodeCompiler::compile() {
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]));
- }));
+ Files.resize(MaxTasks);
+
+ // The /lldltocache option specifies the path to a directory in which to cache
+ // native object files for ThinLTO incremental builds. If a path was
+ // specified, configure LTO to use it as the cache directory.
+ lto::NativeObjectCache Cache;
+ if (!Config->LTOCache.empty())
+ Cache = check(
+ lto::localCache(Config->LTOCache,
+ [&](size_t Task, std::unique_ptr<MemoryBuffer> MB,
+ StringRef Path) { Files[Task] = std::move(MB); }));
+
+ checkError(LTOObj->run(
+ [&](size_t Task) {
+ return llvm::make_unique<lto::NativeObjectStream>(
+ llvm::make_unique<raw_svector_ostream>(Buff[Task]));
+ },
+ Cache));
+
+ if (!Config->LTOCache.empty())
+ pruneCache(Config->LTOCache, Config->LTOCachePolicy);
std::vector<StringRef> Ret;
for (unsigned I = 0; I != MaxTasks; ++I) {
@@ -136,5 +157,10 @@ std::vector<StringRef> BitcodeCompiler::compile() {
}
Ret.emplace_back(Buff[I].data(), Buff[I].size());
}
+
+ for (std::unique_ptr<MemoryBuffer> &File : Files)
+ if (File)
+ Ret.push_back(File->getBuffer());
+
return Ret;
}
diff --git a/COFF/LTO.h b/COFF/LTO.h
index 194a4cce8..a444aa7ac 100644
--- a/COFF/LTO.h
+++ b/COFF/LTO.h
@@ -21,7 +21,7 @@
#ifndef LLD_COFF_LTO_H
#define LLD_COFF_LTO_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/ADT/SmallString.h"
#include <memory>
#include <vector>
@@ -49,6 +49,7 @@ public:
private:
std::unique_ptr<llvm::lto::LTO> LTOObj;
std::vector<SmallString<0>> Buff;
+ std::vector<std::unique_ptr<MemoryBuffer>> Files;
};
}
}
diff --git a/COFF/MapFile.cpp b/COFF/MapFile.cpp
index b63d4672c..076f8637c 100644
--- a/COFF/MapFile.cpp
+++ b/COFF/MapFile.cpp
@@ -48,7 +48,7 @@ static std::string indent(int Depth) { return std::string(Depth * 8, ' '); }
// Returns a list of all symbols that we want to print out.
static std::vector<DefinedRegular *> getSymbols() {
std::vector<DefinedRegular *> V;
- for (coff::ObjectFile *File : Symtab->ObjectFiles)
+ for (ObjFile *File : ObjFile::Instances)
for (SymbolBody *B : File->getSymbols())
if (auto *Sym = dyn_cast<DefinedRegular>(B))
if (Sym && !Sym->getCOFFSymbol().isSectionDefinition())
@@ -115,7 +115,7 @@ void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) {
if (!SC)
continue;
- writeHeader(OS, SC->getRVA(), SC->getSize(), SC->getAlign());
+ writeHeader(OS, SC->getRVA(), SC->getSize(), SC->Alignment);
OS << indent(1) << SC->File->getName() << ":(" << SC->getSectionName()
<< ")\n";
for (DefinedRegular *Sym : SectionSyms[SC])
diff --git a/COFF/MarkLive.cpp b/COFF/MarkLive.cpp
index 25e5cc350..a2756e5c8 100644
--- a/COFF/MarkLive.cpp
+++ b/COFF/MarkLive.cpp
@@ -52,6 +52,13 @@ void markLive(const std::vector<Chunk *> &Chunks) {
while (!Worklist.empty()) {
SectionChunk *SC = Worklist.pop_back_val();
+
+ // If this section was discarded, there are relocations referring to
+ // discarded sections. Ignore these sections to avoid crashing. They will be
+ // diagnosed during relocation processing.
+ if (SC->isDiscarded())
+ continue;
+
assert(SC->isLive() && "We mark as live when pushing onto the worklist!");
// Mark all symbols listed in the relocation table for this section.
diff --git a/COFF/MinGW.cpp b/COFF/MinGW.cpp
new file mode 100644
index 000000000..6943561b0
--- /dev/null
+++ b/COFF/MinGW.cpp
@@ -0,0 +1,119 @@
+//===- MinGW.cpp ----------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "MinGW.h"
+#include "Error.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace lld;
+using namespace lld::coff;
+using namespace llvm;
+using namespace llvm::COFF;
+
+AutoExporter::AutoExporter() {
+ if (Config->Machine == I386)
+ ExcludeSymbols = {
+ "__NULL_IMPORT_DESCRIPTOR",
+ "__pei386_runtime_relocator",
+ "_do_pseudo_reloc",
+ "_impure_ptr",
+ "__impure_ptr",
+ "__fmode",
+ "_environ",
+ "___dso_handle",
+ // These are the MinGW names that differ from the standard
+ // ones (lacking an extra underscore).
+ "_DllMain@12",
+ "_DllEntryPoint@12",
+ "_DllMainCRTStartup@12",
+ };
+ else
+ ExcludeSymbols = {
+ "_NULL_IMPORT_DESCRIPTOR",
+ "_pei386_runtime_relocator",
+ "do_pseudo_reloc",
+ "impure_ptr",
+ "_impure_ptr",
+ "_fmode",
+ "environ",
+ "__dso_handle",
+ // These are the MinGW names that differ from the standard
+ // ones (lacking an extra underscore).
+ "DllMain",
+ "DllEntryPoint",
+ "DllMainCRTStartup",
+ };
+
+ ExcludeLibs = {
+ "libgcc",
+ "libgcc_s",
+ "libstdc++",
+ "libmingw32",
+ "libmingwex",
+ "libg2c",
+ "libsupc++",
+ "libobjc",
+ "libgcj",
+ "libclang_rt.builtins-aarch64",
+ "libclang_rt.builtins-arm",
+ "libclang_rt.builtins-i386",
+ "libclang_rt.builtins-x86_64",
+ };
+ ExcludeObjects = {
+ "crt0.o",
+ "crt1.o",
+ "crt1u.o",
+ "crt2.o",
+ "crt2u.o",
+ "dllcrt1.o",
+ "dllcrt2.o",
+ "gcrt0.o",
+ "gcrt1.o",
+ "gcrt2.o",
+ "crtbegin.o",
+ "crtend.o",
+ };
+}
+
+bool AutoExporter::shouldExport(Defined *Sym) const {
+ if (!Sym || !Sym->isLive() || !Sym->getChunk())
+ return false;
+ if (ExcludeSymbols.count(Sym->getName()))
+ return false;
+ StringRef LibName = sys::path::filename(Sym->getFile()->ParentName);
+ // Drop the file extension.
+ LibName = LibName.substr(0, LibName.rfind('.'));
+ if (ExcludeLibs.count(LibName))
+ return false;
+ StringRef FileName = sys::path::filename(Sym->getFile()->getName());
+ if (LibName.empty() && ExcludeObjects.count(FileName))
+ return false;
+ return true;
+}
+
+void coff::writeDefFile(StringRef Name) {
+ std::error_code EC;
+ raw_fd_ostream OS(Name, EC, sys::fs::F_None);
+ if (EC)
+ fatal("cannot open " + Name + ": " + EC.message());
+
+ OS << "EXPORTS\n";
+ for (Export &E : Config->Exports) {
+ OS << " " << E.ExportName << " "
+ << "@" << E.Ordinal;
+ if (auto *Def = dyn_cast_or_null<Defined>(E.Sym)) {
+ if (Def && Def->getChunk() &&
+ !(Def->getChunk()->getPermissions() & IMAGE_SCN_MEM_EXECUTE))
+ OS << " DATA";
+ }
+ OS << "\n";
+ }
+}
diff --git a/COFF/MinGW.h b/COFF/MinGW.h
new file mode 100644
index 000000000..fe6cc5588
--- /dev/null
+++ b/COFF/MinGW.h
@@ -0,0 +1,38 @@
+//===- MinGW.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_MINGW_H
+#define LLD_COFF_MINGW_H
+
+#include "Config.h"
+#include "Symbols.h"
+#include "lld/Common/LLVM.h"
+
+namespace lld {
+namespace coff {
+
+// Logic for deciding what symbols to export, when exporting all
+// symbols for MinGW.
+class AutoExporter {
+public:
+ AutoExporter();
+
+ llvm::StringSet<> ExcludeSymbols;
+ llvm::StringSet<> ExcludeLibs;
+ llvm::StringSet<> ExcludeObjects;
+
+ bool shouldExport(Defined *Sym) const;
+};
+
+void writeDefFile(StringRef Name);
+
+} // namespace coff
+} // namespace lld
+
+#endif
diff --git a/COFF/Options.td b/COFF/Options.td
index 61523c4f2..e303624ef 100644
--- a/COFF/Options.td
+++ b/COFF/Options.td
@@ -16,6 +16,7 @@ multiclass B<string name, string help> {
}
def align : P<"align", "Section alignment">;
+def aligncomm : P<"aligncomm", "Set common symbol alignment">;
def alternatename : P<"alternatename", "Define weak alias">;
def base : P<"base", "Base address of the program">;
def defaultlib : P<"defaultlib", "Add the library to the list of input files">;
@@ -30,6 +31,8 @@ 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 lldltocache : P<"lldltocache", "Path to ThinLTO cached object file directory">;
+def lldltocachepolicy : P<"lldltocachepolicy", "Pruning policy for the ThinLTO cache">;
def lldsavetemps : F<"lldsavetemps">,
HelpText<"Save temporary files instead of deleting them">;
def machine : P<"machine", "Specify target platform">;
@@ -44,6 +47,7 @@ def stack : P<"stack", "Size of the stack">;
def stub : P<"stub", "Specify DOS stub file">;
def subsystem : P<"subsystem", "Specify subsystem">;
def version : P<"version", "Specify a version number in the PE header">;
+def wholearchive_file : P<"wholearchive", "Include all object files from this archive">;
def disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias<nodefaultlib>;
@@ -75,6 +79,7 @@ def profile : F<"profile">;
def swaprun_cd : F<"swaprun:cd">;
def swaprun_net : F<"swaprun:net">;
def verbose : F<"verbose">;
+def wholearchive_flag : F<"wholearchive">;
def force : F<"force">,
HelpText<"Allow undefined symbols when creating executables">;
@@ -97,9 +102,14 @@ def help : F<"help">;
def help_q : Flag<["/?", "-?"], "">, Alias<help>;
// LLD extensions
+def export_all_symbols : F<"export-all-symbols">;
def nopdb : F<"nopdb">, HelpText<"Disable PDB generation for DWARF users">;
def nosymtab : F<"nosymtab">;
+def lldmingw : F<"lldmingw">;
def msvclto : F<"msvclto">;
+def output_def : Joined<["/", "-"], "output-def:">;
+def rsp_quoting : Joined<["--"], "rsp-quoting=">,
+ HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">;
// Flags for debugging
def lldmap : F<"lldmap">;
@@ -130,6 +140,7 @@ def errorreport : QF<"errorreport">;
def idlout : QF<"idlout">;
def ignore : QF<"ignore">;
def maxilksize : QF<"maxilksize">;
+def natvis : QF<"natvis">;
def pdbaltpath : QF<"pdbaltpath">;
def tlbid : QF<"tlbid">;
def tlbout : QF<"tlbout">;
diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp
index 5520c9791..7a6cd875f 100644
--- a/COFF/PDB.cpp
+++ b/COFF/PDB.cpp
@@ -13,30 +13,40 @@
#include "Error.h"
#include "SymbolTable.h"
#include "Symbols.h"
+#include "Writer.h"
#include "llvm/DebugInfo/CodeView/CVDebugRecord.h"
-#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
+#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
-#include "llvm/DebugInfo/CodeView/SymbolDumper.h"
+#include "llvm/DebugInfo/CodeView/RecordName.h"
+#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/CodeView/SymbolSerializer.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
+#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
#include "llvm/DebugInfo/MSF/MSFCommon.h"
+#include "llvm/DebugInfo/PDB/GenericError.h"
+#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h"
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/GSIStreamBuilder.h"
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h"
-#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h"
#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h"
-#include "llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h"
+#include "llvm/DebugInfo/PDB/Native/TpiHashing.h"
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/PDB.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/BinaryByteStream.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/JamCRC.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/ScopedPrinter.h"
#include <memory>
@@ -45,18 +55,85 @@ using namespace lld;
using namespace lld::coff;
using namespace llvm;
using namespace llvm::codeview;
-using namespace llvm::support;
-using namespace llvm::support::endian;
using llvm::object::coff_section;
static ExitOnError ExitOnErr;
-// Returns a list of all SectionChunks.
-static void addSectionContribs(SymbolTable *Symtab, pdb::DbiStreamBuilder &DbiBuilder) {
- for (Chunk *C : Symtab->getChunks())
- if (auto *SC = dyn_cast<SectionChunk>(C))
- DbiBuilder.addSectionContrib(SC->File->ModuleDBI, SC->Header);
+namespace {
+/// Map from type index and item index in a type server PDB to the
+/// corresponding index in the destination PDB.
+struct CVIndexMap {
+ SmallVector<TypeIndex, 0> TPIMap;
+ SmallVector<TypeIndex, 0> IPIMap;
+ bool IsTypeServerMap = false;
+};
+
+class PDBLinker {
+public:
+ PDBLinker(SymbolTable *Symtab)
+ : Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc),
+ IDTable(Alloc) {}
+
+ /// Emit the basic PDB structure: initial streams, headers, etc.
+ void initialize(const llvm::codeview::DebugInfo &BuildId);
+
+ /// Link CodeView from each object file in the symbol table into the PDB.
+ void addObjectsToPDB();
+
+ /// Link CodeView from a single object file into the PDB.
+ void addObjFile(ObjFile *File);
+
+ /// Produce a mapping from the type and item indices used in the object
+ /// file to those in the destination PDB.
+ ///
+ /// If the object file uses a type server PDB (compiled with /Zi), merge TPI
+ /// and IPI from the type server PDB and return a map for it. Each unique type
+ /// server PDB is merged at most once, so this may return an existing index
+ /// mapping.
+ ///
+ /// If the object does not use a type server PDB (compiled with /Z7), we merge
+ /// all the type and item records from the .debug$S stream and fill in the
+ /// caller-provided ObjectIndexMap.
+ const CVIndexMap &mergeDebugT(ObjFile *File, CVIndexMap &ObjectIndexMap);
+
+ const CVIndexMap &maybeMergeTypeServerPDB(ObjFile *File,
+ TypeServer2Record &TS);
+
+ /// Add the section map and section contributions to the PDB.
+ void addSections(ArrayRef<OutputSection *> OutputSections,
+ ArrayRef<uint8_t> SectionTable);
+
+ void addSectionContrib(pdb::DbiModuleDescriptorBuilder &LinkerModule,
+ OutputSection *OS, Chunk *C);
+
+ /// Write the PDB to disk.
+ void commit();
+
+private:
+ BumpPtrAllocator Alloc;
+
+ SymbolTable *Symtab;
+
+ pdb::PDBFileBuilder Builder;
+
+ /// Type records that will go into the PDB TPI stream.
+ TypeTableBuilder TypeTable;
+
+ /// Item records that will go into the PDB IPI stream.
+ TypeTableBuilder IDTable;
+
+ /// PDBs use a single global string table for filenames in the file checksum
+ /// table.
+ DebugStringTableSubsection PDBStrTab;
+
+ llvm::SmallString<128> NativePath;
+
+ std::vector<pdb::SecMapEntry> SectionMap;
+
+ /// Type index mappings of type server PDBs that we've loaded so far.
+ std::map<GUID, CVIndexMap> TypeServerIndexMappings;
+};
}
static SectionChunk *findByName(std::vector<SectionChunk *> &Sections,
@@ -67,88 +144,678 @@ static SectionChunk *findByName(std::vector<SectionChunk *> &Sections,
return nullptr;
}
-static ArrayRef<uint8_t> getDebugSection(ObjectFile *File, StringRef SecName) {
- SectionChunk *Sec = findByName(File->getDebugChunks(), SecName);
- if (!Sec)
- return {};
-
+static ArrayRef<uint8_t> consumeDebugMagic(ArrayRef<uint8_t> Data,
+ StringRef SecName) {
// First 4 bytes are section magic.
- ArrayRef<uint8_t> Data = Sec->getContents();
if (Data.size() < 4)
fatal(SecName + " too short");
- if (read32le(Data.data()) != COFF::DEBUG_SECTION_MAGIC)
+ if (support::endian::read32le(Data.data()) != COFF::DEBUG_SECTION_MAGIC)
fatal(SecName + " has an invalid magic");
return Data.slice(4);
}
+static ArrayRef<uint8_t> getDebugSection(ObjFile *File, StringRef SecName) {
+ if (SectionChunk *Sec = findByName(File->getDebugChunks(), SecName))
+ return consumeDebugMagic(Sec->getContents(), SecName);
+ return {};
+}
+
static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder,
- codeview::TypeTableBuilder &TypeTable) {
+ TypeTableBuilder &TypeTable) {
// Start the TPI or IPI stream header.
TpiBuilder.setVersionHeader(pdb::PdbTpiV80);
- // Flatten the in memory type table.
+ // Flatten the in memory type table and hash each type.
TypeTable.ForEachRecord([&](TypeIndex TI, ArrayRef<uint8_t> Rec) {
- // FIXME: Hash types.
- TpiBuilder.addTypeRecord(Rec, None);
+ assert(Rec.size() >= sizeof(RecordPrefix));
+ const RecordPrefix *P = reinterpret_cast<const RecordPrefix *>(Rec.data());
+ CVType Type(static_cast<TypeLeafKind>(unsigned(P->RecordKind)), Rec);
+ auto Hash = pdb::hashTypeRecord(Type);
+ if (auto E = Hash.takeError())
+ fatal("type hashing error");
+ TpiBuilder.addTypeRecord(Rec, *Hash);
});
}
-// Add all object files to the PDB. Merge .debug$T sections into IpiData and
-// TpiData.
-static void addObjectsToPDB(SymbolTable *Symtab, pdb::PDBFileBuilder &Builder,
- codeview::TypeTableBuilder &TypeTable,
- codeview::TypeTableBuilder &IDTable) {
- // Follow type servers. If the same type server is encountered more than
- // once for this instance of `PDBTypeServerHandler` (for example if many
- // object files reference the same TypeServer), the types from the
- // TypeServer will only be visited once.
- pdb::PDBTypeServerHandler Handler;
-
- // Visit all .debug$T sections to add them to Builder.
- for (ObjectFile *File : Symtab->ObjectFiles) {
- // Add a module descriptor for every object file. We need to put an absolute
- // path to the object into the PDB. If this is a plain object, we make its
- // path absolute. If it's an object in an archive, we make the archive path
- // absolute.
- bool InArchive = !File->ParentName.empty();
- SmallString<128> Path = InArchive ? File->ParentName : File->getName();
- sys::fs::make_absolute(Path);
- StringRef Name = InArchive ? File->getName() : StringRef(Path);
- File->ModuleDBI = &ExitOnErr(Builder.getDbiBuilder().addModuleInfo(Name));
- File->ModuleDBI->setObjFileName(Path);
-
- // FIXME: Walk the .debug$S sections and add them. Do things like recording
- // source files.
-
- ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T");
- if (Data.empty())
+static Optional<TypeServer2Record>
+maybeReadTypeServerRecord(CVTypeArray &Types) {
+ auto I = Types.begin();
+ if (I == Types.end())
+ return None;
+ const CVType &Type = *I;
+ if (Type.kind() != LF_TYPESERVER2)
+ return None;
+ TypeServer2Record TS;
+ if (auto EC = TypeDeserializer::deserializeAs(const_cast<CVType &>(Type), TS))
+ fatal(EC, "error reading type server record");
+ return std::move(TS);
+}
+
+const CVIndexMap &PDBLinker::mergeDebugT(ObjFile *File,
+ CVIndexMap &ObjectIndexMap) {
+ ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T");
+ if (Data.empty())
+ return ObjectIndexMap;
+
+ BinaryByteStream Stream(Data, support::little);
+ CVTypeArray Types;
+ BinaryStreamReader Reader(Stream);
+ if (auto EC = Reader.readArray(Types, Reader.getLength()))
+ fatal(EC, "Reader::readArray failed");
+
+ // Look through type servers. If we've already seen this type server, don't
+ // merge any type information.
+ if (Optional<TypeServer2Record> TS = maybeReadTypeServerRecord(Types))
+ return maybeMergeTypeServerPDB(File, *TS);
+
+ // This is a /Z7 object. Fill in the temporary, caller-provided
+ // ObjectIndexMap.
+ if (auto Err = mergeTypeAndIdRecords(IDTable, TypeTable,
+ ObjectIndexMap.TPIMap, Types))
+ fatal(Err, "codeview::mergeTypeAndIdRecords failed");
+ return ObjectIndexMap;
+}
+
+static Expected<std::unique_ptr<pdb::NativeSession>>
+tryToLoadPDB(const GUID &GuidFromObj, StringRef TSPath) {
+ std::unique_ptr<pdb::IPDBSession> ThisSession;
+ if (auto EC =
+ pdb::loadDataForPDB(pdb::PDB_ReaderType::Native, TSPath, ThisSession))
+ return std::move(EC);
+
+ std::unique_ptr<pdb::NativeSession> NS(
+ static_cast<pdb::NativeSession *>(ThisSession.release()));
+ pdb::PDBFile &File = NS->getPDBFile();
+ auto ExpectedInfo = File.getPDBInfoStream();
+ // All PDB Files should have an Info stream.
+ if (!ExpectedInfo)
+ return ExpectedInfo.takeError();
+
+ // Just because a file with a matching name was found and it was an actual
+ // PDB file doesn't mean it matches. For it to match the InfoStream's GUID
+ // must match the GUID specified in the TypeServer2 record.
+ if (ExpectedInfo->getGuid() != GuidFromObj)
+ return make_error<pdb::GenericError>(
+ pdb::generic_error_code::type_server_not_found, TSPath);
+
+ return std::move(NS);
+}
+
+const CVIndexMap &PDBLinker::maybeMergeTypeServerPDB(ObjFile *File,
+ TypeServer2Record &TS) {
+ // First, check if we already loaded a PDB with this GUID. Return the type
+ // index mapping if we have it.
+ auto Insertion = TypeServerIndexMappings.insert({TS.getGuid(), CVIndexMap()});
+ CVIndexMap &IndexMap = Insertion.first->second;
+ if (!Insertion.second)
+ return IndexMap;
+
+ // Mark this map as a type server map.
+ IndexMap.IsTypeServerMap = true;
+
+ // Check for a PDB at:
+ // 1. The given file path
+ // 2. Next to the object file or archive file
+ auto ExpectedSession = tryToLoadPDB(TS.getGuid(), TS.getName());
+ if (!ExpectedSession) {
+ consumeError(ExpectedSession.takeError());
+ StringRef LocalPath =
+ !File->ParentName.empty() ? File->ParentName : File->getName();
+ SmallString<128> Path = sys::path::parent_path(LocalPath);
+ sys::path::append(
+ Path, sys::path::filename(TS.getName(), sys::path::Style::windows));
+ ExpectedSession = tryToLoadPDB(TS.getGuid(), Path);
+ }
+ if (auto E = ExpectedSession.takeError())
+ fatal(E, "Type server PDB was not found");
+
+ // Merge TPI first, because the IPI stream will reference type indices.
+ auto ExpectedTpi = (*ExpectedSession)->getPDBFile().getPDBTpiStream();
+ if (auto E = ExpectedTpi.takeError())
+ fatal(E, "Type server does not have TPI stream");
+ if (auto Err = mergeTypeRecords(TypeTable, IndexMap.TPIMap,
+ ExpectedTpi->typeArray()))
+ fatal(Err, "codeview::mergeTypeRecords failed");
+
+ // Merge IPI.
+ auto ExpectedIpi = (*ExpectedSession)->getPDBFile().getPDBIpiStream();
+ if (auto E = ExpectedIpi.takeError())
+ fatal(E, "Type server does not have TPI stream");
+ if (auto Err = mergeIdRecords(IDTable, IndexMap.TPIMap, IndexMap.IPIMap,
+ ExpectedIpi->typeArray()))
+ fatal(Err, "codeview::mergeIdRecords failed");
+
+ return IndexMap;
+}
+
+static bool remapTypeIndex(TypeIndex &TI, ArrayRef<TypeIndex> TypeIndexMap) {
+ if (TI.isSimple())
+ return true;
+ if (TI.toArrayIndex() >= TypeIndexMap.size())
+ return false;
+ TI = TypeIndexMap[TI.toArrayIndex()];
+ return true;
+}
+
+static void remapTypesInSymbolRecord(ObjFile *File,
+ MutableArrayRef<uint8_t> Contents,
+ const CVIndexMap &IndexMap,
+ const TypeTableBuilder &IDTable,
+ ArrayRef<TiReference> TypeRefs) {
+ for (const TiReference &Ref : TypeRefs) {
+ unsigned ByteSize = Ref.Count * sizeof(TypeIndex);
+ if (Contents.size() < Ref.Offset + ByteSize)
+ fatal("symbol record too short");
+
+ // This can be an item index or a type index. Choose the appropriate map.
+ ArrayRef<TypeIndex> TypeOrItemMap = IndexMap.TPIMap;
+ if (Ref.Kind == TiRefKind::IndexRef && IndexMap.IsTypeServerMap)
+ TypeOrItemMap = IndexMap.IPIMap;
+
+ MutableArrayRef<TypeIndex> TIs(
+ reinterpret_cast<TypeIndex *>(Contents.data() + Ref.Offset), Ref.Count);
+ for (TypeIndex &TI : TIs) {
+ if (!remapTypeIndex(TI, TypeOrItemMap)) {
+ TI = TypeIndex(SimpleTypeKind::NotTranslated);
+ log("ignoring symbol record in " + File->getName() +
+ " with bad type index 0x" + utohexstr(TI.getIndex()));
+ continue;
+ }
+ }
+ }
+}
+
+static SymbolKind symbolKind(ArrayRef<uint8_t> RecordData) {
+ const RecordPrefix *Prefix =
+ reinterpret_cast<const RecordPrefix *>(RecordData.data());
+ return static_cast<SymbolKind>(uint16_t(Prefix->RecordKind));
+}
+
+/// MSVC translates S_PROC_ID_END to S_END, and S_[LG]PROC32_ID to S_[LG]PROC32
+static void translateIdSymbols(MutableArrayRef<uint8_t> &RecordData,
+ const TypeTableBuilder &IDTable) {
+ RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(RecordData.data());
+
+ SymbolKind Kind = symbolKind(RecordData);
+
+ if (Kind == SymbolKind::S_PROC_ID_END) {
+ Prefix->RecordKind = SymbolKind::S_END;
+ return;
+ }
+
+ // In an object file, GPROC32_ID has an embedded reference which refers to the
+ // single object file type index namespace. This has already been translated
+ // to the PDB file's ID stream index space, but we need to convert this to a
+ // symbol that refers to the type stream index space. So we remap again from
+ // ID index space to type index space.
+ if (Kind == SymbolKind::S_GPROC32_ID || Kind == SymbolKind::S_LPROC32_ID) {
+ SmallVector<TiReference, 1> Refs;
+ auto Content = RecordData.drop_front(sizeof(RecordPrefix));
+ CVSymbol Sym(Kind, RecordData);
+ discoverTypeIndicesInSymbol(Sym, Refs);
+ assert(Refs.size() == 1);
+ assert(Refs.front().Count == 1);
+
+ TypeIndex *TI =
+ reinterpret_cast<TypeIndex *>(Content.data() + Refs[0].Offset);
+ // `TI` is the index of a FuncIdRecord or MemberFuncIdRecord which lives in
+ // the IPI stream, whose `FunctionType` member refers to the TPI stream.
+ // Note that LF_FUNC_ID and LF_MEMFUNC_ID have the same record layout, and
+ // in both cases we just need the second type index.
+ if (!TI->isSimple() && !TI->isNoneType()) {
+ ArrayRef<uint8_t> FuncIdData = IDTable.records()[TI->toArrayIndex()];
+ SmallVector<TypeIndex, 2> Indices;
+ discoverTypeIndices(FuncIdData, Indices);
+ assert(Indices.size() == 2);
+ *TI = Indices[1];
+ }
+
+ Kind = (Kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32
+ : SymbolKind::S_LPROC32;
+ Prefix->RecordKind = uint16_t(Kind);
+ }
+}
+
+/// Copy the symbol record. In a PDB, symbol records must be 4 byte aligned.
+/// The object file may not be aligned.
+static MutableArrayRef<uint8_t> copySymbolForPdb(const CVSymbol &Sym,
+ BumpPtrAllocator &Alloc) {
+ size_t Size = alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb));
+ assert(Size >= 4 && "record too short");
+ assert(Size <= MaxRecordLength && "record too long");
+ void *Mem = Alloc.Allocate(Size, 4);
+
+ // Copy the symbol record and zero out any padding bytes.
+ MutableArrayRef<uint8_t> NewData(reinterpret_cast<uint8_t *>(Mem), Size);
+ memcpy(NewData.data(), Sym.data().data(), Sym.length());
+ memset(NewData.data() + Sym.length(), 0, Size - Sym.length());
+
+ // Update the record prefix length. It should point to the beginning of the
+ // next record.
+ auto *Prefix = reinterpret_cast<RecordPrefix *>(Mem);
+ Prefix->RecordLen = Size - 2;
+ return NewData;
+}
+
+/// Return true if this symbol opens a scope. This implies that the symbol has
+/// "parent" and "end" fields, which contain the offset of the S_END or
+/// S_INLINESITE_END record.
+static bool symbolOpensScope(SymbolKind Kind) {
+ switch (Kind) {
+ case SymbolKind::S_GPROC32:
+ case SymbolKind::S_LPROC32:
+ case SymbolKind::S_LPROC32_ID:
+ case SymbolKind::S_GPROC32_ID:
+ case SymbolKind::S_BLOCK32:
+ case SymbolKind::S_SEPCODE:
+ case SymbolKind::S_THUNK32:
+ case SymbolKind::S_INLINESITE:
+ case SymbolKind::S_INLINESITE2:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static bool symbolEndsScope(SymbolKind Kind) {
+ switch (Kind) {
+ case SymbolKind::S_END:
+ case SymbolKind::S_PROC_ID_END:
+ case SymbolKind::S_INLINESITE_END:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+struct ScopeRecord {
+ ulittle32_t PtrParent;
+ ulittle32_t PtrEnd;
+};
+
+struct SymbolScope {
+ ScopeRecord *OpeningRecord;
+ uint32_t ScopeOffset;
+};
+
+static void scopeStackOpen(SmallVectorImpl<SymbolScope> &Stack,
+ uint32_t CurOffset, CVSymbol &Sym) {
+ assert(symbolOpensScope(Sym.kind()));
+ SymbolScope S;
+ S.ScopeOffset = CurOffset;
+ S.OpeningRecord = const_cast<ScopeRecord *>(
+ reinterpret_cast<const ScopeRecord *>(Sym.content().data()));
+ S.OpeningRecord->PtrParent = Stack.empty() ? 0 : Stack.back().ScopeOffset;
+ Stack.push_back(S);
+}
+
+static void scopeStackClose(SmallVectorImpl<SymbolScope> &Stack,
+ uint32_t CurOffset, ObjFile *File) {
+ if (Stack.empty()) {
+ warn("symbol scopes are not balanced in " + File->getName());
+ return;
+ }
+ SymbolScope S = Stack.pop_back_val();
+ S.OpeningRecord->PtrEnd = CurOffset;
+}
+
+static bool symbolGoesInModuleStream(const CVSymbol &Sym) {
+ switch (Sym.kind()) {
+ case SymbolKind::S_GDATA32:
+ case SymbolKind::S_CONSTANT:
+ case SymbolKind::S_UDT:
+ // We really should not be seeing S_PROCREF and S_LPROCREF in the first place
+ // since they are synthesized by the linker in response to S_GPROC32 and
+ // S_LPROC32, but if we do see them, don't put them in the module stream I
+ // guess.
+ case SymbolKind::S_PROCREF:
+ case SymbolKind::S_LPROCREF:
+ return false;
+ // S_GDATA32 does not go in the module stream, but S_LDATA32 does.
+ case SymbolKind::S_LDATA32:
+ default:
+ return true;
+ }
+}
+
+static bool symbolGoesInGlobalsStream(const CVSymbol &Sym) {
+ switch (Sym.kind()) {
+ case SymbolKind::S_CONSTANT:
+ case SymbolKind::S_GDATA32:
+ // S_LDATA32 goes in both the module stream and the globals stream.
+ case SymbolKind::S_LDATA32:
+ case SymbolKind::S_GPROC32:
+ case SymbolKind::S_LPROC32:
+ // We really should not be seeing S_PROCREF and S_LPROCREF in the first place
+ // since they are synthesized by the linker in response to S_GPROC32 and
+ // S_LPROC32, but if we do see them, copy them straight through.
+ case SymbolKind::S_PROCREF:
+ case SymbolKind::S_LPROCREF:
+ return true;
+ // FIXME: For now, we drop all S_UDT symbols (i.e. they don't go in the
+ // globals stream or the modules stream). These have special handling which
+ // needs more investigation before we can get right, but by putting them all
+ // into the globals stream WinDbg fails to display local variables of class
+ // types saying that it cannot find the type Foo *. So as a stopgap just to
+ // keep things working, we drop them.
+ case SymbolKind::S_UDT:
+ default:
+ return false;
+ }
+}
+
+static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, ObjFile &File,
+ const CVSymbol &Sym) {
+ switch (Sym.kind()) {
+ case SymbolKind::S_CONSTANT:
+ case SymbolKind::S_UDT:
+ case SymbolKind::S_GDATA32:
+ case SymbolKind::S_LDATA32:
+ case SymbolKind::S_PROCREF:
+ case SymbolKind::S_LPROCREF:
+ Builder.addGlobalSymbol(Sym);
+ break;
+ case SymbolKind::S_GPROC32:
+ case SymbolKind::S_LPROC32: {
+ SymbolRecordKind K = SymbolRecordKind::ProcRefSym;
+ if (Sym.kind() == SymbolKind::S_LPROC32)
+ K = SymbolRecordKind::LocalProcRef;
+ ProcRefSym PS(K);
+ PS.Module = static_cast<uint16_t>(File.ModuleDBI->getModuleIndex());
+ // For some reason, MSVC seems to add one to this value.
+ ++PS.Module;
+ PS.Name = getSymbolName(Sym);
+ PS.SumName = 0;
+ PS.SymOffset = File.ModuleDBI->getNextSymbolOffset();
+ Builder.addGlobalSymbol(PS);
+ break;
+ }
+ default:
+ llvm_unreachable("Invalid symbol kind!");
+ }
+}
+
+static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjFile *File,
+ pdb::GSIStreamBuilder &GsiBuilder,
+ const CVIndexMap &IndexMap,
+ const TypeTableBuilder &IDTable,
+ BinaryStreamRef SymData) {
+ // FIXME: Improve error recovery by warning and skipping records when
+ // possible.
+ CVSymbolArray Syms;
+ BinaryStreamReader Reader(SymData);
+ ExitOnErr(Reader.readArray(Syms, Reader.getLength()));
+ SmallVector<SymbolScope, 4> Scopes;
+ for (CVSymbol Sym : Syms) {
+ // Discover type index references in the record. Skip it if we don't know
+ // where they are.
+ SmallVector<TiReference, 32> TypeRefs;
+ if (!discoverTypeIndicesInSymbol(Sym, TypeRefs)) {
+ log("ignoring unknown symbol record with kind 0x" + utohexstr(Sym.kind()));
continue;
+ }
+
+ // Copy the symbol record so we can mutate it.
+ MutableArrayRef<uint8_t> NewData = copySymbolForPdb(Sym, Alloc);
+
+ // Re-map all the type index references.
+ MutableArrayRef<uint8_t> Contents =
+ NewData.drop_front(sizeof(RecordPrefix));
+ remapTypesInSymbolRecord(File, Contents, IndexMap, IDTable, TypeRefs);
+
+ // An object file may have S_xxx_ID symbols, but these get converted to
+ // "real" symbols in a PDB.
+ translateIdSymbols(NewData, IDTable);
+
+ SymbolKind NewKind = symbolKind(NewData);
- BinaryByteStream Stream(Data, support::little);
- codeview::CVTypeArray Types;
- BinaryStreamReader Reader(Stream);
- SmallVector<TypeIndex, 128> SourceToDest;
- Handler.addSearchPath(llvm::sys::path::parent_path(File->getName()));
- if (auto EC = Reader.readArray(Types, Reader.getLength()))
- fatal(EC, "Reader::readArray failed");
- if (auto Err = codeview::mergeTypeAndIdRecords(
- IDTable, TypeTable, SourceToDest, &Handler, Types))
- fatal(Err, "codeview::mergeTypeStreams failed");
+ // Fill in "Parent" and "End" fields by maintaining a stack of scopes.
+ CVSymbol NewSym(NewKind, NewData);
+ if (symbolOpensScope(NewKind))
+ scopeStackOpen(Scopes, File->ModuleDBI->getNextSymbolOffset(), NewSym);
+ else if (symbolEndsScope(NewKind))
+ scopeStackClose(Scopes, File->ModuleDBI->getNextSymbolOffset(), File);
+
+ // Add the symbol to the globals stream if necessary. Do this before adding
+ // the symbol to the module since we may need to get the next symbol offset,
+ // and writing to the module's symbol stream will update that offset.
+ if (symbolGoesInGlobalsStream(NewSym))
+ addGlobalSymbol(GsiBuilder, *File, NewSym);
+
+ // Add the symbol to the module.
+ if (symbolGoesInModuleStream(NewSym))
+ File->ModuleDBI->addSymbol(NewSym);
}
+}
+
+// Allocate memory for a .debug$S section and relocate it.
+static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &Alloc,
+ SectionChunk *DebugChunk) {
+ uint8_t *Buffer = Alloc.Allocate<uint8_t>(DebugChunk->getSize());
+ assert(DebugChunk->OutputSectionOff == 0 &&
+ "debug sections should not be in output sections");
+ DebugChunk->writeTo(Buffer);
+ return consumeDebugMagic(makeArrayRef(Buffer, DebugChunk->getSize()),
+ ".debug$S");
+}
+
+void PDBLinker::addObjFile(ObjFile *File) {
+ // Add a module descriptor for every object file. We need to put an absolute
+ // path to the object into the PDB. If this is a plain object, we make its
+ // path absolute. If it's an object in an archive, we make the archive path
+ // absolute.
+ bool InArchive = !File->ParentName.empty();
+ SmallString<128> Path = InArchive ? File->ParentName : File->getName();
+ sys::fs::make_absolute(Path);
+ sys::path::native(Path, sys::path::Style::windows);
+ StringRef Name = InArchive ? File->getName() : StringRef(Path);
+
+ File->ModuleDBI = &ExitOnErr(Builder.getDbiBuilder().addModuleInfo(Name));
+ File->ModuleDBI->setObjFileName(Path);
+
+ // Before we can process symbol substreams from .debug$S, we need to process
+ // type information, file checksums, and the string table. Add type info to
+ // the PDB first, so that we can get the map from object file type and item
+ // indices to PDB type and item indices.
+ CVIndexMap ObjectIndexMap;
+ const CVIndexMap &IndexMap = mergeDebugT(File, ObjectIndexMap);
+
+ // Now do all live .debug$S sections.
+ for (SectionChunk *DebugChunk : File->getDebugChunks()) {
+ if (!DebugChunk->isLive() || DebugChunk->getSectionName() != ".debug$S")
+ continue;
+
+ ArrayRef<uint8_t> RelocatedDebugContents =
+ relocateDebugChunk(Alloc, DebugChunk);
+ if (RelocatedDebugContents.empty())
+ continue;
+
+ DebugSubsectionArray Subsections;
+ BinaryStreamReader Reader(RelocatedDebugContents, support::little);
+ ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size()));
+
+ DebugStringTableSubsectionRef CVStrTab;
+ DebugChecksumsSubsectionRef Checksums;
+ for (const DebugSubsectionRecord &SS : Subsections) {
+ switch (SS.kind()) {
+ case DebugSubsectionKind::StringTable:
+ ExitOnErr(CVStrTab.initialize(SS.getRecordData()));
+ break;
+ case DebugSubsectionKind::FileChecksums:
+ ExitOnErr(Checksums.initialize(SS.getRecordData()));
+ break;
+ case DebugSubsectionKind::Lines:
+ // We can add the relocated line table directly to the PDB without
+ // modification because the file checksum offsets will stay the same.
+ File->ModuleDBI->addDebugSubsection(SS);
+ break;
+ case DebugSubsectionKind::Symbols:
+ mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap,
+ IDTable, SS.getRecordData());
+ break;
+ default:
+ // FIXME: Process the rest of the subsections.
+ break;
+ }
+ }
+
+ if (Checksums.valid()) {
+ // Make a new file checksum table that refers to offsets in the PDB-wide
+ // string table. Generally the string table subsection appears after the
+ // checksum table, so we have to do this after looping over all the
+ // subsections.
+ if (!CVStrTab.valid())
+ fatal(".debug$S sections must have both a string table subsection "
+ "and a checksum subsection table or neither");
+ auto NewChecksums = make_unique<DebugChecksumsSubsection>(PDBStrTab);
+ for (FileChecksumEntry &FC : Checksums) {
+ StringRef FileName = ExitOnErr(CVStrTab.getString(FC.FileNameOffset));
+ ExitOnErr(Builder.getDbiBuilder().addModuleSourceFile(*File->ModuleDBI,
+ FileName));
+ NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum);
+ }
+ File->ModuleDBI->addDebugSubsection(std::move(NewChecksums));
+ }
+ }
+}
+
+static PublicSym32 createPublic(Defined *Def) {
+ PublicSym32 Pub(SymbolKind::S_PUB32);
+ Pub.Name = Def->getName();
+ if (auto *D = dyn_cast<DefinedCOFF>(Def)) {
+ if (D->getCOFFSymbol().isFunctionDefinition())
+ Pub.Flags = PublicSymFlags::Function;
+ } else if (isa<DefinedImportThunk>(Def)) {
+ Pub.Flags = PublicSymFlags::Function;
+ }
+
+ OutputSection *OS = Def->getChunk()->getOutputSection();
+ assert(OS && "all publics should be in final image");
+ Pub.Offset = Def->getRVA() - OS->getRVA();
+ Pub.Segment = OS->SectionIndex;
+ return Pub;
+}
+
+// Add all object files to the PDB. Merge .debug$T sections into IpiData and
+// TpiData.
+void PDBLinker::addObjectsToPDB() {
+ for (ObjFile *File : ObjFile::Instances)
+ addObjFile(File);
+
+ Builder.getStringTableBuilder().setStrings(PDBStrTab);
// Construct TPI stream contents.
addTypeInfo(Builder.getTpiBuilder(), TypeTable);
// Construct IPI stream contents.
addTypeInfo(Builder.getIpiBuilder(), IDTable);
+
+ // Compute the public and global symbols.
+ auto &GsiBuilder = Builder.getGsiBuilder();
+ std::vector<PublicSym32> Publics;
+ Symtab->forEachSymbol([&Publics](Symbol *S) {
+ // Only emit defined, live symbols that have a chunk.
+ auto *Def = dyn_cast<Defined>(S->body());
+ if (Def && Def->isLive() && Def->getChunk())
+ Publics.push_back(createPublic(Def));
+ });
+
+ if (!Publics.empty()) {
+ // Sort the public symbols and add them to the stream.
+ std::sort(Publics.begin(), Publics.end(),
+ [](const PublicSym32 &L, const PublicSym32 &R) {
+ return L.Name < R.Name;
+ });
+ for (const PublicSym32 &Pub : Publics)
+ GsiBuilder.addPublicSymbol(Pub);
+ }
+}
+
+static void addCommonLinkerModuleSymbols(StringRef Path,
+ pdb::DbiModuleDescriptorBuilder &Mod,
+ BumpPtrAllocator &Allocator) {
+ ObjNameSym ONS(SymbolRecordKind::ObjNameSym);
+ Compile3Sym CS(SymbolRecordKind::Compile3Sym);
+ EnvBlockSym EBS(SymbolRecordKind::EnvBlockSym);
+
+ ONS.Name = "* Linker *";
+ ONS.Signature = 0;
+
+ CS.Machine = Config->is64() ? CPUType::X64 : CPUType::Intel80386;
+ // Interestingly, if we set the string to 0.0.0.0, then when trying to view
+ // local variables WinDbg emits an error that private symbols are not present.
+ // By setting this to a valid MSVC linker version string, local variables are
+ // displayed properly. As such, even though it is not representative of
+ // LLVM's version information, we need this for compatibility.
+ CS.Flags = CompileSym3Flags::None;
+ CS.VersionBackendBuild = 25019;
+ CS.VersionBackendMajor = 14;
+ CS.VersionBackendMinor = 10;
+ CS.VersionBackendQFE = 0;
+
+ // MSVC also sets the frontend to 0.0.0.0 since this is specifically for the
+ // linker module (which is by definition a backend), so we don't need to do
+ // anything here. Also, it seems we can use "LLVM Linker" for the linker name
+ // without any problems. Only the backend version has to be hardcoded to a
+ // magic number.
+ CS.VersionFrontendBuild = 0;
+ CS.VersionFrontendMajor = 0;
+ CS.VersionFrontendMinor = 0;
+ CS.VersionFrontendQFE = 0;
+ CS.Version = "LLVM Linker";
+ CS.setLanguage(SourceLanguage::Link);
+
+ ArrayRef<StringRef> Args = makeArrayRef(Config->Argv).drop_front();
+ std::string ArgStr = llvm::join(Args, " ");
+ EBS.Fields.push_back("cwd");
+ SmallString<64> cwd;
+ sys::fs::current_path(cwd);
+ EBS.Fields.push_back(cwd);
+ EBS.Fields.push_back("exe");
+ SmallString<64> exe = Config->Argv[0];
+ llvm::sys::fs::make_absolute(exe);
+ EBS.Fields.push_back(exe);
+ EBS.Fields.push_back("pdb");
+ EBS.Fields.push_back(Path);
+ EBS.Fields.push_back("cmd");
+ EBS.Fields.push_back(ArgStr);
+ Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ ONS, Allocator, CodeViewContainer::Pdb));
+ Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ CS, Allocator, CodeViewContainer::Pdb));
+ Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ EBS, Allocator, CodeViewContainer::Pdb));
+}
+
+static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &Mod,
+ OutputSection &OS,
+ BumpPtrAllocator &Allocator) {
+ SectionSym Sym(SymbolRecordKind::SectionSym);
+ Sym.Alignment = 12; // 2^12 = 4KB
+ Sym.Characteristics = OS.getCharacteristics();
+ Sym.Length = OS.getVirtualSize();
+ Sym.Name = OS.getName();
+ Sym.Rva = OS.getRVA();
+ Sym.SectionNumber = OS.SectionIndex;
+ Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ Sym, Allocator, CodeViewContainer::Pdb));
}
// Creates a PDB file.
-void coff::createPDB(StringRef Path, SymbolTable *Symtab,
+void coff::createPDB(SymbolTable *Symtab,
+ ArrayRef<OutputSection *> OutputSections,
ArrayRef<uint8_t> SectionTable,
- const llvm::codeview::DebugInfo *DI) {
- BumpPtrAllocator Alloc;
- pdb::PDBFileBuilder Builder(Alloc);
+ const llvm::codeview::DebugInfo &BuildId) {
+ PDBLinker PDB(Symtab);
+ PDB.initialize(BuildId);
+ PDB.addObjectsToPDB();
+ PDB.addSections(OutputSections, SectionTable);
+ PDB.commit();
+}
+
+void PDBLinker::initialize(const llvm::codeview::DebugInfo &BuildId) {
ExitOnErr(Builder.initialize(4096)); // 4096 is blocksize
// Create streams in MSF for predefined streams, namely
@@ -158,41 +825,78 @@ void coff::createPDB(StringRef Path, SymbolTable *Symtab,
// Add an Info stream.
auto &InfoBuilder = Builder.getInfoBuilder();
- InfoBuilder.setAge(DI ? DI->PDB70.Age : 0);
+ InfoBuilder.setAge(BuildId.PDB70.Age);
- pdb::PDB_UniqueId uuid{};
- if (DI)
- memcpy(&uuid, &DI->PDB70.Signature, sizeof(uuid));
+ GUID uuid;
+ memcpy(&uuid, &BuildId.PDB70.Signature, sizeof(uuid));
InfoBuilder.setGuid(uuid);
- // Should be the current time, but set 0 for reproducibilty.
- InfoBuilder.setSignature(0);
+ InfoBuilder.setSignature(time(nullptr));
InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70);
- // Add an empty DPI stream.
+ // Add an empty DBI stream.
pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
- DbiBuilder.setVersionHeader(pdb::PdbDbiV110);
+ DbiBuilder.setAge(BuildId.PDB70.Age);
+ DbiBuilder.setVersionHeader(pdb::PdbDbiV70);
+ ExitOnErr(DbiBuilder.addDbgStream(pdb::DbgHeaderType::NewFPO, {}));
+}
+
+void PDBLinker::addSectionContrib(pdb::DbiModuleDescriptorBuilder &LinkerModule,
+ OutputSection *OS, Chunk *C) {
+ pdb::SectionContrib SC;
+ memset(&SC, 0, sizeof(SC));
+ SC.ISect = OS->SectionIndex;
+ SC.Off = C->getRVA() - OS->getRVA();
+ SC.Size = C->getSize();
+ if (auto *SecChunk = dyn_cast<SectionChunk>(C)) {
+ SC.Characteristics = SecChunk->Header->Characteristics;
+ SC.Imod = SecChunk->File->ModuleDBI->getModuleIndex();
+ ArrayRef<uint8_t> Contents = SecChunk->getContents();
+ JamCRC CRC(0);
+ ArrayRef<char> CharContents = makeArrayRef(
+ reinterpret_cast<const char *>(Contents.data()), Contents.size());
+ CRC.update(CharContents);
+ SC.DataCrc = CRC.getCRC();
+ } else {
+ SC.Characteristics = OS->getCharacteristics();
+ // FIXME: When we start creating DBI for import libraries, use those here.
+ SC.Imod = LinkerModule.getModuleIndex();
+ }
+ SC.RelocCrc = 0; // FIXME
+ Builder.getDbiBuilder().addSectionContrib(SC);
+}
- codeview::TypeTableBuilder TypeTable(BAlloc);
- codeview::TypeTableBuilder IDTable(BAlloc);
- addObjectsToPDB(Symtab, Builder, TypeTable, IDTable);
+void PDBLinker::addSections(ArrayRef<OutputSection *> OutputSections,
+ ArrayRef<uint8_t> SectionTable) {
+ // It's not entirely clear what this is, but the * Linker * module uses it.
+ pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
+ NativePath = Config->PDBPath;
+ sys::fs::make_absolute(NativePath);
+ sys::path::native(NativePath, sys::path::Style::windows);
+ uint32_t PdbFilePathNI = DbiBuilder.addECName(NativePath);
+ auto &LinkerModule = ExitOnErr(DbiBuilder.addModuleInfo("* Linker *"));
+ LinkerModule.setPdbFilePathNI(PdbFilePathNI);
+ addCommonLinkerModuleSymbols(NativePath, LinkerModule, Alloc);
- // Add Section Contributions.
- addSectionContribs(Symtab, DbiBuilder);
+ // Add section contributions. They must be ordered by ascending RVA.
+ for (OutputSection *OS : OutputSections) {
+ addLinkerModuleSectionSymbol(LinkerModule, *OS, Alloc);
+ for (Chunk *C : OS->getChunks())
+ addSectionContrib(LinkerModule, OS, C);
+ }
// 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);
+ SectionMap = pdb::DbiStreamBuilder::createSectionMap(Sections);
DbiBuilder.setSectionMap(SectionMap);
- ExitOnErr(DbiBuilder.addModuleInfo("* Linker *"));
-
// Add COFF section header stream.
ExitOnErr(
DbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, SectionTable));
+}
+void PDBLinker::commit() {
// Write to a file.
- ExitOnErr(Builder.commit(Path));
+ ExitOnErr(Builder.commit(Config->PDBPath));
}
diff --git a/COFF/PDB.h b/COFF/PDB.h
index c9c379142..defd7d236 100644
--- a/COFF/PDB.h
+++ b/COFF/PDB.h
@@ -21,11 +21,13 @@ union DebugInfo;
namespace lld {
namespace coff {
+class OutputSection;
class SymbolTable;
-void createPDB(llvm::StringRef Path, SymbolTable *Symtab,
+void createPDB(SymbolTable *Symtab,
+ llvm::ArrayRef<OutputSection *> OutputSections,
llvm::ArrayRef<uint8_t> SectionTable,
- const llvm::codeview::DebugInfo *DI);
+ const llvm::codeview::DebugInfo &BuildId);
}
}
diff --git a/COFF/Strings.cpp b/COFF/Strings.cpp
index d0558413f..84f9b9a55 100644
--- a/COFF/Strings.cpp
+++ b/COFF/Strings.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "Strings.h"
+#include <mutex>
#if defined(_MSC_VER)
#include <Windows.h>
@@ -21,6 +22,10 @@ using namespace llvm;
Optional<std::string> coff::demangle(StringRef S) {
#if defined(_MSC_VER)
+ // UnDecorateSymbolName is not thread-safe, so we need a mutex.
+ static std::mutex Mu;
+ std::lock_guard<std::mutex> Lock(Mu);
+
char Buf[4096];
if (S.startswith("?"))
if (size_t Len = UnDecorateSymbolName(S.str().c_str(), Buf, sizeof(Buf), 0))
diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp
index 5089825e6..40fbea91c 100644
--- a/COFF/SymbolTable.cpp
+++ b/COFF/SymbolTable.cpp
@@ -68,12 +68,12 @@ void SymbolTable::addFile(InputFile *File) {
" conflicts with " + machineToStr(Config->Machine));
}
- if (auto *F = dyn_cast<ObjectFile>(File)) {
- ObjectFiles.push_back(F);
+ if (auto *F = dyn_cast<ObjFile>(File)) {
+ ObjFile::Instances.push_back(F);
} else if (auto *F = dyn_cast<BitcodeFile>(File)) {
- BitcodeFiles.push_back(F);
+ BitcodeFile::Instances.push_back(F);
} else if (auto *F = dyn_cast<ImportFile>(File)) {
- ImportFiles.push_back(F);
+ ImportFile::Instances.push_back(F);
}
StringRef S = File->getDirectives();
@@ -84,8 +84,16 @@ void SymbolTable::addFile(InputFile *File) {
Driver->parseDirectives(S);
}
+static void errorOrWarn(const Twine &S) {
+ if (Config->Force)
+ warn(S);
+ else
+ error(S);
+}
+
void SymbolTable::reportRemainingUndefines() {
SmallPtrSet<SymbolBody *, 8> Undefs;
+
for (auto &I : Symtab) {
Symbol *Sym = I.second;
auto *Undef = dyn_cast<Undefined>(Sym->body());
@@ -93,7 +101,9 @@ void SymbolTable::reportRemainingUndefines() {
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()) {
// We resolve weak aliases by replacing the alias's SymbolBody with the
@@ -112,6 +122,7 @@ void SymbolTable::reportRemainingUndefines() {
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_")) {
@@ -124,23 +135,25 @@ void SymbolTable::reportRemainingUndefines() {
continue;
}
}
+
// Remaining undefined symbols are not fatal if /force is specified.
// They are replaced with dummy defined symbols.
if (Config->Force)
replaceBody<DefinedAbsolute>(Sym, Name, 0);
Undefs.insert(Sym->body());
}
+
if (Undefs.empty())
return;
+
for (SymbolBody *B : Config->GCRoot)
if (Undefs.count(B))
- warn("<root>: undefined symbol: " + B->getName());
- for (ObjectFile *File : ObjectFiles)
+ errorOrWarn("<root>: undefined symbol: " + B->getName());
+
+ for (ObjFile *File : ObjFile::Instances)
for (SymbolBody *Sym : File->getSymbols())
if (Undefs.count(Sym))
- warn(toString(File) + ": undefined symbol: " + Sym->getName());
- if (!Config->Force)
- fatal("link failed");
+ errorOrWarn(toString(File) + ": undefined symbol: " + Sym->getName());
}
std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
@@ -219,13 +232,13 @@ Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) {
return S;
}
-Symbol *SymbolTable::addRelative(StringRef N, uint64_t VA) {
+Symbol *SymbolTable::addSynthetic(StringRef N, Chunk *C) {
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);
+ replaceBody<DefinedSynthetic>(S, N, C);
else if (!isa<DefinedCOFF>(S->body()))
reportDuplicate(S, nullptr);
return S;
@@ -269,34 +282,39 @@ Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size,
return S;
}
-Symbol *SymbolTable::addImportData(StringRef N, ImportFile *F) {
+DefinedImportData *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()))
+ 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;
+ return cast<DefinedImportData>(S->body());
+ }
+
+ reportDuplicate(S, F);
+ return nullptr;
}
-Symbol *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID,
- uint16_t Machine) {
+DefinedImportThunk *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()))
+ 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;
+ return cast<DefinedImportThunk>(S->body());
+ }
+
+ reportDuplicate(S, ID->File);
+ return nullptr;
}
std::vector<Chunk *> SymbolTable::getChunks() {
std::vector<Chunk *> Res;
- for (ObjectFile *File : ObjectFiles) {
+ for (ObjFile *File : ObjFile::Instances) {
std::vector<Chunk *> &V = File->getChunks();
Res.insert(Res.end(), V.begin(), V.end());
}
@@ -356,18 +374,18 @@ SymbolBody *SymbolTable::addUndefined(StringRef Name) {
std::vector<StringRef> SymbolTable::compileBitcodeFiles() {
LTO.reset(new BitcodeCompiler);
- for (BitcodeFile *F : BitcodeFiles)
+ for (BitcodeFile *F : BitcodeFile::Instances)
LTO->add(*F);
return LTO->compile();
}
void SymbolTable::addCombinedLTOObjects() {
- if (BitcodeFiles.empty())
+ if (BitcodeFile::Instances.empty())
return;
for (StringRef Object : compileBitcodeFiles()) {
- auto *Obj = make<ObjectFile>(MemoryBufferRef(Object, "lto.tmp"));
+ auto *Obj = make<ObjFile>(MemoryBufferRef(Object, "lto.tmp"));
Obj->parse();
- ObjectFiles.push_back(Obj);
+ ObjFile::Instances.push_back(Obj);
}
}
diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h
index 0aa8a4593..c82582657 100644
--- a/COFF/SymbolTable.h
+++ b/COFF/SymbolTable.h
@@ -75,17 +75,10 @@ public:
void addCombinedLTOObjects();
std::vector<StringRef> compileBitcodeFiles();
- // The writer needs to handle DLL import libraries specially in
- // order to create the import descriptor table.
- std::vector<ImportFile *> ImportFiles;
-
- // The writer needs to infer the machine type from the object files.
- std::vector<ObjectFile *> ObjectFiles;
-
// Creates an Undefined symbol for a given name.
SymbolBody *addUndefined(StringRef Name);
- Symbol *addRelative(StringRef N, uint64_t VA);
+ Symbol *addSynthetic(StringRef N, Chunk *C);
Symbol *addAbsolute(StringRef N, uint64_t VA);
Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias);
@@ -97,22 +90,26 @@ public:
Symbol *addCommon(InputFile *F, StringRef N, uint64_t Size,
const llvm::object::coff_symbol_generic *S = nullptr,
CommonChunk *C = nullptr);
- Symbol *addImportData(StringRef N, ImportFile *F);
- Symbol *addImportThunk(StringRef Name, DefinedImportData *S,
- uint16_t Machine);
+ DefinedImportData *addImportData(StringRef N, ImportFile *F);
+ DefinedImportThunk *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;
+ // Iterates symbols in non-determinstic hash table order.
+ template <typename T> void forEachSymbol(T Callback) {
+ for (auto &Pair : Symtab)
+ Callback(Pair.second);
+ }
+
private:
std::pair<Symbol *, bool> insert(StringRef Name);
StringRef findByPrefix(StringRef Prefix);
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> Symtab;
-
- std::vector<BitcodeFile *> BitcodeFiles;
std::unique_ptr<BitcodeCompiler> LTO;
};
diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp
index 5c185a511..2d6159049 100644
--- a/COFF/Symbols.cpp
+++ b/COFF/Symbols.cpp
@@ -39,7 +39,7 @@ StringRef SymbolBody::getName() {
// is a waste of time.
if (Name.empty()) {
auto *D = cast<DefinedCOFF>(this);
- cast<ObjectFile>(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name);
+ cast<ObjFile>(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name);
}
return Name;
}
@@ -52,20 +52,34 @@ InputFile *SymbolBody::getFile() {
return nullptr;
}
+bool SymbolBody::isLive() const {
+ if (auto *R = dyn_cast<DefinedRegular>(this))
+ return R->getChunk()->isLive();
+ if (auto *Imp = dyn_cast<DefinedImportData>(this))
+ return Imp->File->Live;
+ if (auto *Imp = dyn_cast<DefinedImportThunk>(this))
+ return Imp->WrappedSym->File->Live;
+ // Assume any other kind of symbol is live.
+ return true;
+}
+
COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
- size_t SymSize =
- cast<ObjectFile>(File)->getCOFFObj()->getSymbolTableEntrySize();
+ size_t SymSize = cast<ObjFile>(File)->getCOFFObj()->getSymbolTableEntrySize();
if (SymSize == sizeof(coff_symbol16))
return COFFSymbolRef(reinterpret_cast<const coff_symbol16 *>(Sym));
assert(SymSize == sizeof(coff_symbol32));
return COFFSymbolRef(reinterpret_cast<const coff_symbol32 *>(Sym));
}
+uint16_t DefinedAbsolute::OutputSectionIndex = 0;
+
static Chunk *makeImportThunk(DefinedImportData *S, uint16_t Machine) {
if (Machine == AMD64)
return make<ImportThunkChunkX64>(S);
if (Machine == I386)
return make<ImportThunkChunkX86>(S);
+ if (Machine == ARM64)
+ return make<ImportThunkChunkARM64>(S);
assert(Machine == ARMNT);
return make<ImportThunkChunkARM>(S);
}
diff --git a/COFF/Symbols.h b/COFF/Symbols.h
index 801fc87f9..4042b4eef 100644
--- a/COFF/Symbols.h
+++ b/COFF/Symbols.h
@@ -13,7 +13,7 @@
#include "Chunks.h"
#include "Config.h"
#include "Memory.h"
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/COFF.h"
@@ -31,7 +31,7 @@ using llvm::object::coff_symbol_generic;
class ArchiveFile;
class InputFile;
-class ObjectFile;
+class ObjFile;
struct Symbol;
class SymbolTable;
@@ -50,13 +50,13 @@ public:
DefinedImportThunkKind,
DefinedImportDataKind,
DefinedAbsoluteKind,
- DefinedRelativeKind,
+ DefinedSyntheticKind,
UndefinedKind,
LazyKind,
LastDefinedCOFFKind = DefinedCommonKind,
- LastDefinedKind = DefinedRelativeKind,
+ LastDefinedKind = DefinedSyntheticKind,
};
Kind kind() const { return static_cast<Kind>(SymbolKind); }
@@ -70,6 +70,10 @@ public:
// Returns the file from which this symbol was created.
InputFile *getFile();
+ // Indicates that this symbol will be included in the final image. Only valid
+ // after calling markLive.
+ bool isLive() const;
+
Symbol *symbol();
const Symbol *symbol() const {
return const_cast<SymbolBody *>(this)->symbol();
@@ -110,17 +114,9 @@ public:
// writer sets and uses RVAs.
uint64_t getRVA();
- // Returns the RVA relative to the beginning of the output section.
- // Used to implement SECREL relocation type.
- uint64_t getSecrel();
-
- // Returns the output section index.
- // Used to implement SECTION relocation type.
- uint64_t getSectionIndex();
-
- // Returns true if this symbol points to an executable (e.g. .text) section.
- // Used to implement ARM relocations.
- bool isExecutable();
+ // Returns the chunk containing this symbol. Absolute symbols and __ImageBase
+ // do not have chunks, so this may return null.
+ Chunk *getChunk();
};
// Symbols defined via a COFF object file or bitcode file. For COFF files, this
@@ -163,10 +159,10 @@ public:
return S->kind() == DefinedRegularKind;
}
- uint64_t getRVA() { return (*Data)->getRVA() + Sym->Value; }
- bool isCOMDAT() { return IsCOMDAT; }
- SectionChunk *getChunk() { return *Data; }
- uint32_t getValue() { return Sym->Value; }
+ uint64_t getRVA() const { return (*Data)->getRVA() + Sym->Value; }
+ bool isCOMDAT() const { return IsCOMDAT; }
+ SectionChunk *getChunk() const { return *Data; }
+ uint32_t getValue() const { return Sym->Value; }
private:
SectionChunk **Data;
@@ -186,6 +182,7 @@ public:
}
uint64_t getRVA() { return Data->getRVA(); }
+ CommonChunk *getChunk() { return Data; }
private:
friend SymbolTable;
@@ -212,28 +209,34 @@ public:
uint64_t getRVA() { return VA - Config->ImageBase; }
void setVA(uint64_t V) { VA = V; }
+ // The sentinel absolute symbol section index. Section index relocations
+ // against absolute symbols resolve to this 16 bit number, and it is the
+ // largest valid section index plus one. This is written by the Writer.
+ static uint16_t OutputSectionIndex;
+ uint16_t getSecIdx() { return OutputSectionIndex; }
+
private:
uint64_t VA;
};
-// This is a kind of absolute symbol but relative to the image base.
-// Unlike absolute symbols, relocations referring this kind of symbols
-// are subject of the base relocation. This type is used rarely --
-// mainly for __ImageBase.
-class DefinedRelative : public Defined {
+// This symbol is used for linker-synthesized symbols like __ImageBase and
+// __safe_se_handler_table.
+class DefinedSynthetic : public Defined {
public:
- explicit DefinedRelative(StringRef Name, uint64_t V = 0)
- : Defined(DefinedRelativeKind, Name), RVA(V) {}
+ explicit DefinedSynthetic(StringRef Name, Chunk *C)
+ : Defined(DefinedSyntheticKind, Name), C(C) {}
static bool classof(const SymbolBody *S) {
- return S->kind() == DefinedRelativeKind;
+ return S->kind() == DefinedSyntheticKind;
}
- uint64_t getRVA() { return RVA; }
- void setRVA(uint64_t V) { RVA = V; }
+ // A null chunk indicates that this is __ImageBase. Otherwise, this is some
+ // other synthesized chunk, like SEHTableChunk.
+ uint32_t getRVA() { return C ? C->getRVA() : 0; }
+ Chunk *getChunk() { return C; }
private:
- uint64_t RVA;
+ Chunk *C;
};
// This class represents a symbol defined in an archive file. It is
@@ -295,9 +298,11 @@ public:
}
uint64_t getRVA() { return File->Location->getRVA(); }
+ Chunk *getChunk() { return File->Location; }
+ void setLocation(Chunk *AddressTable) { File->Location = AddressTable; }
+
StringRef getDLLName() { return File->DLLName; }
StringRef getExternalName() { return File->ExternalName; }
- void setLocation(Chunk *AddressTable) { File->Location = AddressTable; }
uint16_t getOrdinal() { return File->Hdr->OrdinalHint; }
ImportFile *File;
@@ -350,8 +355,8 @@ inline uint64_t Defined::getRVA() {
switch (kind()) {
case DefinedAbsoluteKind:
return cast<DefinedAbsolute>(this)->getRVA();
- case DefinedRelativeKind:
- return cast<DefinedRelative>(this)->getRVA();
+ case DefinedSyntheticKind:
+ return cast<DefinedSynthetic>(this)->getRVA();
case DefinedImportDataKind:
return cast<DefinedImportData>(this)->getRVA();
case DefinedImportThunkKind:
@@ -369,6 +374,29 @@ inline uint64_t Defined::getRVA() {
llvm_unreachable("unknown symbol kind");
}
+inline Chunk *Defined::getChunk() {
+ switch (kind()) {
+ case DefinedRegularKind:
+ return cast<DefinedRegular>(this)->getChunk();
+ case DefinedAbsoluteKind:
+ return nullptr;
+ case DefinedSyntheticKind:
+ return cast<DefinedSynthetic>(this)->getChunk();
+ case DefinedImportDataKind:
+ return cast<DefinedImportData>(this)->getChunk();
+ case DefinedImportThunkKind:
+ return cast<DefinedImportThunk>(this)->getChunk();
+ case DefinedLocalImportKind:
+ return cast<DefinedLocalImport>(this)->getChunk();
+ case DefinedCommonKind:
+ return cast<DefinedCommon>(this)->getChunk();
+ case LazyKind:
+ case UndefinedKind:
+ llvm_unreachable("Cannot get the chunk of an undefined symbol.");
+ }
+ 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
@@ -386,7 +414,7 @@ struct Symbol {
// 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,
+ DefinedRegular, DefinedCommon, DefinedAbsolute, DefinedSynthetic, Lazy,
Undefined, DefinedImportData, DefinedImportThunk, DefinedLocalImport>
Body;
diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp
index fb1f3cae5..db446d083 100644
--- a/COFF/Writer.cpp
+++ b/COFF/Writer.cpp
@@ -20,12 +20,12 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/BinaryStreamReader.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/Parallel.h"
#include "llvm/Support/RandomNumberGenerator.h"
-#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstdio>
#include <map>
@@ -65,8 +65,9 @@ public:
D->Type = COFF::IMAGE_DEBUG_TYPE_CODEVIEW;
D->SizeOfData = Record->getSize();
D->AddressOfRawData = Record->getRVA();
- // TODO(compnerd) get the file offset
- D->PointerToRawData = 0;
+ OutputSection *OS = Record->getOutputSection();
+ uint64_t Offs = OS->getFileOff() + (Record->getRVA() - OS->getRVA());
+ D->PointerToRawData = Offs;
++D;
}
@@ -77,32 +78,36 @@ private:
};
class CVDebugRecordChunk : public Chunk {
+public:
+ CVDebugRecordChunk() {
+ PDBAbsPath = Config->PDBPath;
+ if (!PDBAbsPath.empty())
+ llvm::sys::fs::make_absolute(PDBAbsPath);
+ }
+
size_t getSize() const override {
- return sizeof(codeview::DebugInfo) + Config->PDBPath.size() + 1;
+ return sizeof(codeview::DebugInfo) + PDBAbsPath.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;
+ BuildId = reinterpret_cast<codeview::DebugInfo *>(B + OutputSectionOff);
// 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';
+ char *P = reinterpret_cast<char *>(B + OutputSectionOff + sizeof(*BuildId));
+ if (!PDBAbsPath.empty())
+ memcpy(P, PDBAbsPath.data(), PDBAbsPath.size());
+ P[PDBAbsPath.size()] = '\0';
}
-public:
- mutable codeview::DebugInfo *DI = nullptr;
+ SmallString<128> PDBAbsPath;
+ mutable codeview::DebugInfo *BuildId = nullptr;
};
// The writer writes a SymbolTable result to a file.
class Writer {
public:
- Writer(SymbolTable *T) : Symtab(T) {}
void run();
private:
@@ -118,9 +123,8 @@ private:
void fixSafeSEHSymbols();
void setSectionPermissions();
void writeSections();
- void sortExceptionTable();
void writeBuildId();
- void applyRelocations();
+ void sortExceptionTable();
llvm::Optional<coff_symbol16> createSymbol(Defined *D);
size_t addEntryToStringTable(StringRef Str);
@@ -133,7 +137,6 @@ private:
uint32_t getSizeOfInitializedData();
std::map<StringRef, std::vector<DefinedImportData *>> binImports();
- SymbolTable *Symtab;
std::unique_ptr<FileOutputBuffer> Buffer;
std::vector<OutputSection *> OutputSections;
std::vector<char> Strtab;
@@ -146,6 +149,7 @@ private:
Chunk *DebugDirectory = nullptr;
std::vector<Chunk *> DebugRecords;
CVDebugRecordChunk *BuildId = nullptr;
+ Optional<codeview::DebugInfo> PreviousBuildId;
ArrayRef<uint8_t> SectionTable;
uint64_t FileSize;
@@ -158,7 +162,7 @@ private:
namespace lld {
namespace coff {
-void writeResult(SymbolTable *T) { Writer(T).run(); }
+void writeResult() { Writer().run(); }
void OutputSection::setRVA(uint64_t RVA) {
Header.VirtualAddress = RVA;
@@ -179,10 +183,12 @@ void OutputSection::addChunk(Chunk *C) {
Chunks.push_back(C);
C->setOutputSection(this);
uint64_t Off = Header.VirtualSize;
- Off = alignTo(Off, C->getAlign());
+ Off = alignTo(Off, C->Alignment);
C->setRVA(Off);
- C->setOutputSectionOff(Off);
+ C->OutputSectionOff = Off;
Off += C->getSize();
+ if (Off > UINT32_MAX)
+ error("section larger than 4 GiB: " + Name);
Header.VirtualSize = Off;
if (C->hasData())
Header.SizeOfRawData = alignTo(Off, SectorSize);
@@ -210,27 +216,69 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) {
}
}
-uint64_t Defined::getSecrel() {
- if (auto *D = dyn_cast<DefinedRegular>(this))
- return getRVA() - D->getChunk()->getOutputSection()->getRVA();
- fatal("SECREL relocation points to a non-regular symbol");
-}
+} // namespace coff
+} // namespace lld
-uint64_t Defined::getSectionIndex() {
- if (auto *D = dyn_cast<DefinedRegular>(this))
- return D->getChunk()->getOutputSection()->SectionIndex;
- fatal("SECTION relocation points to a non-regular symbol");
-}
+// PDBs are matched against executables using a build id which consists of three
+// components:
+// 1. A 16-bit GUID
+// 2. An age
+// 3. A time stamp.
+//
+// Debuggers and symbol servers match executables against debug info by checking
+// each of these components of the EXE/DLL against the corresponding value in
+// the PDB and failing a match if any of the components differ. In the case of
+// symbol servers, symbols are cached in a folder that is a function of the
+// GUID. As a result, in order to avoid symbol cache pollution where every
+// incremental build copies a new PDB to the symbol cache, we must try to re-use
+// the existing GUID if one exists, but bump the age. This way the match will
+// fail, so the symbol cache knows to use the new PDB, but the GUID matches, so
+// it overwrites the existing item in the symbol cache rather than making a new
+// one.
+static Optional<codeview::DebugInfo> loadExistingBuildId(StringRef Path) {
+ // We don't need to incrementally update a previous build id if we're not
+ // writing codeview debug info.
+ if (!Config->Debug)
+ return None;
-bool Defined::isExecutable() {
- const auto X = IMAGE_SCN_MEM_EXECUTE;
- if (auto *D = dyn_cast<DefinedRegular>(this))
- return D->getChunk()->getOutputSection()->getPermissions() & X;
- return isa<DefinedImportThunk>(this);
-}
+ auto ExpectedBinary = llvm::object::createBinary(Path);
+ if (!ExpectedBinary) {
+ consumeError(ExpectedBinary.takeError());
+ return None;
+ }
-} // namespace coff
-} // namespace lld
+ auto Binary = std::move(*ExpectedBinary);
+ if (!Binary.getBinary()->isCOFF())
+ return None;
+
+ std::error_code EC;
+ COFFObjectFile File(Binary.getBinary()->getMemoryBufferRef(), EC);
+ if (EC)
+ return None;
+
+ // If the machine of the binary we're outputting doesn't match the machine
+ // of the existing binary, don't try to re-use the build id.
+ if (File.is64() != Config->is64() || File.getMachine() != Config->Machine)
+ return None;
+
+ for (const auto &DebugDir : File.debug_directories()) {
+ if (DebugDir.Type != IMAGE_DEBUG_TYPE_CODEVIEW)
+ continue;
+
+ const codeview::DebugInfo *ExistingDI = nullptr;
+ StringRef PDBFileName;
+ if (auto EC = File.getDebugPDBInfo(ExistingDI, PDBFileName)) {
+ (void)EC;
+ return None;
+ }
+ // We only support writing PDBs in v70 format. So if this is not a build
+ // id that we recognize / support, ignore it.
+ if (ExistingDI->Signature.CVSignature != OMF::Signature::PDB70)
+ return None;
+ return *ExistingDI;
+ }
+ return None;
+}
// The main function of the writer.
void Writer::run() {
@@ -244,6 +292,10 @@ void Writer::run() {
removeEmptySections();
setSectionPermissions();
createSymbolAndStringTable();
+
+ // We must do this before opening the output file, as it depends on being able
+ // to read the contents of the existing output file.
+ PreviousBuildId = loadExistingBuildId(Config->OutputFile);
openFile(Config->OutputFile);
if (Config->is64()) {
writeHeader<pe32plus_header>();
@@ -256,10 +308,9 @@ void Writer::run() {
writeBuildId();
if (!Config->PDBPath.empty() && Config->Debug) {
- const llvm::codeview::DebugInfo *DI = nullptr;
- if (Config->DebugTypes & static_cast<unsigned>(coff::DebugType::CV))
- DI = BuildId->DI;
- createPDB(Config->PDBPath, Symtab, SectionTable, DI);
+
+ assert(BuildId);
+ createPDB(Symtab, OutputSections, SectionTable, *BuildId->BuildId);
}
writeMapFile(OutputSections);
@@ -323,13 +374,13 @@ void Writer::createMiscChunks() {
if (Config->Debug) {
DebugDirectory = make<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 = make<CVDebugRecordChunk>();
-
- BuildId = Chunk;
- DebugRecords.push_back(Chunk);
- }
+ // Make a CVDebugRecordChunk even when /DEBUG:CV is not specified. We
+ // output a PDB no matter what, and this chunk provides the only means of
+ // allowing a debugger to match a PDB and an executable. So we need it even
+ // if we're ultimately not going to write CodeView data to the PDB.
+ auto *CVChunk = make<CVDebugRecordChunk>();
+ BuildId = CVChunk;
+ DebugRecords.push_back(CVChunk);
RData->addChunk(DebugDirectory);
for (Chunk *C : DebugRecords)
@@ -342,15 +393,22 @@ void Writer::createMiscChunks() {
std::set<Defined *> Handlers;
- for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) {
+ for (ObjFile *File : ObjFile::Instances) {
if (!File->SEHCompat)
return;
- for (SymbolBody *B : File->SEHandlers)
- Handlers.insert(cast<Defined>(B));
+ for (SymbolBody *B : File->SEHandlers) {
+ // Make sure the handler is still live. Assume all handlers are regular
+ // symbols.
+ auto *D = dyn_cast<DefinedRegular>(B);
+ if (D && D->getChunk()->isLive())
+ Handlers.insert(D);
+ }
}
- SEHTable = make<SEHTableChunk>(Handlers);
- RData->addChunk(SEHTable);
+ if (!Handlers.empty()) {
+ SEHTable = make<SEHTableChunk>(Handlers);
+ RData->addChunk(SEHTable);
+ }
}
// Create .idata section for the DLL-imported symbol table.
@@ -358,13 +416,13 @@ void Writer::createMiscChunks() {
// IdataContents class abstracted away the details for us,
// so we just let it create chunks and add them to the section.
void Writer::createImportTables() {
- if (Symtab->ImportFiles.empty())
+ if (ImportFile::Instances.empty())
return;
// Initialize DLLOrder so that import entries are ordered in
// the same order as in the command line. (That affects DLL
// initialization order, and this ordering is MSVC-compatible.)
- for (ImportFile *File : Symtab->ImportFiles) {
+ for (ImportFile *File : ImportFile::Instances) {
if (!File->Live)
continue;
@@ -374,7 +432,7 @@ void Writer::createImportTables() {
}
OutputSection *Text = createSection(".text");
- for (ImportFile *File : Symtab->ImportFiles) {
+ for (ImportFile *File : ImportFile::Instances) {
if (!File->Live)
continue;
@@ -442,19 +500,15 @@ 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))
+ if (isa<DefinedSynthetic>(Def))
return None;
+ // Don't write dead symbols or symbols in codeview sections to the symbol
+ // table.
+ if (!Def->isLive())
+ return None;
if (auto *D = dyn_cast<DefinedRegular>(Def))
- if (!D->getChunk()->isLive())
- return None;
-
- if (auto *Sym = dyn_cast<DefinedImportData>(Def))
- if (!Sym->File->Live)
- return None;
-
- if (auto *Sym = dyn_cast<DefinedImportThunk>(Def))
- if (!Sym->WrappedSym->File->Live)
+ if (D->getChunk()->isCodeView())
return None;
coff_symbol16 Sym;
@@ -511,7 +565,7 @@ void Writer::createSymbolAndStringTable() {
Sec->setStringTableOff(addEntryToStringTable(Name));
}
- for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) {
+ for (ObjFile *File : ObjFile::Instances) {
for (SymbolBody *B : File->getSymbols()) {
auto *D = dyn_cast<Defined>(B);
if (!D || D->WrittenToSymtab)
@@ -599,6 +653,15 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
auto *PE = reinterpret_cast<PEHeaderTy *>(Buf);
Buf += sizeof(*PE);
PE->Magic = Config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32;
+
+ // If {Major,Minor}LinkerVersion is left at 0.0, then for some
+ // reason signing the resulting PE file with Authenticode produces a
+ // signature that fails to validate on Windows 7 (but is OK on 10).
+ // Set it to 14.0, which is what VS2015 outputs, and which avoids
+ // that problem.
+ PE->MajorLinkerVersion = 14;
+ PE->MinorLinkerVersion = 0;
+
PE->ImageBase = Config->ImageBase;
PE->SectionAlignment = PageSize;
PE->FileAlignment = SectorSize;
@@ -622,19 +685,14 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
PE->SizeOfStackCommit = Config->StackCommit;
PE->SizeOfHeapReserve = Config->HeapReserve;
PE->SizeOfHeapCommit = Config->HeapCommit;
-
- // Import Descriptor Tables and Import Address Tables are merged
- // in our output. That's not compatible with the Binding feature
- // that is sort of prelinking. Setting this flag to make it clear
- // that our outputs are not for the Binding.
- PE->DLLCharacteristics = IMAGE_DLL_CHARACTERISTICS_NO_BIND;
-
if (Config->AppContainer)
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER;
if (Config->DynamicBase)
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;
if (Config->HighEntropyVA)
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA;
+ if (!Config->AllowBind)
+ PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_BIND;
if (Config->NxCompat)
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
if (!Config->AllowIsolation)
@@ -743,10 +801,13 @@ void Writer::openFile(StringRef Path) {
void Writer::fixSafeSEHSymbols() {
if (!SEHTable)
return;
- 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);
+ // Replace the absolute table symbol with a synthetic symbol pointing to the
+ // SEHTable chunk so that we can emit base relocations for it and resolve
+ // section relative relocations.
+ Symbol *T = Symtab->find("___safe_se_handler_table");
+ Symbol *C = Symtab->find("___safe_se_handler_count");
+ replaceBody<DefinedSynthetic>(T, T->body()->getName(), SEHTable);
+ cast<DefinedAbsolute>(C->body())->setVA(SEHTable->getSize() / 4);
}
// Handles /section options to allow users to overwrite
@@ -762,6 +823,10 @@ void Writer::setSectionPermissions() {
// Write section contents to a mmap'ed file.
void Writer::writeSections() {
+ // Record the section index that should be used when resolving a section
+ // relocation against an absolute symbol.
+ DefinedAbsolute::OutputSectionIndex = OutputSections.size() + 1;
+
uint8_t *Buf = Buffer->getBufferStart();
for (OutputSection *Sec : OutputSections) {
uint8_t *SecBuf = Buf + Sec->getFileOff();
@@ -775,6 +840,25 @@ void Writer::writeSections() {
}
}
+void Writer::writeBuildId() {
+ // If we're not writing a build id (e.g. because /debug is not specified),
+ // then just return;
+ if (!Config->Debug)
+ return;
+
+ assert(BuildId && "BuildId is not set!");
+
+ if (PreviousBuildId.hasValue()) {
+ *BuildId->BuildId = *PreviousBuildId;
+ BuildId->BuildId->PDB70.Age = BuildId->BuildId->PDB70.Age + 1;
+ return;
+ }
+
+ BuildId->BuildId->Signature.CVSignature = OMF::Signature::PDB70;
+ BuildId->BuildId->PDB70.Age = 1;
+ llvm::getRandomBytes(BuildId->BuildId->PDB70.Signature, 16);
+}
+
// Sort .pdata section contents according to PE/COFF spec 5.5.
void Writer::sortExceptionTable() {
OutputSection *Sec = findSection(".pdata");
@@ -798,26 +882,6 @@ 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;
-
- assert(BuildId->DI->Signature.CVSignature == OMF::Signature::PDB70 &&
- "only PDB 7.0 is supported");
- assert(sizeof(BuildId->DI->PDB70.Signature) == 16 &&
- "signature size mismatch");
-
- // Compute an MD5 hash.
- ArrayRef<uint8_t> Buf(Buffer->getBufferStart(), Buffer->getBufferEnd());
- memcpy(BuildId->DI->PDB70.Signature, MD5::hash(Buf).data(), 16);
-
- // TODO(compnerd) track the Age
- BuildId->DI->PDB70.Age = 1;
-}
-
OutputSection *Writer::findSection(StringRef Name) {
for (OutputSection *Sec : OutputSections)
if (Sec->getName() == Name)
diff --git a/COFF/Writer.h b/COFF/Writer.h
index fef575423..e423a8e80 100644
--- a/COFF/Writer.h
+++ b/COFF/Writer.h
@@ -18,11 +18,9 @@
namespace lld {
namespace coff {
-class SymbolTable;
-
static const int PageSize = 4096;
-void writeResult(SymbolTable *T);
+void writeResult();
// OutputSection represents a section in an output file. It's a
// container of chunks. OutputSection and Chunk are 1:N relationship.
diff --git a/Common/CMakeLists.txt b/Common/CMakeLists.txt
new file mode 100644
index 000000000..46873f808
--- /dev/null
+++ b/Common/CMakeLists.txt
@@ -0,0 +1,26 @@
+if(NOT LLD_BUILT_STANDALONE)
+ set(tablegen_deps intrinsics_gen)
+endif()
+
+add_lld_library(lldCommon
+ Reproduce.cpp
+ TargetOptionsCommandFlags.cpp
+ Threads.cpp
+ Version.cpp
+
+ ADDITIONAL_HEADER_DIRS
+ ${LLD_INCLUDE_DIR}/lld/Common
+
+ LINK_COMPONENTS
+ Codegen
+ MC
+ Option
+ Support
+ Target
+
+ LINK_LIBS
+ ${LLVM_PTHREAD_LIB}
+
+ DEPENDS
+ ${tablegen_deps}
+ )
diff --git a/lib/Core/Reproduce.cpp b/Common/Reproduce.cpp
index e3629a93c..1e3dabaea 100644
--- a/lib/Core/Reproduce.cpp
+++ b/Common/Reproduce.cpp
@@ -7,7 +7,7 @@
//
//===----------------------------------------------------------------------===//
-#include "lld/Core/Reproduce.h"
+#include "lld/Common/Reproduce.h"
#include "llvm/Option/Arg.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
@@ -44,9 +44,9 @@ std::string lld::relativeToRoot(StringRef Path) {
// 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();
+ if (S.contains(' '))
+ return ("\"" + S + "\"").str();
+ return S;
}
std::string lld::rewritePath(StringRef S) {
diff --git a/lib/Core/TargetOptionsCommandFlags.cpp b/Common/TargetOptionsCommandFlags.cpp
index e0f26761e..0115ee2b4 100644
--- a/lib/Core/TargetOptionsCommandFlags.cpp
+++ b/Common/TargetOptionsCommandFlags.cpp
@@ -14,7 +14,7 @@
//
//===----------------------------------------------------------------------===//
-#include "lld/Core/TargetOptionsCommandFlags.h"
+#include "lld/Common/TargetOptionsCommandFlags.h"
#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/Target/TargetOptions.h"
@@ -27,6 +27,6 @@ llvm::TargetOptions lld::InitTargetOptionsFromCodeGenFlags() {
return ::InitTargetOptionsFromCodeGenFlags();
}
-llvm::CodeModel::Model lld::GetCodeModelFromCMModel() {
- return CMModel;
+llvm::Optional<llvm::CodeModel::Model> lld::GetCodeModelFromCMModel() {
+ return getCodeModel();
}
diff --git a/Common/Threads.cpp b/Common/Threads.cpp
new file mode 100644
index 000000000..2667683a1
--- /dev/null
+++ b/Common/Threads.cpp
@@ -0,0 +1,32 @@
+//===- Threads.cpp --------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Common/Threads.h"
+#include <thread>
+#include <vector>
+
+static std::vector<std::thread> Threads;
+
+bool lld::ThreadsEnabled = true;
+
+// Runs a given function in a new thread.
+void lld::runBackground(std::function<void()> Fn) {
+ Threads.emplace_back(Fn);
+}
+
+// Wait for all threads spawned for runBackground() to finish.
+//
+// You need to call this function from the main thread before exiting
+// because it is not defined what will happen to non-main threads when
+// the main thread exits.
+void lld::waitForBackgroundThreads() {
+ for (std::thread &T : Threads)
+ if (T.joinable())
+ T.join();
+}
diff --git a/lib/Config/Version.cpp b/Common/Version.cpp
index 25544756f..6226c9a2f 100644
--- a/lib/Config/Version.cpp
+++ b/Common/Version.cpp
@@ -1,4 +1,4 @@
-//===- lib/Config/Version.cpp - LLD Version Number ---------------*- C++-=====//
+//===- lib/Common/Version.cpp - LLD Version Number ---------------*- C++-=====//
//
// The LLVM Compiler Infrastructure
//
@@ -11,7 +11,7 @@
//
//===----------------------------------------------------------------------===//
-#include "lld/Config/Version.h"
+#include "lld/Common/Version.h"
using namespace llvm;
diff --git a/ELF/Arch/AArch64.cpp b/ELF/Arch/AArch64.cpp
index b26cf0815..49312c57c 100644
--- a/ELF/Arch/AArch64.cpp
+++ b/ELF/Arch/AArch64.cpp
@@ -32,20 +32,20 @@ namespace {
class AArch64 final : public TargetInfo {
public:
AArch64();
- RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ RelExpr getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const override;
- bool isPicRel(uint32_t Type) const override;
+ bool isPicRel(RelType Type) const override;
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
void writePltHeader(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
- bool usesOnlyLowPageBits(uint32_t Type) const override;
- void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
- RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
+ bool usesOnlyLowPageBits(RelType Type) const override;
+ void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
RelExpr Expr) const override;
- void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
- void relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
- void relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
+ void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
};
} // namespace
@@ -68,11 +68,9 @@ AArch64::AArch64() {
TcbSize = 16;
}
-RelExpr AArch64::getRelExpr(uint32_t Type, const SymbolBody &S,
+RelExpr AArch64::getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const {
switch (Type) {
- default:
- return R_ABS;
case R_AARCH64_TLSDESC_ADR_PAGE21:
return R_TLSDESC_PAGE;
case R_AARCH64_TLSDESC_LD64_LO12:
@@ -92,6 +90,7 @@ RelExpr AArch64::getRelExpr(uint32_t Type, const SymbolBody &S,
case R_AARCH64_PREL32:
case R_AARCH64_PREL64:
case R_AARCH64_ADR_PREL_LO21:
+ case R_AARCH64_LD_PREL_LO19:
return R_PC;
case R_AARCH64_ADR_PREL_PG_HI21:
return R_PAGE_PC;
@@ -103,10 +102,12 @@ RelExpr AArch64::getRelExpr(uint32_t Type, const SymbolBody &S,
return R_GOT_PAGE_PC;
case R_AARCH64_NONE:
return R_NONE;
+ default:
+ return R_ABS;
}
}
-RelExpr AArch64::adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
+RelExpr AArch64::adjustRelaxExpr(RelType Type, const uint8_t *Data,
RelExpr Expr) const {
if (Expr == R_RELAX_TLS_GD_TO_IE) {
if (Type == R_AARCH64_TLSDESC_ADR_PAGE21)
@@ -116,7 +117,7 @@ RelExpr AArch64::adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
return Expr;
}
-bool AArch64::usesOnlyLowPageBits(uint32_t Type) const {
+bool AArch64::usesOnlyLowPageBits(RelType Type) const {
switch (Type) {
default:
return false;
@@ -134,7 +135,7 @@ bool AArch64::usesOnlyLowPageBits(uint32_t Type) const {
}
}
-bool AArch64::isPicRel(uint32_t Type) const {
+bool AArch64::isPicRel(RelType Type) const {
return Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64;
}
@@ -201,7 +202,7 @@ static void or32AArch64Imm(uint8_t *L, uint64_t Imm) {
or32le(L, (Imm & 0xFFF) << 10);
}
-void AArch64::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_AARCH64_ABS16:
case R_AARCH64_PREL16:
@@ -232,12 +233,23 @@ void AArch64::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
checkInt<21>(Loc, Val, Type);
write32AArch64Addr(Loc, Val);
break;
- case R_AARCH64_CALL26:
case R_AARCH64_JUMP26:
+ // Normally we would just write the bits of the immediate field, however
+ // when patching instructions for the cpu errata fix -fix-cortex-a53-843419
+ // we want to replace a non-branch instruction with a branch immediate
+ // instruction. By writing all the bits of the instruction including the
+ // opcode and the immediate (0 001 | 01 imm26) we can do this
+ // transformation by placing a R_AARCH64_JUMP26 relocation at the offset of
+ // the instruction we want to patch.
+ write32le(Loc, 0x14000000);
+ LLVM_FALLTHROUGH;
+ case R_AARCH64_CALL26:
checkInt<28>(Loc, Val, Type);
or32le(Loc, (Val & 0x0FFFFFFC) >> 2);
break;
case R_AARCH64_CONDBR19:
+ case R_AARCH64_LD_PREL_LO19:
+ checkAlignment<4>(Loc, Val, Type);
checkInt<21>(Loc, Val, Type);
or32le(Loc, (Val & 0x1FFFFC) << 3);
break;
@@ -251,15 +263,19 @@ void AArch64::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
or32AArch64Imm(Loc, getBits(Val, 0, 11));
break;
case R_AARCH64_LDST16_ABS_LO12_NC:
+ checkAlignment<2>(Loc, Val, Type);
or32AArch64Imm(Loc, getBits(Val, 1, 11));
break;
case R_AARCH64_LDST32_ABS_LO12_NC:
+ checkAlignment<4>(Loc, Val, Type);
or32AArch64Imm(Loc, getBits(Val, 2, 11));
break;
case R_AARCH64_LDST64_ABS_LO12_NC:
+ checkAlignment<8>(Loc, Val, Type);
or32AArch64Imm(Loc, getBits(Val, 3, 11));
break;
case R_AARCH64_LDST128_ABS_LO12_NC:
+ checkAlignment<16>(Loc, Val, Type);
or32AArch64Imm(Loc, getBits(Val, 4, 11));
break;
case R_AARCH64_MOVW_UABS_G0_NC:
@@ -291,7 +307,7 @@ void AArch64::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
}
}
-void AArch64::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void AArch64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// TLSDESC Global-Dynamic relocation are in the form:
// adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21]
// ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12]
@@ -321,7 +337,7 @@ void AArch64::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
}
}
-void AArch64::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void AArch64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// TLSDESC Global-Dynamic relocation are in the form:
// adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21]
// ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12]
@@ -352,7 +368,7 @@ void AArch64::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
}
}
-void AArch64::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void AArch64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
checkUInt<32>(Loc, Val, Type);
if (Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
diff --git a/ELF/Arch/AMDGPU.cpp b/ELF/Arch/AMDGPU.cpp
index de566c617..a969a84d2 100644
--- a/ELF/Arch/AMDGPU.cpp
+++ b/ELF/Arch/AMDGPU.cpp
@@ -25,19 +25,19 @@ namespace {
class AMDGPU final : public TargetInfo {
public:
AMDGPU();
- void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
- RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ RelExpr getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const override;
};
} // namespace
AMDGPU::AMDGPU() {
- RelativeRel = R_AMDGPU_REL64;
+ RelativeRel = R_AMDGPU_RELATIVE64;
GotRel = R_AMDGPU_ABS64;
GotEntrySize = 8;
}
-void AMDGPU::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void AMDGPU::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_AMDGPU_ABS32:
case R_AMDGPU_GOTPCREL:
@@ -58,7 +58,7 @@ void AMDGPU::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
}
}
-RelExpr AMDGPU::getRelExpr(uint32_t Type, const SymbolBody &S,
+RelExpr AMDGPU::getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const {
switch (Type) {
case R_AMDGPU_ABS32:
@@ -73,8 +73,7 @@ RelExpr AMDGPU::getRelExpr(uint32_t Type, const SymbolBody &S,
case R_AMDGPU_GOTPCREL32_HI:
return R_GOT_PC;
default:
- error(toString(S.File) + ": unknown relocation type: " + toString(Type));
- return R_HINT;
+ return R_INVALID;
}
}
diff --git a/ELF/Arch/ARM.cpp b/ELF/Arch/ARM.cpp
index a8a5ca151..3fca467a1 100644
--- a/ELF/Arch/ARM.cpp
+++ b/ELF/Arch/ARM.cpp
@@ -26,11 +26,11 @@ namespace {
class ARM final : public TargetInfo {
public:
ARM();
- RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ RelExpr getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const override;
- bool isPicRel(uint32_t Type) const override;
- uint32_t getDynRel(uint32_t Type) const override;
- int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
+ bool isPicRel(RelType Type) const override;
+ RelType getDynRel(RelType Type) const override;
+ int64_t getImplicitAddend(const uint8_t *Buf, RelType 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;
@@ -38,9 +38,10 @@ public:
int32_t Index, unsigned RelOff) const override;
void addPltSymbols(InputSectionBase *IS, uint64_t Off) const override;
void addPltHeaderSymbols(InputSectionBase *ISD) const override;
- bool needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File,
+ bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
const SymbolBody &S) const override;
- void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
+ bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override;
+ void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
};
} // namespace
@@ -57,16 +58,15 @@ ARM::ARM() {
GotPltEntrySize = 4;
PltEntrySize = 16;
PltHeaderSize = 20;
+ TrapInstr = 0xd4d4d4d4;
// ARM uses Variant 1 TLS
TcbSize = 8;
NeedsThunks = true;
}
-RelExpr ARM::getRelExpr(uint32_t Type, const SymbolBody &S,
+RelExpr ARM::getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const {
switch (Type) {
- default:
- return R_ABS;
case R_ARM_THM_JUMP11:
return R_PC;
case R_ARM_CALL:
@@ -117,15 +117,17 @@ RelExpr ARM::getRelExpr(uint32_t Type, const SymbolBody &S,
return R_NONE;
case R_ARM_TLS_LE32:
return R_TLS;
+ default:
+ return R_ABS;
}
}
-bool ARM::isPicRel(uint32_t Type) const {
+bool ARM::isPicRel(RelType Type) const {
return (Type == R_ARM_TARGET1 && !Config->Target1Rel) ||
(Type == R_ARM_ABS32);
}
-uint32_t ARM::getDynRel(uint32_t Type) const {
+RelType ARM::getDynRel(RelType Type) const {
if (Type == R_ARM_TARGET1 && !Config->Target1Rel)
return R_ARM_ABS32;
if (Type == R_ARM_ABS32)
@@ -186,18 +188,17 @@ void ARM::addPltSymbols(InputSectionBase *ISD, uint64_t Off) const {
addSyntheticLocal("$d", STT_NOTYPE, Off + 12, 0, IS);
}
-bool ARM::needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File,
+bool ARM::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
const SymbolBody &S) const {
// If S is an undefined weak symbol in an executable we don't need a Thunk.
// In a DSO calls to undefined symbols, including weak ones get PLT entries
// which may need a thunk.
- if (S.isUndefined() && !S.isLocal() && S.symbol()->isWeak() &&
- !Config->Shared)
+ if (S.isUndefWeak() && !Config->Shared)
return false;
// 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.
- switch (RelocType) {
+ switch (Type) {
case R_ARM_PC24:
case R_ARM_PLT32:
case R_ARM_JUMP24:
@@ -217,7 +218,50 @@ bool ARM::needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File,
return false;
}
-void ARM::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+bool ARM::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
+ uint64_t Range;
+ uint64_t InstrSize;
+
+ switch (Type) {
+ case R_ARM_PC24:
+ case R_ARM_PLT32:
+ case R_ARM_JUMP24:
+ case R_ARM_CALL:
+ Range = 0x2000000;
+ InstrSize = 4;
+ break;
+ case R_ARM_THM_JUMP19:
+ Range = 0x100000;
+ InstrSize = 2;
+ break;
+ case R_ARM_THM_JUMP24:
+ case R_ARM_THM_CALL:
+ Range = 0x1000000;
+ InstrSize = 2;
+ break;
+ default:
+ return true;
+ }
+ // PC at Src is 2 instructions ahead, immediate of branch is signed
+ if (Src > Dst)
+ Range -= 2 * InstrSize;
+ else
+ Range += InstrSize;
+
+ if ((Dst & 0x1) == 0)
+ // Destination is ARM, if ARM caller then Src is already 4-byte aligned.
+ // If Thumb Caller (BLX) the Src address has bottom 2 bits cleared to ensure
+ // destination will be 4 byte aligned.
+ Src &= ~0x3;
+ else
+ // Bit 0 == 1 denotes Thumb state, it is not part of the range
+ Dst &= ~0x1;
+
+ uint64_t Distance = (Src > Dst) ? Src - Dst : Dst - Src;
+ return Distance <= Range;
+}
+
+void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_ARM_ABS32:
case R_ARM_BASE_PREL:
@@ -354,7 +398,7 @@ void ARM::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
}
}
-int64_t ARM::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
+int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
switch (Type) {
default:
return 0;
diff --git a/ELF/Arch/AVR.cpp b/ELF/Arch/AVR.cpp
index 3853248f8..a85e4783d 100644
--- a/ELF/Arch/AVR.cpp
+++ b/ELF/Arch/AVR.cpp
@@ -43,24 +43,18 @@ using namespace lld::elf;
namespace {
class AVR final : public TargetInfo {
public:
- RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ RelExpr getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const override;
- void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
+ void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
};
} // namespace
-RelExpr AVR::getRelExpr(uint32_t Type, const SymbolBody &S,
+RelExpr AVR::getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const {
- switch (Type) {
- case R_AVR_CALL:
- return R_ABS;
- default:
- error(toString(S.File) + ": unknown relocation type: " + toString(Type));
- return R_HINT;
- }
+ return R_ABS;
}
-void AVR::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void AVR::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_AVR_CALL: {
uint16_t Hi = Val >> 17;
diff --git a/ELF/Arch/Mips.cpp b/ELF/Arch/Mips.cpp
index 422e5db8c..5f232dcff 100644
--- a/ELF/Arch/Mips.cpp
+++ b/ELF/Arch/Mips.cpp
@@ -28,19 +28,19 @@ namespace {
template <class ELFT> class MIPS final : public TargetInfo {
public:
MIPS();
- RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ RelExpr getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const override;
- int64_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;
+ int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override;
+ bool isPicRel(RelType Type) const override;
+ RelType getDynRel(RelType Type) const override;
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
void writePltHeader(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
- bool needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File,
+ bool needsThunk(RelExpr Expr, RelType Type, 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;
+ void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ bool usesOnlyLowPageBits(RelType Type) const override;
};
} // namespace
@@ -54,6 +54,7 @@ template <class ELFT> MIPS<ELFT>::MIPS() {
CopyRel = R_MIPS_COPY;
PltRel = R_MIPS_JUMP_SLOT;
NeedsThunks = true;
+ TrapInstr = 0xefefefef;
if (ELFT::Is64Bits) {
RelativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32;
@@ -69,23 +70,34 @@ template <class ELFT> MIPS<ELFT>::MIPS() {
}
template <class ELFT>
-RelExpr MIPS<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S,
+RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const {
// See comment in the calculateMipsRelChain.
if (ELFT::Is64Bits || Config->MipsN32Abi)
Type &= 0xff;
+
switch (Type) {
- default:
- return R_ABS;
case R_MIPS_JALR:
+ case R_MICROMIPS_JALR:
return R_HINT;
case R_MIPS_GPREL16:
case R_MIPS_GPREL32:
+ case R_MICROMIPS_GPREL16:
+ case R_MICROMIPS_GPREL7_S2:
return R_MIPS_GOTREL;
case R_MIPS_26:
+ case R_MICROMIPS_26_S1:
return R_PLT;
+ case R_MICROMIPS_PC26_S1:
+ return R_PLT_PC;
case R_MIPS_HI16:
case R_MIPS_LO16:
+ case R_MIPS_HIGHER:
+ case R_MIPS_HIGHEST:
+ case R_MICROMIPS_HI16:
+ case R_MICROMIPS_LO16:
+ case R_MICROMIPS_HIGHER:
+ case R_MICROMIPS_HIGHEST:
// 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
@@ -95,7 +107,24 @@ RelExpr MIPS<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S,
if (&S == ElfSym::MipsLocalGp)
return R_MIPS_GOT_GP;
LLVM_FALLTHROUGH;
+ case R_MIPS_32:
+ case R_MIPS_64:
case R_MIPS_GOT_OFST:
+ case R_MIPS_SUB:
+ case R_MIPS_TLS_DTPREL_HI16:
+ case R_MIPS_TLS_DTPREL_LO16:
+ case R_MIPS_TLS_DTPREL32:
+ case R_MIPS_TLS_DTPREL64:
+ case R_MIPS_TLS_TPREL_HI16:
+ case R_MIPS_TLS_TPREL_LO16:
+ case R_MIPS_TLS_TPREL32:
+ case R_MIPS_TLS_TPREL64:
+ case R_MICROMIPS_GOT_OFST:
+ case R_MICROMIPS_SUB:
+ case R_MICROMIPS_TLS_DTPREL_HI16:
+ case R_MICROMIPS_TLS_DTPREL_LO16:
+ case R_MICROMIPS_TLS_TPREL_HI16:
+ case R_MICROMIPS_TLS_TPREL_LO16:
return R_ABS;
case R_MIPS_PC32:
case R_MIPS_PC16:
@@ -104,34 +133,56 @@ RelExpr MIPS<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S,
case R_MIPS_PC26_S2:
case R_MIPS_PCHI16:
case R_MIPS_PCLO16:
+ case R_MICROMIPS_PC7_S1:
+ case R_MICROMIPS_PC10_S1:
+ case R_MICROMIPS_PC16_S1:
+ case R_MICROMIPS_PC18_S3:
+ case R_MICROMIPS_PC19_S2:
+ case R_MICROMIPS_PC23_S2:
+ case R_MICROMIPS_PC21_S1:
return R_PC;
case R_MIPS_GOT16:
+ case R_MICROMIPS_GOT16:
if (S.isLocal())
return R_MIPS_GOT_LOCAL_PAGE;
LLVM_FALLTHROUGH;
case R_MIPS_CALL16:
case R_MIPS_GOT_DISP:
case R_MIPS_TLS_GOTTPREL:
+ case R_MICROMIPS_CALL16:
+ case R_MICROMIPS_GOT_DISP:
+ case R_MICROMIPS_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:
+ case R_MICROMIPS_CALL_HI16:
+ case R_MICROMIPS_CALL_LO16:
+ case R_MICROMIPS_GOT_HI16:
+ case R_MICROMIPS_GOT_LO16:
return R_MIPS_GOT_OFF32;
case R_MIPS_GOT_PAGE:
+ case R_MICROMIPS_GOT_PAGE:
return R_MIPS_GOT_LOCAL_PAGE;
case R_MIPS_TLS_GD:
+ case R_MICROMIPS_TLS_GD:
return R_MIPS_TLSGD;
case R_MIPS_TLS_LDM:
+ case R_MICROMIPS_TLS_LDM:
return R_MIPS_TLSLD;
+ case R_MIPS_NONE:
+ return R_NONE;
+ default:
+ return R_INVALID;
}
}
-template <class ELFT> bool MIPS<ELFT>::isPicRel(uint32_t Type) const {
+template <class ELFT> bool MIPS<ELFT>::isPicRel(RelType Type) const {
return Type == R_MIPS_32 || Type == R_MIPS_64;
}
-template <class ELFT> uint32_t MIPS<ELFT>::getDynRel(uint32_t Type) const {
+template <class ELFT> RelType MIPS<ELFT>::getDynRel(RelType Type) const {
return RelativeRel;
}
@@ -140,54 +191,82 @@ void MIPS<ELFT>::writeGotPlt(uint8_t *Buf, const SymbolBody &) const {
write32<ELFT::TargetEndianness>(Buf, InX::Plt->getVA());
}
-template <endianness E, uint8_t BSIZE, uint8_t SHIFT>
-static int64_t getPcRelocAddend(const uint8_t *Loc) {
- uint32_t Instr = read32<E>(Loc);
- uint32_t Mask = 0xffffffff >> (32 - BSIZE);
- return SignExtend64<BSIZE + SHIFT>((Instr & Mask) << SHIFT);
+template <endianness E> static uint32_t readShuffle(const uint8_t *Loc) {
+ // The major opcode of a microMIPS instruction needs to appear
+ // in the first 16-bit word (lowest address) for efficient hardware
+ // decode so that it knows if the instruction is 16-bit or 32-bit
+ // as early as possible. To do so, little-endian binaries keep 16-bit
+ // words in a big-endian order. That is why we have to swap these
+ // words to get a correct value.
+ uint32_t V = read32<E>(Loc);
+ if (E == support::little)
+ return (V << 16) | (V >> 16);
+ return V;
}
-template <endianness E, uint8_t BSIZE, uint8_t SHIFT>
-static void applyMipsPcReloc(uint8_t *Loc, uint32_t Type, uint64_t V) {
- uint32_t Mask = 0xffffffff >> (32 - BSIZE);
+template <endianness E>
+static void writeRelocation(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
+ uint8_t Shift) {
uint32_t Instr = read32<E>(Loc);
- if (SHIFT > 0)
- checkAlignment<(1 << SHIFT)>(Loc, V, Type);
- checkInt<BSIZE + SHIFT>(Loc, V, Type);
- write32<E>(Loc, (Instr & ~Mask) | ((V >> SHIFT) & Mask));
+ uint32_t Mask = 0xffffffff >> (32 - BitsSize);
+ uint32_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask);
+ write32<E>(Loc, Data);
}
-template <endianness E> static void writeMipsHi16(uint8_t *Loc, uint64_t V) {
- uint32_t Instr = read32<E>(Loc);
- uint16_t Res = ((V + 0x8000) >> 16) & 0xffff;
- write32<E>(Loc, (Instr & 0xffff0000) | Res);
-}
+template <endianness E>
+static void writeMicroRelocation32(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
+ uint8_t Shift) {
+ // See comments in readShuffle for purpose of this code.
+ uint16_t *Words = (uint16_t *)Loc;
+ if (E == support::little)
+ std::swap(Words[0], Words[1]);
-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);
-}
+ writeRelocation<E>(Loc, V, BitsSize, Shift);
-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);
+ if (E == support::little)
+ std::swap(Words[0], Words[1]);
}
-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 <endianness E>
+static void writeMicroRelocation16(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
+ uint8_t Shift) {
+ uint16_t Instr = read16<E>(Loc);
+ uint16_t Mask = 0xffff >> (16 - BitsSize);
+ uint16_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask);
+ write16<E>(Loc, Data);
}
-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;
-}
+static bool isMicroMips() { return Config->MipsEFlags & EF_MIPS_MICROMIPS; }
template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const {
const endianness E = ELFT::TargetEndianness;
+ if (isMicroMips()) {
+ uint64_t GotPlt = In<ELFT>::GotPlt->getVA();
+ uint64_t Plt = In<ELFT>::Plt->getVA();
+ // Overwrite trap instructions written by Writer::writeTrapInstr.
+ memset(Buf, 0, PltHeaderSize);
+
+ write16<E>(Buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - .
+ write16<E>(Buf + 4, 0xff23); // lw $25, 0($3)
+ write16<E>(Buf + 8, 0x0535); // subu16 $2, $2, $3
+ write16<E>(Buf + 10, 0x2525); // srl16 $2, $2, 2
+ write16<E>(Buf + 12, 0x3302); // addiu $24, $2, -2
+ write16<E>(Buf + 14, 0xfffe);
+ write16<E>(Buf + 16, 0x0dff); // move $15, $31
+ if (isMipsR6()) {
+ write16<E>(Buf + 18, 0x0f83); // move $28, $3
+ write16<E>(Buf + 20, 0x472b); // jalrc $25
+ write16<E>(Buf + 22, 0x0c00); // nop
+ relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPlt - Plt);
+ } else {
+ write16<E>(Buf + 18, 0x45f9); // jalrc $25
+ write16<E>(Buf + 20, 0x0f83); // move $28, $3
+ write16<E>(Buf + 22, 0x0c00); // nop
+ relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPlt - Plt);
+ }
+ return;
+ }
+
if (Config->MipsN32Abi) {
write32<E>(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
write32<E>(Buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14)
@@ -206,9 +285,9 @@ template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const {
write32<E>(Buf + 28, 0x2718fffe); // subu $24, $24, 2
uint64_t GotPlt = InX::GotPlt->getVA();
- writeMipsHi16<E>(Buf, GotPlt);
- writeMipsLo16<E>(Buf + 4, GotPlt);
- writeMipsLo16<E>(Buf + 8, GotPlt);
+ writeRelocation<E>(Buf, GotPlt + 0x8000, 16, 16);
+ writeRelocation<E>(Buf + 4, GotPlt, 16, 0);
+ writeRelocation<E>(Buf + 8, GotPlt, 16, 0);
}
template <class ELFT>
@@ -216,25 +295,45 @@ void MIPS<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
uint64_t PltEntryAddr, int32_t Index,
unsigned RelOff) const {
const endianness E = ELFT::TargetEndianness;
+ if (isMicroMips()) {
+ // Overwrite trap instructions written by Writer::writeTrapInstr.
+ memset(Buf, 0, PltEntrySize);
+
+ if (isMipsR6()) {
+ write16<E>(Buf, 0x7840); // addiupc $2, (GOTPLT) - .
+ write16<E>(Buf + 4, 0xff22); // lw $25, 0($2)
+ write16<E>(Buf + 8, 0x0f02); // move $24, $2
+ write16<E>(Buf + 10, 0x4723); // jrc $25 / jr16 $25
+ relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPltEntryAddr - PltEntryAddr);
+ } else {
+ write16<E>(Buf, 0x7900); // addiupc $2, (GOTPLT) - .
+ write16<E>(Buf + 4, 0xff22); // lw $25, 0($2)
+ write16<E>(Buf + 8, 0x4599); // jrc $25 / jr16 $25
+ write16<E>(Buf + 10, 0x0f02); // move $24, $2
+ relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPltEntryAddr - PltEntryAddr);
+ }
+ return;
+ }
+
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 + 8, isMipsR6() ? 0x03200009 : 0x03200008); // jr $25
write32<E>(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry)
- writeMipsHi16<E>(Buf, GotPltEntryAddr);
- writeMipsLo16<E>(Buf + 4, GotPltEntryAddr);
- writeMipsLo16<E>(Buf + 12, GotPltEntryAddr);
+ writeRelocation<E>(Buf, GotPltEntryAddr + 0x8000, 16, 16);
+ writeRelocation<E>(Buf + 4, GotPltEntryAddr, 16, 0);
+ writeRelocation<E>(Buf + 12, GotPltEntryAddr, 16, 0);
}
template <class ELFT>
-bool MIPS<ELFT>::needsThunk(RelExpr Expr, uint32_t Type, const InputFile *File,
+bool MIPS<ELFT>::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
const SymbolBody &S) const {
// Any MIPS PIC code function is invoked with its address in register $t9.
// So if we have a branch instruction from non-PIC code to the PIC one
// we cannot make the jump directly and need to create a small stubs
// to save the target function address.
// See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- if (Type != R_MIPS_26)
+ if (Type != R_MIPS_26 && Type != R_MICROMIPS_26_S1 &&
+ Type != R_MICROMIPS_PC26_S1)
return false;
auto *F = dyn_cast_or_null<ELFFileBase<ELFT>>(File);
if (!F)
@@ -249,11 +348,9 @@ bool MIPS<ELFT>::needsThunk(RelExpr Expr, uint32_t Type, const InputFile *File,
}
template <class ELFT>
-int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
+int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
const endianness E = ELFT::TargetEndianness;
switch (Type) {
- default:
- return 0;
case R_MIPS_32:
case R_MIPS_GPREL32:
case R_MIPS_TLS_DTPREL32:
@@ -263,7 +360,11 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
// 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) & 0x3ffffff) << 2);
+ return SignExtend64<28>(read32<E>(Buf) << 2);
+ case R_MIPS_GOT16:
+ case R_MIPS_HI16:
+ case R_MIPS_PCHI16:
+ return SignExtend64<16>(read32<E>(Buf)) << 16;
case R_MIPS_GPREL16:
case R_MIPS_LO16:
case R_MIPS_PCLO16:
@@ -272,21 +373,53 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
case R_MIPS_TLS_TPREL_HI16:
case R_MIPS_TLS_TPREL_LO16:
return SignExtend64<16>(read32<E>(Buf));
+ case R_MICROMIPS_GOT16:
+ case R_MICROMIPS_HI16:
+ return SignExtend64<16>(readShuffle<E>(Buf)) << 16;
+ case R_MICROMIPS_GPREL16:
+ case R_MICROMIPS_LO16:
+ case R_MICROMIPS_TLS_DTPREL_HI16:
+ case R_MICROMIPS_TLS_DTPREL_LO16:
+ case R_MICROMIPS_TLS_TPREL_HI16:
+ case R_MICROMIPS_TLS_TPREL_LO16:
+ return SignExtend64<16>(readShuffle<E>(Buf));
+ case R_MICROMIPS_GPREL7_S2:
+ return SignExtend64<9>(readShuffle<E>(Buf) << 2);
case R_MIPS_PC16:
- return getPcRelocAddend<E, 16, 2>(Buf);
+ return SignExtend64<18>(read32<E>(Buf) << 2);
case R_MIPS_PC19_S2:
- return getPcRelocAddend<E, 19, 2>(Buf);
+ return SignExtend64<21>(read32<E>(Buf) << 2);
case R_MIPS_PC21_S2:
- return getPcRelocAddend<E, 21, 2>(Buf);
+ return SignExtend64<23>(read32<E>(Buf) << 2);
case R_MIPS_PC26_S2:
- return getPcRelocAddend<E, 26, 2>(Buf);
+ return SignExtend64<28>(read32<E>(Buf) << 2);
case R_MIPS_PC32:
- return getPcRelocAddend<E, 32, 0>(Buf);
+ return SignExtend64<32>(read32<E>(Buf));
+ case R_MICROMIPS_26_S1:
+ return SignExtend64<27>(readShuffle<E>(Buf) << 1);
+ case R_MICROMIPS_PC7_S1:
+ return SignExtend64<8>(read16<E>(Buf) << 1);
+ case R_MICROMIPS_PC10_S1:
+ return SignExtend64<11>(read16<E>(Buf) << 1);
+ case R_MICROMIPS_PC16_S1:
+ return SignExtend64<17>(readShuffle<E>(Buf) << 1);
+ case R_MICROMIPS_PC18_S3:
+ return SignExtend64<21>(readShuffle<E>(Buf) << 3);
+ case R_MICROMIPS_PC19_S2:
+ return SignExtend64<21>(readShuffle<E>(Buf) << 2);
+ case R_MICROMIPS_PC21_S1:
+ return SignExtend64<22>(readShuffle<E>(Buf) << 1);
+ case R_MICROMIPS_PC23_S2:
+ return SignExtend64<25>(readShuffle<E>(Buf) << 2);
+ case R_MICROMIPS_PC26_S1:
+ return SignExtend64<27>(readShuffle<E>(Buf) << 1);
+ default:
+ return 0;
}
}
static std::pair<uint32_t, uint64_t>
-calculateMipsRelChain(uint8_t *Loc, uint32_t Type, uint64_t Val) {
+calculateMipsRelChain(uint8_t *Loc, RelType 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,
@@ -299,32 +432,43 @@ calculateMipsRelChain(uint8_t *Loc, uint32_t Type, uint64_t Val) {
// relocations used to modify result of the first one: extend it to
// 64-bit, extract high or low part etc. For details, see part 2.9 Relocation
// at the https://dmz-portal.mips.com/mw/images/8/82/007-4658-001.pdf
- uint32_t Type2 = (Type >> 8) & 0xff;
- uint32_t Type3 = (Type >> 16) & 0xff;
+ RelType Type2 = (Type >> 8) & 0xff;
+ RelType Type3 = (Type >> 16) & 0xff;
if (Type2 == R_MIPS_NONE && Type3 == R_MIPS_NONE)
return std::make_pair(Type, Val);
if (Type2 == R_MIPS_64 && Type3 == R_MIPS_NONE)
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);
+ if (Type2 == R_MICROMIPS_SUB &&
+ (Type3 == R_MICROMIPS_HI16 || Type3 == R_MICROMIPS_LO16))
+ return std::make_pair(Type3, -Val);
error(getErrorLocation(Loc) + "unsupported relocations combination " +
Twine(Type));
return std::make_pair(Type & 0xff, Val);
}
template <class ELFT>
-void MIPS<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
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 ||
- Type == R_MIPS_TLS_DTPREL32 || Type == R_MIPS_TLS_DTPREL64)
+ Type == R_MIPS_TLS_DTPREL32 || Type == R_MIPS_TLS_DTPREL64 ||
+ Type == R_MICROMIPS_TLS_DTPREL_HI16 ||
+ Type == R_MICROMIPS_TLS_DTPREL_LO16) {
Val -= 0x8000;
- else if (Type == R_MIPS_TLS_TPREL_HI16 || Type == R_MIPS_TLS_TPREL_LO16 ||
- Type == R_MIPS_TLS_TPREL32 || Type == R_MIPS_TLS_TPREL64)
+ } else if (Type == R_MIPS_TLS_TPREL_HI16 || Type == R_MIPS_TLS_TPREL_LO16 ||
+ Type == R_MIPS_TLS_TPREL32 || Type == R_MIPS_TLS_TPREL64 ||
+ Type == R_MICROMIPS_TLS_TPREL_HI16 ||
+ Type == R_MICROMIPS_TLS_TPREL_LO16) {
Val -= 0x7000;
+ }
+
if (ELFT::Is64Bits || Config->MipsN32Abi)
std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val);
+
switch (Type) {
case R_MIPS_32:
case R_MIPS_GPREL32:
@@ -338,36 +482,65 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
write64<E>(Loc, Val);
break;
case R_MIPS_26:
- write32<E>(Loc, (read32<E>(Loc) & ~0x3ffffff) | ((Val >> 2) & 0x3ffffff));
+ writeRelocation<E>(Loc, Val, 26, 2);
break;
case R_MIPS_GOT16:
// The R_MIPS_GOT16 relocation's value in "relocatable" linking mode
// is updated addend (not a GOT index). In that case write high 16 bits
// to store a correct addend value.
- if (Config->Relocatable)
- writeMipsHi16<E>(Loc, Val);
- else {
+ if (Config->Relocatable) {
+ writeRelocation<E>(Loc, Val + 0x8000, 16, 16);
+ } else {
+ checkInt<16>(Loc, Val, Type);
+ writeRelocation<E>(Loc, Val, 16, 0);
+ }
+ break;
+ case R_MICROMIPS_GOT16:
+ if (Config->Relocatable) {
+ writeMicroRelocation32<E>(Loc, Val + 0x8000, 16, 16);
+ } else {
checkInt<16>(Loc, Val, Type);
- writeMipsLo16<E>(Loc, Val);
+ writeMicroRelocation32<E>(Loc, Val, 16, 0);
}
break;
+ case R_MIPS_CALL16:
case R_MIPS_GOT_DISP:
case R_MIPS_GOT_PAGE:
case R_MIPS_GPREL16:
case R_MIPS_TLS_GD:
+ case R_MIPS_TLS_GOTTPREL:
case R_MIPS_TLS_LDM:
checkInt<16>(Loc, Val, Type);
LLVM_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:
case R_MIPS_TLS_DTPREL_LO16:
- case R_MIPS_TLS_GOTTPREL:
case R_MIPS_TLS_TPREL_LO16:
- writeMipsLo16<E>(Loc, Val);
+ writeRelocation<E>(Loc, Val, 16, 0);
+ break;
+ case R_MICROMIPS_GOT_DISP:
+ case R_MICROMIPS_GOT_PAGE:
+ case R_MICROMIPS_GPREL16:
+ case R_MICROMIPS_TLS_GD:
+ case R_MICROMIPS_TLS_LDM:
+ checkInt<16>(Loc, Val, Type);
+ writeMicroRelocation32<E>(Loc, Val, 16, 0);
+ break;
+ case R_MICROMIPS_CALL16:
+ case R_MICROMIPS_CALL_LO16:
+ case R_MICROMIPS_GOT_OFST:
+ case R_MICROMIPS_LO16:
+ case R_MICROMIPS_TLS_DTPREL_LO16:
+ case R_MICROMIPS_TLS_GOTTPREL:
+ case R_MICROMIPS_TLS_TPREL_LO16:
+ writeMicroRelocation32<E>(Loc, Val, 16, 0);
+ break;
+ case R_MICROMIPS_GPREL7_S2:
+ checkInt<7>(Loc, Val, Type);
+ writeMicroRelocation32<E>(Loc, Val, 7, 2);
break;
case R_MIPS_CALL_HI16:
case R_MIPS_GOT_HI16:
@@ -375,40 +548,95 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
case R_MIPS_PCHI16:
case R_MIPS_TLS_DTPREL_HI16:
case R_MIPS_TLS_TPREL_HI16:
- writeMipsHi16<E>(Loc, Val);
+ writeRelocation<E>(Loc, Val + 0x8000, 16, 16);
+ break;
+ case R_MICROMIPS_CALL_HI16:
+ case R_MICROMIPS_GOT_HI16:
+ case R_MICROMIPS_HI16:
+ case R_MICROMIPS_TLS_DTPREL_HI16:
+ case R_MICROMIPS_TLS_TPREL_HI16:
+ writeMicroRelocation32<E>(Loc, Val + 0x8000, 16, 16);
break;
case R_MIPS_HIGHER:
- writeMipsHigher<E>(Loc, Val);
+ writeRelocation<E>(Loc, Val + 0x80008000, 16, 32);
break;
case R_MIPS_HIGHEST:
- writeMipsHighest<E>(Loc, Val);
+ writeRelocation<E>(Loc, Val + 0x800080008000, 16, 48);
+ break;
+ case R_MICROMIPS_HIGHER:
+ writeMicroRelocation32<E>(Loc, Val + 0x80008000, 16, 32);
+ break;
+ case R_MICROMIPS_HIGHEST:
+ writeMicroRelocation32<E>(Loc, Val + 0x800080008000, 16, 48);
break;
case R_MIPS_JALR:
+ case R_MICROMIPS_JALR:
// Ignore this optimization relocation for now
break;
case R_MIPS_PC16:
- applyMipsPcReloc<E, 16, 2>(Loc, Type, Val);
+ checkAlignment<4>(Loc, Val, Type);
+ checkInt<18>(Loc, Val, Type);
+ writeRelocation<E>(Loc, Val, 16, 2);
break;
case R_MIPS_PC19_S2:
- applyMipsPcReloc<E, 19, 2>(Loc, Type, Val);
+ checkAlignment<4>(Loc, Val, Type);
+ checkInt<21>(Loc, Val, Type);
+ writeRelocation<E>(Loc, Val, 19, 2);
break;
case R_MIPS_PC21_S2:
- applyMipsPcReloc<E, 21, 2>(Loc, Type, Val);
+ checkAlignment<4>(Loc, Val, Type);
+ checkInt<23>(Loc, Val, Type);
+ writeRelocation<E>(Loc, Val, 21, 2);
break;
case R_MIPS_PC26_S2:
- applyMipsPcReloc<E, 26, 2>(Loc, Type, Val);
+ checkAlignment<4>(Loc, Val, Type);
+ checkInt<28>(Loc, Val, Type);
+ writeRelocation<E>(Loc, Val, 26, 2);
break;
case R_MIPS_PC32:
- applyMipsPcReloc<E, 32, 0>(Loc, Type, Val);
+ writeRelocation<E>(Loc, Val, 32, 0);
+ break;
+ case R_MICROMIPS_26_S1:
+ case R_MICROMIPS_PC26_S1:
+ checkInt<27>(Loc, Val, Type);
+ writeMicroRelocation32<E>(Loc, Val, 26, 1);
+ break;
+ case R_MICROMIPS_PC7_S1:
+ checkInt<8>(Loc, Val, Type);
+ writeMicroRelocation16<E>(Loc, Val, 7, 1);
+ break;
+ case R_MICROMIPS_PC10_S1:
+ checkInt<11>(Loc, Val, Type);
+ writeMicroRelocation16<E>(Loc, Val, 10, 1);
+ break;
+ case R_MICROMIPS_PC16_S1:
+ checkInt<17>(Loc, Val, Type);
+ writeMicroRelocation32<E>(Loc, Val, 16, 1);
+ break;
+ case R_MICROMIPS_PC18_S3:
+ checkInt<21>(Loc, Val, Type);
+ writeMicroRelocation32<E>(Loc, Val, 18, 3);
+ break;
+ case R_MICROMIPS_PC19_S2:
+ checkInt<21>(Loc, Val, Type);
+ writeMicroRelocation32<E>(Loc, Val, 19, 2);
+ break;
+ case R_MICROMIPS_PC21_S1:
+ checkInt<22>(Loc, Val, Type);
+ writeMicroRelocation32<E>(Loc, Val, 21, 1);
+ break;
+ case R_MICROMIPS_PC23_S2:
+ checkInt<25>(Loc, Val, Type);
+ writeMicroRelocation32<E>(Loc, Val, 23, 2);
break;
default:
error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
}
}
-template <class ELFT>
-bool MIPS<ELFT>::usesOnlyLowPageBits(uint32_t Type) const {
- return Type == R_MIPS_LO16 || Type == R_MIPS_GOT_OFST;
+template <class ELFT> bool MIPS<ELFT>::usesOnlyLowPageBits(RelType Type) const {
+ return Type == R_MIPS_LO16 || Type == R_MIPS_GOT_OFST ||
+ Type == R_MICROMIPS_LO16 || Type == R_MICROMIPS_GOT_OFST;
}
template <class ELFT> TargetInfo *elf::getMipsTargetInfo() {
diff --git a/ELF/Mips.cpp b/ELF/Arch/MipsArchTree.cpp
index af92fb9d2..017f0c795 100644
--- a/ELF/Mips.cpp
+++ b/ELF/Arch/MipsArchTree.cpp
@@ -1,4 +1,4 @@
-//===- Mips.cpp ----------------------------------------------------------===//
+//===- MipsArchTree.cpp --------------------------------------------------===//
//
// The LLVM Linker
//
@@ -37,7 +37,7 @@ struct FileFlags {
StringRef Filename;
uint32_t Flags;
};
-}
+} // namespace
static StringRef getAbiName(uint32_t Flags) {
switch (Flags) {
@@ -281,10 +281,11 @@ static uint32_t getArchFlags(ArrayRef<FileFlags> Files) {
return Ret;
}
-template <class ELFT> uint32_t elf::getMipsEFlags() {
+template <class ELFT> uint32_t elf::calcMipsEFlags() {
std::vector<FileFlags> V;
- for (elf::ObjectFile<ELFT> *F : Symtab<ELFT>::X->getObjectFiles())
- V.push_back({F->getName(), F->getObj().getHeader()->e_flags});
+ for (InputFile *F : ObjectFiles)
+ V.push_back(
+ {F->getName(), cast<ObjFile<ELFT>>(F)->getObj().getHeader()->e_flags});
if (V.empty())
return 0;
checkFlags(V);
@@ -337,8 +338,8 @@ uint8_t elf::getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
return NewFlag;
if (compareMipsFpAbi(OldFlag, NewFlag) < 0)
error("target floating point ABI '" + getMipsFpAbiName(OldFlag) +
- "' is incompatible with '" + getMipsFpAbiName(NewFlag) + "': " +
- FileName);
+ "' is incompatible with '" + getMipsFpAbiName(NewFlag) +
+ "': " + FileName);
return OldFlag;
}
@@ -363,7 +364,12 @@ bool elf::isMipsN32Abi(const InputFile *F) {
}
}
-template uint32_t elf::getMipsEFlags<ELF32LE>();
-template uint32_t elf::getMipsEFlags<ELF32BE>();
-template uint32_t elf::getMipsEFlags<ELF64LE>();
-template uint32_t elf::getMipsEFlags<ELF64BE>();
+bool elf::isMipsR6() {
+ uint32_t Arch = Config->MipsEFlags & EF_MIPS_ARCH;
+ return Arch == EF_MIPS_ARCH_32R6 || Arch == EF_MIPS_ARCH_64R6;
+}
+
+template uint32_t elf::calcMipsEFlags<ELF32LE>();
+template uint32_t elf::calcMipsEFlags<ELF32BE>();
+template uint32_t elf::calcMipsEFlags<ELF64LE>();
+template uint32_t elf::calcMipsEFlags<ELF64BE>();
diff --git a/ELF/Arch/PPC.cpp b/ELF/Arch/PPC.cpp
index 2eb89ef2b..cf170fcb8 100644
--- a/ELF/Arch/PPC.cpp
+++ b/ELF/Arch/PPC.cpp
@@ -21,14 +21,25 @@ using namespace lld::elf;
namespace {
class PPC final : public TargetInfo {
public:
- PPC() {}
- void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
- RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ PPC() { GotBaseSymOff = 0x8000; }
+ void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ RelExpr getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const override;
};
} // namespace
-void PPC::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+RelExpr PPC::getRelExpr(RelType Type, const SymbolBody &S,
+ const uint8_t *Loc) const {
+ switch (Type) {
+ case R_PPC_REL24:
+ case R_PPC_REL32:
+ return R_PC;
+ default:
+ return R_ABS;
+ }
+}
+
+void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_PPC_ADDR16_HA:
write16be(Loc, (Val + 0x8000) >> 16);
@@ -48,17 +59,6 @@ void PPC::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
}
}
-RelExpr PPC::getRelExpr(uint32_t Type, const SymbolBody &S,
- const uint8_t *Loc) const {
- switch (Type) {
- case R_PPC_REL24:
- case R_PPC_REL32:
- return R_PC;
- default:
- return R_ABS;
- }
-}
-
TargetInfo *elf::getPPCTargetInfo() {
static PPC Target;
return &Target;
diff --git a/ELF/Arch/PPC64.cpp b/ELF/Arch/PPC64.cpp
index bf414d75b..e82e08636 100644
--- a/ELF/Arch/PPC64.cpp
+++ b/ELF/Arch/PPC64.cpp
@@ -39,11 +39,11 @@ namespace {
class PPC64 final : public TargetInfo {
public:
PPC64();
- RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ RelExpr getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const override;
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
- void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
+ void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
};
} // namespace
@@ -82,11 +82,9 @@ PPC64::PPC64() {
DefaultImageBase = 0x10000000;
}
-RelExpr PPC64::getRelExpr(uint32_t Type, const SymbolBody &S,
+RelExpr PPC64::getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const {
switch (Type) {
- default:
- return R_ABS;
case R_PPC64_TOC16:
case R_PPC64_TOC16_DS:
case R_PPC64_TOC16_HA:
@@ -98,6 +96,8 @@ RelExpr PPC64::getRelExpr(uint32_t Type, const SymbolBody &S,
return R_PPC_TOC;
case R_PPC64_REL24:
return R_PPC_PLT_OPD;
+ default:
+ return R_ABS;
}
}
@@ -122,7 +122,7 @@ void PPC64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
write32be(Buf + 28, 0x4e800420); // bctr
}
-static std::pair<uint32_t, uint64_t> toAddr16Rel(uint32_t Type, uint64_t Val) {
+static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) {
uint64_t V = Val - PPC64TocOffset;
switch (Type) {
case R_PPC64_TOC16:
@@ -142,7 +142,7 @@ static std::pair<uint32_t, uint64_t> toAddr16Rel(uint32_t Type, uint64_t Val) {
}
}
-void PPC64::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
// For a TOC-relative relocation, proceed in terms of the corresponding
// ADDR16 relocation type.
std::tie(Type, Val) = toAddr16Rel(Type, Val);
diff --git a/ELF/Arch/SPARCV9.cpp b/ELF/Arch/SPARCV9.cpp
new file mode 100644
index 000000000..175579e11
--- /dev/null
+++ b/ELF/Arch/SPARCV9.cpp
@@ -0,0 +1,148 @@
+//===- SPARCV9.cpp --------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Error.h"
+#include "InputFiles.h"
+#include "Symbols.h"
+#include "SyntheticSections.h"
+#include "Target.h"
+#include "llvm/Support/Endian.h"
+
+using namespace llvm;
+using namespace llvm::support::endian;
+using namespace llvm::ELF;
+using namespace lld;
+using namespace lld::elf;
+
+namespace {
+class SPARCV9 final : public TargetInfo {
+public:
+ SPARCV9();
+ RelExpr getRelExpr(RelType Type, const SymbolBody &S,
+ const uint8_t *Loc) const override;
+ void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
+ int32_t Index, unsigned RelOff) const override;
+ void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+};
+} // namespace
+
+SPARCV9::SPARCV9() {
+ CopyRel = R_SPARC_COPY;
+ GotRel = R_SPARC_GLOB_DAT;
+ PltRel = R_SPARC_JMP_SLOT;
+ RelativeRel = R_SPARC_RELATIVE;
+ GotEntrySize = 8;
+ PltEntrySize = 32;
+ PltHeaderSize = 4 * PltEntrySize;
+
+ PageSize = 8192;
+ DefaultMaxPageSize = 0x100000;
+ DefaultImageBase = 0x100000;
+}
+
+RelExpr SPARCV9::getRelExpr(RelType Type, const SymbolBody &S,
+ const uint8_t *Loc) const {
+ switch (Type) {
+ case R_SPARC_32:
+ case R_SPARC_UA32:
+ case R_SPARC_64:
+ case R_SPARC_UA64:
+ return R_ABS;
+ case R_SPARC_PC10:
+ case R_SPARC_PC22:
+ case R_SPARC_DISP32:
+ case R_SPARC_WDISP30:
+ return R_PC;
+ case R_SPARC_GOT10:
+ return R_GOT_OFF;
+ case R_SPARC_GOT22:
+ return R_GOT_OFF;
+ case R_SPARC_WPLT30:
+ return R_PLT_PC;
+ case R_SPARC_NONE:
+ return R_NONE;
+ default:
+ return R_INVALID;
+ }
+}
+
+void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
+ switch (Type) {
+ case R_SPARC_32:
+ case R_SPARC_UA32:
+ // V-word32
+ checkUInt<32>(Loc, Val, Type);
+ write32be(Loc, Val);
+ break;
+ case R_SPARC_DISP32:
+ // V-disp32
+ checkInt<32>(Loc, Val, Type);
+ write32be(Loc, Val);
+ break;
+ case R_SPARC_WDISP30:
+ case R_SPARC_WPLT30:
+ // V-disp30
+ checkInt<32>(Loc, Val, Type);
+ write32be(Loc, (read32be(Loc) & ~0x3fffffff) | ((Val >> 2) & 0x3fffffff));
+ break;
+ case R_SPARC_22:
+ // V-imm22
+ checkUInt<22>(Loc, Val, Type);
+ write32be(Loc, (read32be(Loc) & ~0x003fffff) | (Val & 0x003fffff));
+ break;
+ case R_SPARC_GOT22:
+ case R_SPARC_PC22:
+ // T-imm22
+ write32be(Loc, (read32be(Loc) & ~0x003fffff) | ((Val >> 10) & 0x003fffff));
+ break;
+ case R_SPARC_WDISP19:
+ // V-disp19
+ checkInt<21>(Loc, Val, Type);
+ write32be(Loc, (read32be(Loc) & ~0x0007ffff) | ((Val >> 2) & 0x0007ffff));
+ break;
+ case R_SPARC_GOT10:
+ case R_SPARC_PC10:
+ // T-simm10
+ write32be(Loc, (read32be(Loc) & ~0x000003ff) | (Val & 0x000003ff));
+ break;
+ case R_SPARC_64:
+ case R_SPARC_UA64:
+ case R_SPARC_GLOB_DAT:
+ // V-xword64
+ write64be(Loc, Val);
+ break;
+ default:
+ error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
+ }
+}
+
+void SPARCV9::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
+ uint64_t PltEntryAddr, int32_t Index,
+ unsigned RelOff) const {
+ const uint8_t PltData[] = {
+ 0x03, 0x00, 0x00, 0x00, // sethi (. - .PLT0), %g1
+ 0x30, 0x68, 0x00, 0x00, // ba,a %xcc, .PLT1
+ 0x01, 0x00, 0x00, 0x00, // nop
+ 0x01, 0x00, 0x00, 0x00, // nop
+ 0x01, 0x00, 0x00, 0x00, // nop
+ 0x01, 0x00, 0x00, 0x00, // nop
+ 0x01, 0x00, 0x00, 0x00, // nop
+ 0x01, 0x00, 0x00, 0x00 // nop
+ };
+ memcpy(Buf, PltData, sizeof(PltData));
+
+ uint64_t Off = PltHeaderSize + Index * PltEntrySize;
+ relocateOne(Buf, R_SPARC_22, Off);
+ relocateOne(Buf + 4, R_SPARC_WDISP19, -(Off + 4 - PltEntrySize));
+}
+
+TargetInfo *elf::getSPARCV9TargetInfo() {
+ static SPARCV9 Target;
+ return &Target;
+}
diff --git a/ELF/Arch/X86.cpp b/ELF/Arch/X86.cpp
index 1bbc5fa00..f7a61afed 100644
--- a/ELF/Arch/X86.cpp
+++ b/ELF/Arch/X86.cpp
@@ -24,28 +24,29 @@ namespace {
class X86 final : public TargetInfo {
public:
X86();
- RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ RelExpr getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const override;
- int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
+ int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override;
void writeGotPltHeader(uint8_t *Buf) const override;
- uint32_t getDynRel(uint32_t Type) const override;
+ RelType getDynRel(RelType 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 GotPltEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
- void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
+ void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
+ RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
RelExpr Expr) const override;
- void relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
- void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
- void relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
- void relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
+ void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
};
} // namespace
X86::X86() {
+ GotBaseSymOff = -1;
CopyRel = R_386_COPY;
GotRel = R_386_GLOB_DAT;
PltRel = R_386_JUMP_SLOT;
@@ -59,12 +60,12 @@ X86::X86() {
PltEntrySize = 16;
PltHeaderSize = 16;
TlsGdRelaxSkip = 2;
-
- // 0xCC is the "int3" (call debug exception handler) instruction.
- TrapInstr = 0xcccccccc;
+ TrapInstr = 0xcccccccc; // 0xcc = INT3
}
-RelExpr X86::getRelExpr(uint32_t Type, const SymbolBody &S,
+static bool hasBaseReg(uint8_t ModRM) { return (ModRM & 0xc7) != 0x5; }
+
+RelExpr X86::getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const {
switch (Type) {
case R_386_8:
@@ -88,24 +89,42 @@ RelExpr X86::getRelExpr(uint32_t Type, const SymbolBody &S,
return R_GOT;
case R_386_GOT32:
case R_386_GOT32X:
- // These relocations can be calculated in two different ways.
- // Usual calculation is G + A - GOT what means an offset in GOT table
- // (R_GOT_FROM_END). When instruction pointed by relocation has no base
- // register, then relocations can be used when PIC code is disabled. In that
- // case calculation is G + A, it resolves to an address of entry in GOT
- // (R_GOT) and not an offset.
+ // These relocations are arguably mis-designed because their calculations
+ // depend on the instructions they are applied to. This is bad because we
+ // usually don't care about whether the target section contains valid
+ // machine instructions or not. But this is part of the documented ABI, so
+ // we had to implement as the standard requires.
//
- // To check that instruction has no base register we scan ModR/M byte.
- // See "Table 2-2. 32-Bit Addressing Forms with the ModR/M Byte"
- // (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/
- // 64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf)
- if ((Loc[-1] & 0xc7) != 0x5)
- return R_GOT_FROM_END;
- if (Config->Pic)
- error(toString(S.File) + ": relocation " + toString(Type) + " against '" +
- S.getName() +
- "' without base register can not be used when PIC enabled");
- return R_GOT;
+ // x86 does not support PC-relative data access. Therefore, in order to
+ // access GOT contents, a GOT address needs to be known at link-time
+ // (which means non-PIC) or compilers have to emit code to get a GOT
+ // address at runtime (which means code is position-independent but
+ // compilers need to emit extra code for each GOT access.) This decision
+ // is made at compile-time. In the latter case, compilers emit code to
+ // load an GOT address to a register, which is usually %ebx.
+ //
+ // So, there are two ways to refer to symbol foo's GOT entry: foo@GOT or
+ // foo@GOT(%reg).
+ //
+ // foo@GOT is not usable in PIC. If we are creating a PIC output and if we
+ // find such relocation, we should report an error. foo@GOT is resolved to
+ // an *absolute* address of foo's GOT entry, because both GOT address and
+ // foo's offset are known. In other words, it's G + A.
+ //
+ // foo@GOT(%reg) needs to be resolved to a *relative* offset from a GOT to
+ // foo's GOT entry in the table, because GOT address is not known but foo's
+ // offset in the table is known. It's G + A - GOT.
+ //
+ // It's unfortunate that compilers emit the same relocation for these
+ // different use cases. In order to distinguish them, we have to read a
+ // machine instruction.
+ //
+ // The following code implements it. We assume that Loc[0] is the first
+ // byte of a displacement or an immediate field of a valid machine
+ // instruction. That means a ModRM byte is at Loc[-1]. By taking a look at
+ // the byte, we can determine whether the instruction is register-relative
+ // (i.e. it was generated for foo@GOT(%reg)) or absolute (i.e. foo@GOT).
+ return hasBaseReg(Loc[-1]) ? R_GOT_FROM_END : R_GOT;
case R_386_TLS_GOTIE:
return R_GOT_FROM_END;
case R_386_GOTOFF:
@@ -117,12 +136,11 @@ RelExpr X86::getRelExpr(uint32_t Type, const SymbolBody &S,
case R_386_NONE:
return R_NONE;
default:
- error(toString(S.File) + ": unknown relocation type: " + toString(Type));
- return R_HINT;
+ return R_INVALID;
}
}
-RelExpr X86::adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
+RelExpr X86::adjustRelaxExpr(RelType Type, const uint8_t *Data,
RelExpr Expr) const {
switch (Expr) {
default:
@@ -149,7 +167,7 @@ void X86::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
write32le(Buf, S.getVA());
}
-uint32_t X86::getDynRel(uint32_t Type) const {
+RelType X86::getDynRel(RelType Type) const {
if (Type == R_386_TLS_LE)
return R_386_TLS_TPOFF;
if (Type == R_386_TLS_LE_32)
@@ -209,10 +227,8 @@ void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16);
}
-int64_t X86::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
+int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
switch (Type) {
- default:
- return 0;
case R_386_8:
case R_386_PC8:
return SignExtend64<8>(*Buf);
@@ -229,10 +245,12 @@ int64_t X86::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
case R_386_TLS_LDO_32:
case R_386_TLS_LE:
return SignExtend64<32>(read32le(Buf));
+ default:
+ return 0;
}
}
-void X86::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
// R_386_{PC,}{8,16} are not part of the i386 psABI, but they are
// being used for some 16-bit programs such as boot loaders, so
// we want to support them.
@@ -263,13 +281,35 @@ void X86::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
checkInt<17>(Loc, Val, Type);
write16le(Loc, Val);
break;
- default:
+ case R_386_32:
+ case R_386_GLOB_DAT:
+ case R_386_GOT32:
+ case R_386_GOT32X:
+ case R_386_GOTOFF:
+ case R_386_GOTPC:
+ case R_386_PC32:
+ case R_386_PLT32:
+ case R_386_RELATIVE:
+ case R_386_TLS_DTPMOD32:
+ case R_386_TLS_DTPOFF32:
+ case R_386_TLS_GD:
+ case R_386_TLS_GOTIE:
+ case R_386_TLS_IE:
+ case R_386_TLS_LDM:
+ case R_386_TLS_LDO_32:
+ case R_386_TLS_LE:
+ case R_386_TLS_LE_32:
+ case R_386_TLS_TPOFF:
+ case R_386_TLS_TPOFF32:
checkInt<32>(Loc, Val, Type);
write32le(Loc, Val);
+ break;
+ default:
+ error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
}
}
-void X86::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void X86::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// Convert
// leal x@tlsgd(, %ebx, 1),
// call __tls_get_addr@plt
@@ -284,7 +324,7 @@ void X86::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
write32le(Loc + 5, Val);
}
-void X86::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void X86::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// Convert
// leal x@tlsgd(, %ebx, 1),
// call __tls_get_addr@plt
@@ -301,7 +341,7 @@ void X86::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
// In some conditions, relocations can be optimized to avoid using GOT.
// This function does that for Initial Exec to Local Exec case.
-void X86::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void X86::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// Ulrich's document section 6.2 says that @gotntpoff can
// be used with MOVL or ADDL instructions.
// @indntpoff is similar to @gotntpoff, but for use in
@@ -338,7 +378,7 @@ void X86::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
write32le(Loc, Val);
}
-void X86::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void X86::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
if (Type == R_386_TLS_LDO_32) {
write32le(Loc, Val);
return;
diff --git a/ELF/Arch/X86_64.cpp b/ELF/Arch/X86_64.cpp
index 61163b8d3..c244d7545 100644
--- a/ELF/Arch/X86_64.cpp
+++ b/ELF/Arch/X86_64.cpp
@@ -26,23 +26,23 @@ namespace {
template <class ELFT> class X86_64 final : public TargetInfo {
public:
X86_64();
- RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ RelExpr getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const override;
- bool isPicRel(uint32_t Type) const override;
+ bool isPicRel(RelType Type) const override;
void writeGotPltHeader(uint8_t *Buf) const override;
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
void writePltHeader(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
- void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
+ void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
+ RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
RelExpr Expr) const override;
void relaxGot(uint8_t *Loc, uint64_t Val) const override;
- void relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
- void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
- void relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
- void relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
+ void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
private:
void relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
@@ -51,6 +51,7 @@ private:
} // namespace
template <class ELFT> X86_64<ELFT>::X86_64() {
+ GotBaseSymOff = -1;
CopyRel = R_X86_64_COPY;
GotRel = R_X86_64_GLOB_DAT;
PltRel = R_X86_64_JUMP_SLOT;
@@ -64,17 +65,15 @@ template <class ELFT> X86_64<ELFT>::X86_64() {
PltEntrySize = 16;
PltHeaderSize = 16;
TlsGdRelaxSkip = 2;
+ TrapInstr = 0xcccccccc; // 0xcc = INT3
// Align to the large page size (known as a superpage or huge page).
// FreeBSD automatically promotes large, superpage-aligned allocations.
DefaultImageBase = 0x200000;
-
- // 0xCC is the "int3" (call debug exception handler) instruction.
- TrapInstr = 0xcccccccc;
}
template <class ELFT>
-RelExpr X86_64<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S,
+RelExpr X86_64<ELFT>::getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const {
switch (Type) {
case R_X86_64_8:
@@ -110,8 +109,7 @@ RelExpr X86_64<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S,
case R_X86_64_NONE:
return R_NONE;
default:
- error(toString(S.File) + ": unknown relocation type: " + toString(Type));
- return R_HINT;
+ return R_INVALID;
}
}
@@ -125,7 +123,7 @@ template <class ELFT> void X86_64<ELFT>::writeGotPltHeader(uint8_t *Buf) const {
template <class ELFT>
void X86_64<ELFT>::writeGotPlt(uint8_t *Buf, const SymbolBody &S) const {
- // See comments in X86TargetInfo::writeGotPlt.
+ // See comments in X86::writeGotPlt.
write32le(Buf, S.getPltVA() + 6);
}
@@ -158,13 +156,13 @@ void X86_64<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16);
}
-template <class ELFT> bool X86_64<ELFT>::isPicRel(uint32_t Type) const {
+template <class ELFT> bool X86_64<ELFT>::isPicRel(RelType Type) const {
return Type != R_X86_64_PC32 && Type != R_X86_64_32 &&
Type != R_X86_64_TPOFF32;
}
template <class ELFT>
-void X86_64<ELFT>::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type,
+void X86_64<ELFT>::relaxTlsGdToLe(uint8_t *Loc, RelType Type,
uint64_t Val) const {
// Convert
// .byte 0x66
@@ -187,7 +185,7 @@ void X86_64<ELFT>::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type,
}
template <class ELFT>
-void X86_64<ELFT>::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type,
+void X86_64<ELFT>::relaxTlsGdToIe(uint8_t *Loc, RelType Type,
uint64_t Val) const {
// Convert
// .byte 0x66
@@ -212,7 +210,7 @@ void X86_64<ELFT>::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type,
// In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to
// R_X86_64_TPOFF32 so that it does not use GOT.
template <class ELFT>
-void X86_64<ELFT>::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type,
+void X86_64<ELFT>::relaxTlsIeToLe(uint8_t *Loc, RelType Type,
uint64_t Val) const {
uint8_t *Inst = Loc - 3;
uint8_t Reg = Loc[-1] >> 3;
@@ -255,7 +253,7 @@ void X86_64<ELFT>::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type,
}
template <class ELFT>
-void X86_64<ELFT>::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type,
+void X86_64<ELFT>::relaxTlsLdToLe(uint8_t *Loc, RelType Type,
uint64_t Val) const {
// Convert
// leaq bar@tlsld(%rip), %rdi
@@ -284,8 +282,7 @@ void X86_64<ELFT>::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type,
}
template <class ELFT>
-void X86_64<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
- uint64_t Val) const {
+void X86_64<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_X86_64_8:
checkUInt<8>(Loc, Val, Type);
@@ -324,12 +321,12 @@ void X86_64<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
write64le(Loc, Val);
break;
default:
- llvm_unreachable("unexpected relocation");
+ error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
}
}
template <class ELFT>
-RelExpr X86_64<ELFT>::adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
+RelExpr X86_64<ELFT>::adjustRelaxExpr(RelType Type, const uint8_t *Data,
RelExpr RelExpr) const {
if (Type != R_X86_64_GOTPCRELX && Type != R_X86_64_REX_GOTPCRELX)
return RelExpr;
diff --git a/ELF/CMakeLists.txt b/ELF/CMakeLists.txt
index 09a19fee1..205702975 100644
--- a/ELF/CMakeLists.txt
+++ b/ELF/CMakeLists.txt
@@ -12,8 +12,10 @@ add_lld_library(lldELF
Arch/ARM.cpp
Arch/AVR.cpp
Arch/Mips.cpp
+ Arch/MipsArchTree.cpp
Arch/PPC.cpp
Arch/PPC64.cpp
+ Arch/SPARCV9.cpp
Arch/X86.cpp
Arch/X86_64.cpp
Driver.cpp
@@ -29,7 +31,6 @@ add_lld_library(lldELF
LinkerScript.cpp
MapFile.cpp
MarkLive.cpp
- Mips.cpp
OutputSections.cpp
Relocations.cpp
ScriptLexer.cpp
@@ -44,28 +45,18 @@ add_lld_library(lldELF
LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
- Analysis
BinaryFormat
- BitReader
- BitWriter
- Codegen
Core
DebugInfoDWARF
Demangle
- IPO
- Linker
LTO
+ MC
Object
Option
- Passes
- MC
Support
- Target
- TransformUtils
LINK_LIBS
- lldConfig
- lldCore
+ lldCommon
${LLVM_PTHREAD_LIB}
DEPENDS
diff --git a/ELF/Config.h b/ELF/Config.h
index 9c73b4c9c..d5d830921 100644
--- a/ELF/Config.h
+++ b/ELF/Config.h
@@ -44,7 +44,7 @@ enum class DiscardPolicy { Default, All, Locals, None };
enum class StripPolicy { None, All, Debug };
// For --unresolved-symbols.
-enum class UnresolvedPolicy { ReportError, Warn, WarnAll, Ignore, IgnoreAll };
+enum class UnresolvedPolicy { ReportError, Warn, Ignore, IgnoreAll };
// For --sort-section and linkerscript sorting rules.
enum class SortSectionPolicy { Default, None, Alignment, Name, Priority };
@@ -67,21 +67,15 @@ struct VersionDefinition {
size_t NameOff = 0; // Offset in the string table
};
-// Structure for mapping renamed symbols
-struct RenamedSymbol {
- Symbol *Target;
- uint8_t OrigBinding;
-};
-
// This struct contains the global configuration for the linker.
// Most fields are direct mapping from the command line options
// and such fields have the same name as the corresponding options.
// Most fields are initialized by the driver.
struct Configuration {
- InputFile *FirstElf = nullptr;
uint8_t OSABI = 0;
llvm::CachePruningPolicy ThinLTOCachePolicy;
llvm::StringMap<uint64_t> SectionStartMap;
+ llvm::StringRef Chroot;
llvm::StringRef DynamicLinker;
llvm::StringRef Entry;
llvm::StringRef Emulation;
@@ -97,14 +91,16 @@ struct Configuration {
llvm::StringRef ThinLTOCacheDir;
std::string Rpath;
std::vector<VersionDefinition> VersionDefinitions;
+ std::vector<llvm::StringRef> Argv;
std::vector<llvm::StringRef> AuxiliaryList;
+ std::vector<llvm::StringRef> FilterList;
std::vector<llvm::StringRef> SearchPaths;
std::vector<llvm::StringRef> SymbolOrderingFile;
std::vector<llvm::StringRef> Undefined;
+ std::vector<SymbolVersion> DynamicList;
std::vector<SymbolVersion> VersionScriptGlobals;
std::vector<SymbolVersion> VersionScriptLocals;
std::vector<uint8_t> BuildIdVector;
- llvm::MapVector<Symbol *, RenamedSymbol> RenamedSymbols;
bool AllowMultipleDefinition;
bool AsNeeded = false;
bool Bsymbolic;
@@ -121,11 +117,14 @@ struct Configuration {
bool FatalWarnings;
bool GcSections;
bool GdbIndex;
- bool GnuHash;
+ bool GnuHash = false;
+ bool HasDynamicList = false;
+ bool HasDynSymTab;
bool ICF;
bool MipsN32Abi = false;
bool NoGnuUnique;
bool NoUndefinedVersion;
+ bool NoinhibitExec;
bool Nostdlib;
bool OFormatBinary;
bool Omagic;
@@ -137,9 +136,8 @@ struct Configuration {
bool SingleRoRx;
bool Shared;
bool Static = false;
- bool SysvHash;
+ bool SysvHash = false;
bool Target1Rel;
- bool Threads;
bool Trace;
bool Verbose;
bool WarnCommon;
@@ -166,7 +164,7 @@ struct Configuration {
uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL;
uint16_t EMachine = llvm::ELF::EM_NONE;
uint64_t ErrorLimit = 20;
- uint64_t ImageBase;
+ llvm::Optional<uint64_t> ImageBase;
uint64_t MaxPageSize;
uint64_t ZStackSize;
unsigned LTOPartitions;
@@ -204,6 +202,12 @@ struct Configuration {
// if that's true.)
bool IsMips64EL;
+ // Holds set of ELF header flags for MIPS targets. The set calculated
+ // by the `elf::calcMipsEFlags` function and cached in this field. For
+ // the calculation we iterate over all input object files and combine
+ // their ELF flags.
+ uint32_t MipsEFlags = 0;
+
// The ELF spec defines two types of relocation table entries, RELA and
// REL. RELA is a triplet of (offset, info, addend) while REL is a
// tuple of (offset, info). Addends for REL are implicit and read from
diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp
index c87ffa26a..af56b2ba3 100644
--- a/ELF/Driver.cpp
+++ b/ELF/Driver.cpp
@@ -38,10 +38,10 @@
#include "SymbolTable.h"
#include "SyntheticSections.h"
#include "Target.h"
-#include "Threads.h"
#include "Writer.h"
-#include "lld/Config/Version.h"
-#include "lld/Driver/Driver.h"
+#include "lld/Common/Driver.h"
+#include "lld/Common/Threads.h"
+#include "lld/Common/Version.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/CommandLine.h"
@@ -74,15 +74,29 @@ bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
raw_ostream &Error) {
ErrorCount = 0;
ErrorOS = &Error;
- Argv0 = Args[0];
InputSections.clear();
+ OutputSections.clear();
Tar = nullptr;
+ BinaryFiles.clear();
+ BitcodeFiles.clear();
+ ObjectFiles.clear();
+ SharedFiles.clear();
Config = make<Configuration>();
Driver = make<LinkerDriver>();
Script = make<LinkerScript>();
+ Symtab = make<SymbolTable>();
+ Config->Argv = {Args.begin(), Args.end()};
Driver->main(Args, CanExitEarly);
+ waitForBackgroundThreads();
+
+ // Exit immediately if we don't need to return to the caller.
+ // This saves time because the overhead of calling destructors
+ // for all globally-allocated objects is not negligible.
+ if (Config->ExitEarly)
+ exitLld(ErrorCount ? 1 : 0);
+
freeArena();
return !ErrorCount;
}
@@ -112,12 +126,8 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
.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: " + Emul);
- else
- error("unknown emulation: " + Emul);
- }
+ if (Ret.first == ELFNoneKind)
+ error("unknown emulation: " + Emul);
return std::make_tuple(Ret.first, Ret.second, OSABI);
}
@@ -131,6 +141,7 @@ std::vector<std::pair<MemoryBufferRef, uint64_t>> static getArchiveMembers(
std::vector<std::pair<MemoryBufferRef, uint64_t>> V;
Error Err = Error::success();
+ bool AddToTar = File->isThin() && Tar;
for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
Archive::Child C =
check(COrErr, MB.getBufferIdentifier() +
@@ -139,6 +150,8 @@ std::vector<std::pair<MemoryBufferRef, uint64_t>> static getArchiveMembers(
check(C.getMemoryBufferRef(),
MB.getBufferIdentifier() +
": could not get the buffer for a child of the archive");
+ if (AddToTar)
+ Tar->append(relativeToRoot(check(C.getFullName())), MBRef.getBuffer());
V.push_back(std::make_pair(MBRef, C.getChildOffset()));
}
if (Err)
@@ -187,7 +200,7 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
// we'll handle it as if it had a symbol table.
if (!File->isEmpty() && !File->hasSymbolTable()) {
for (const auto &P : getArchiveMembers(MBRef))
- Files.push_back(make<LazyObjectFile>(P.first, Path, P.second));
+ Files.push_back(make<LazyObjFile>(P.first, Path, P.second));
return;
}
@@ -200,6 +213,7 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
error("attempted static link of dynamic object " + Path);
return;
}
+
// DSOs usually have DT_SONAME tags in their ELF headers, and the
// sonames are used to identify DSOs. But if they are missing,
// they are identified by filenames. We don't know whether the new
@@ -210,12 +224,12 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
// If a file was specified by -lfoo, the directory part is not
// significant, as a user did not specify it. This behavior is
// compatible with GNU.
- Files.push_back(createSharedFile(
- MBRef, WithLOption ? sys::path::filename(Path) : Path));
+ Files.push_back(
+ createSharedFile(MBRef, WithLOption ? path::filename(Path) : Path));
return;
default:
if (InLib)
- Files.push_back(make<LazyObjectFile>(MBRef, "", 0));
+ Files.push_back(make<LazyObjFile>(MBRef, "", 0));
else
Files.push_back(createObjectFile(MBRef));
}
@@ -258,6 +272,9 @@ static void checkOptions(opt::InputArgList &Args) {
if (Config->Pie && Config->Shared)
error("-shared and -pie may not be used together");
+ if (!Config->Shared && !Config->FilterList.empty())
+ error("-F may not be used without -shared");
+
if (!Config->Shared && !Config->AuxiliaryList.empty())
error("-f may not be used without -shared");
@@ -278,7 +295,7 @@ static int getInteger(opt::InputArgList &Args, unsigned Key, int Default) {
if (auto *Arg = Args.getLastArg(Key)) {
StringRef S = Arg->getValue();
if (!to_integer(S, V, 10))
- error(Arg->getSpelling() + ": number expected, but got " + S);
+ error(Arg->getSpelling() + ": number expected, but got '" + S + "'");
}
return V;
}
@@ -301,7 +318,7 @@ static uint64_t getZOptionValue(opt::InputArgList &Args, StringRef Key,
for (auto *Arg : Args.filtered(OPT_z)) {
std::pair<StringRef, StringRef> KV = StringRef(Arg->getValue()).split('=');
if (KV.first == Key) {
- uint64_t Result;
+ uint64_t Result = Default;
if (!to_integer(KV.second, Result))
error("invalid " + Key + ": " + KV.second);
return Result;
@@ -414,9 +431,6 @@ static std::string getRpath(opt::InputArgList &Args) {
// Determines what we should do if there are remaining unresolved
// symbols after the name resolution.
static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &Args) {
- // -noinhibit-exec or -r imply some default values.
- if (Args.hasArg(OPT_noinhibit_exec))
- return UnresolvedPolicy::WarnAll;
if (Args.hasArg(OPT_relocatable))
return UnresolvedPolicy::IgnoreAll;
@@ -547,17 +561,6 @@ static SortSectionPolicy getSortSection(opt::InputArgList &Args) {
return SortSectionPolicy::Default;
}
-static std::pair<bool, bool> getHashStyle(opt::InputArgList &Args) {
- StringRef S = Args.getLastArgValue(OPT_hash_style, "sysv");
- if (S == "sysv")
- return {true, false};
- if (S == "gnu")
- return {false, true};
- if (S != "both")
- error("unknown -hash-style: " + S);
- return {true, true};
-}
-
// Parse --build-id or --build-id=<style>. We handle "tree" as a
// synonym for "sha1" because all our hash functions including
// -build-id=sha1 are actually tree hashes for performance reasons.
@@ -609,12 +612,21 @@ static bool getCompressDebugSections(opt::InputArgList &Args) {
return true;
}
+static int parseInt(StringRef S, opt::Arg *Arg) {
+ int V = 0;
+ if (!to_integer(S, V, 10))
+ error(Arg->getSpelling() + ": number expected, but got '" + S + "'");
+ return V;
+}
+
// Initializes Config members by the command line options.
void LinkerDriver::readConfigs(opt::InputArgList &Args) {
- Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition);
+ Config->AllowMultipleDefinition =
+ Args.hasArg(OPT_allow_multiple_definition) || hasZOption(Args, "muldefs");
Config->AuxiliaryList = getArgs(Args, OPT_auxiliary);
Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic);
Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions);
+ Config->Chroot = Args.getLastArgValue(OPT_chroot);
Config->CompressDebugSections = getCompressDebugSections(Args);
Config->DefineCommon = getArg(Args, OPT_define_common, OPT_no_define_common,
!Args.hasArg(OPT_relocatable));
@@ -622,7 +634,8 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
Config->DisableVerify = Args.hasArg(OPT_disable_verify);
Config->Discard = getDiscard(Args);
Config->DynamicLinker = getDynamicLinker(Args);
- Config->EhFrameHdr = Args.hasArg(OPT_eh_frame_hdr);
+ Config->EhFrameHdr =
+ getArg(Args, OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false);
Config->EmitRelocs = Args.hasArg(OPT_emit_relocs);
Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags);
Config->Entry = Args.getLastArgValue(OPT_entry);
@@ -630,10 +643,11 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
getArg(Args, OPT_export_dynamic, OPT_no_export_dynamic, false);
Config->FatalWarnings =
getArg(Args, OPT_fatal_warnings, OPT_no_fatal_warnings, false);
+ Config->FilterList = getArgs(Args, OPT_filter);
Config->Fini = Args.getLastArgValue(OPT_fini, "_fini");
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->GdbIndex = getArg(Args, OPT_gdb_index, OPT_no_gdb_index, false);
+ Config->ICF = getArg(Args, OPT_icf_all, OPT_icf_none, false);
Config->Init = Args.getLastArgValue(OPT_init, "_init");
Config->LTOAAPipeline = Args.getLastArgValue(OPT_lto_aa_pipeline);
Config->LTONewPmPasses = Args.getLastArgValue(OPT_lto_newpm_passes);
@@ -642,6 +656,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
Config->MapFile = Args.getLastArgValue(OPT_Map);
Config->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique);
Config->NoUndefinedVersion = Args.hasArg(OPT_no_undefined_version);
+ Config->NoinhibitExec = Args.hasArg(OPT_noinhibit_exec);
Config->Nostdlib = Args.hasArg(OPT_nostdlib);
Config->OFormatBinary = isOutputFormatBinary(Args);
Config->Omagic = Args.hasArg(OPT_omagic);
@@ -654,7 +669,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
Config->Rpath = getRpath(Args);
Config->Relocatable = Args.hasArg(OPT_relocatable);
Config->SaveTemps = Args.hasArg(OPT_save_temps);
- Config->SearchPaths = getArgs(Args, OPT_L);
+ Config->SearchPaths = getArgs(Args, OPT_library_path);
Config->SectionStartMap = getSectionStartMap(Args);
Config->Shared = Args.hasArg(OPT_shared);
Config->SingleRoRx = Args.hasArg(OPT_no_rosegment);
@@ -669,7 +684,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
parseCachePruningPolicy(Args.getLastArgValue(OPT_thinlto_cache_policy)),
"--thinlto-cache-policy: invalid cache policy");
Config->ThinLTOJobs = getInteger(Args, OPT_thinlto_jobs, -1u);
- Config->Threads = getArg(Args, OPT_threads, OPT_no_threads, true);
+ ThreadsEnabled = getArg(Args, OPT_threads, OPT_no_threads, true);
Config->Trace = Args.hasArg(OPT_trace);
Config->Undefined = getArgs(Args, OPT_undefined);
Config->UnresolvedSymbols = getUnresolvedSymbolPolicy(Args);
@@ -688,16 +703,35 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
Config->ZText = !hasZOption(Args, "notext");
Config->ZWxneeded = hasZOption(Args, "wxneeded");
+ // Parse LTO plugin-related options for compatibility with gold.
+ for (auto *Arg : Args.filtered(OPT_plugin_opt, OPT_plugin_opt_eq)) {
+ StringRef S = Arg->getValue();
+ if (S == "disable-verify")
+ Config->DisableVerify = true;
+ else if (S == "save-temps")
+ Config->SaveTemps = true;
+ else if (S.startswith("O"))
+ Config->LTOO = parseInt(S.substr(1), Arg);
+ else if (S.startswith("lto-partitions="))
+ Config->LTOPartitions = parseInt(S.substr(15), Arg);
+ else if (S.startswith("jobs="))
+ Config->ThinLTOJobs = parseInt(S.substr(5), Arg);
+ else if (!S.startswith("/") && !S.startswith("-fresolution=") &&
+ !S.startswith("-pass-through=") && !S.startswith("mcpu=") &&
+ !S.startswith("thinlto") && S != "-function-sections" &&
+ S != "-data-sections")
+ error(Arg->getSpelling() + ": unknown option: " + S);
+ }
+
if (Config->LTOO > 3)
- error("invalid optimization level for LTO: " +
- Args.getLastArgValue(OPT_lto_O));
+ error("invalid optimization level for LTO: " + Twine(Config->LTOO));
if (Config->LTOPartitions == 0)
error("--lto-partitions: number of threads must be > 0");
if (Config->ThinLTOJobs == 0)
error("--thinlto-jobs: number of threads must be > 0");
+ // Parse ELF{32,64}{LE,BE} and CPU type.
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, Config->OSABI) =
parseEmulation(S);
@@ -705,6 +739,19 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
Config->Emulation = S;
}
+ // Parse -hash-style={sysv,gnu,both}.
+ if (auto *Arg = Args.getLastArg(OPT_hash_style)) {
+ StringRef S = Arg->getValue();
+ if (S == "sysv")
+ Config->SysvHash = true;
+ else if (S == "gnu")
+ Config->GnuHash = true;
+ else if (S == "both")
+ Config->SysvHash = Config->GnuHash = true;
+ else
+ error("unknown -hash-style: " + S);
+ }
+
if (Args.hasArg(OPT_print_map))
Config->MapFile = "-";
@@ -715,7 +762,6 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
if (Config->Omagic)
Config->ZRelro = false;
- std::tie(Config->SysvHash, Config->GnuHash) = getHashStyle(Args);
std::tie(Config->BuildId, Config->BuildIdVector) = getBuildId(Args);
if (auto *Arg = Args.getLastArg(OPT_symbol_ordering_file))
@@ -744,18 +790,8 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
readDynamicList(*Buffer);
for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol))
- Config->VersionScriptGlobals.push_back(
+ Config->DynamicList.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))
@@ -799,14 +835,13 @@ static bool getBinaryOption(StringRef S) {
void LinkerDriver::createFiles(opt::InputArgList &Args) {
for (auto *Arg : Args) {
- switch (Arg->getOption().getID()) {
- case OPT_l:
+ switch (Arg->getOption().getUnaliasedOption().getID()) {
+ case OPT_library:
addLibrary(Arg->getValue());
break;
case OPT_INPUT:
addFile(Arg->getValue(), /*WithLOption=*/false);
break;
- case OPT_alias_script_T:
case OPT_script:
if (Optional<MemoryBufferRef> MB = readFile(Arg->getValue()))
readLinkerScript(*MB);
@@ -873,13 +908,12 @@ static uint64_t getMaxPageSize(opt::InputArgList &Args) {
}
// 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.
+static Optional<uint64_t> getImageBase(opt::InputArgList &Args) {
+ // Because we are using "Config->MaxPageSize" 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;
+ return None;
StringRef S = Arg->getValue();
uint64_t V;
@@ -907,16 +941,58 @@ getDefsym(opt::InputArgList &Args) {
return Ret;
}
+// Parses `--exclude-libs=lib,lib,...`.
+// The library names may be delimited by commas or colons.
+static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &Args) {
+ DenseSet<StringRef> Ret;
+ for (auto *Arg : Args.filtered(OPT_exclude_libs)) {
+ StringRef S = Arg->getValue();
+ for (;;) {
+ size_t Pos = S.find_first_of(",:");
+ if (Pos == StringRef::npos)
+ break;
+ Ret.insert(S.substr(0, Pos));
+ S = S.substr(Pos + 1);
+ }
+ Ret.insert(S);
+ }
+ return Ret;
+}
+
+// Handles the -exclude-libs option. If a static library file is specified
+// by the -exclude-libs option, all public symbols from the archive become
+// private unless otherwise specified by version scripts or something.
+// A special library name "ALL" means all archive files.
+//
+// This is not a popular option, but some programs such as bionic libc use it.
+static void excludeLibs(opt::InputArgList &Args, ArrayRef<InputFile *> Files) {
+ DenseSet<StringRef> Libs = getExcludeLibs(Args);
+ bool All = Libs.count("ALL");
+
+ for (InputFile *File : Files)
+ if (auto *F = dyn_cast<ArchiveFile>(File))
+ if (All || Libs.count(path::filename(F->getName())))
+ for (SymbolBody *Sym : F->getSymbols())
+ Sym->symbol()->VersionId = VER_NDX_LOCAL;
+}
+
// Do actual linking. Note that when this function is called,
// all linker scripts have already been parsed.
template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
- SymbolTable<ELFT> Symtab;
- elf::Symtab<ELFT>::X = &Symtab;
Target = getTarget();
Config->MaxPageSize = getMaxPageSize(Args);
Config->ImageBase = getImageBase(Args);
+ // If a -hash-style option was not given, set to a default value,
+ // which varies depending on the target.
+ if (!Args.hasArg(OPT_hash_style)) {
+ if (Config->EMachine == EM_MIPS)
+ Config->SysvHash = true;
+ else
+ Config->SysvHash = Config->GnuHash = true;
+ }
+
// Default output filename is "a.out" by the Unix tradition.
if (Config->OutputFile.empty())
Config->OutputFile = "a.out";
@@ -941,69 +1017,98 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// Handle --trace-symbol.
for (auto *Arg : Args.filtered(OPT_trace_symbol))
- Symtab.trace(Arg->getValue());
+ Symtab->trace(Arg->getValue());
// 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);
+ Symtab->addFile<ELFT>(F);
+
+ // Now that we have every file, we can decide if we will need a
+ // dynamic symbol table.
+ // We need one if we were asked to export dynamic symbols or if we are
+ // producing a shared library.
+ // We also need one if any shared libraries are used and for pie executables
+ // (probably because the dynamic linker needs it).
+ Config->HasDynSymTab =
+ !SharedFiles.empty() || Config->Pic || Config->ExportDynamic;
+
+ // Some symbols (such as __ehdr_start) are defined lazily only when there
+ // are undefined symbols for them, so we add these to trigger that logic.
+ for (StringRef Sym : Script->ReferencedSymbols)
+ Symtab->addUndefined<ELFT>(Sym);
+
+ // Handle the `--undefined <sym>` options.
+ for (StringRef S : Config->Undefined)
+ Symtab->fetchIfLazy<ELFT>(S);
// 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);
+ Symtab->fetchIfLazy<ELFT>(Config->Entry);
// Return if there were name resolution errors.
if (ErrorCount)
return;
- Symtab.scanUndefinedFlags();
- Symtab.scanShlibUndefined();
- Symtab.scanVersionScript();
+ // Handle undefined symbols in DSOs.
+ Symtab->scanShlibUndefined<ELFT>();
+
+ // Handle the -exclude-libs option.
+ if (Args.hasArg(OPT_exclude_libs))
+ excludeLibs(Args, Files);
+
+ // Apply version scripts.
+ Symtab->scanVersionScript();
// Create wrapped symbols for -wrap option.
for (auto *Arg : Args.filtered(OPT_wrap))
- Symtab.addSymbolWrap(Arg->getValue());
+ Symtab->addSymbolWrap<ELFT>(Arg->getValue());
// Create alias symbols for -defsym option.
for (std::pair<StringRef, StringRef> &Def : getDefsym(Args))
- Symtab.addSymbolAlias(Def.first, Def.second);
+ Symtab->addSymbolAlias<ELFT>(Def.first, Def.second);
- Symtab.addCombinedLTOObject();
+ Symtab->addCombinedLTOObject<ELFT>();
if (ErrorCount)
return;
- // Some symbols (such as __ehdr_start) are defined lazily only when there
- // are undefined symbols for them, so we add these to trigger that logic.
- for (StringRef Sym : Script->Opt.ReferencedSymbols)
- Symtab.addUndefined(Sym);
-
// Apply symbol renames for -wrap and -defsym
- Symtab.applySymbolRenames();
+ Symtab->applySymbolRenames();
// 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 (InputFile *F : ObjectFiles)
for (InputSectionBase *S : F->getSections())
if (S && S != &InputSection::Discarded)
InputSections.push_back(S);
- for (BinaryFile *F : Symtab.getBinaryFiles())
+ for (BinaryFile *F : BinaryFiles)
for (InputSectionBase *S : F->getSections())
InputSections.push_back(cast<InputSection>(S));
+ if (Config->EMachine == EM_MIPS)
+ Config->MipsEFlags = calcMipsEFlags<ELFT>();
+
// This adds a .comment section containing a version string. We have to add it
// before decompressAndMergeSections because the .comment section is a
// mergeable section.
if (!Config->Relocatable)
InputSections.push_back(createCommentSection<ELFT>());
+ // Create a .bss section for each common symbol and then replace the common
+ // symbol with a DefinedRegular symbol. As a result, all common symbols are
+ // "instantiated" as regular defined symbols, so that we don't need to care
+ // about common symbols beyond this point. Note that if -r is given, we just
+ // need to pass through common symbols as-is.
+ if (Config->DefineCommon)
+ createCommonSections<ELFT>();
+
// Do size optimizations: garbage collection, merging of SHF_MERGE sections
// and identical code folding.
- if (Config->GcSections)
- markLive<ELFT>();
- decompressAndMergeSections();
+ markLive<ELFT>();
+ decompressSections();
+ mergeSections();
if (Config->ICF)
doIcf<ELFT>();
diff --git a/ELF/Driver.h b/ELF/Driver.h
index af8834163..617dcb200 100644
--- a/ELF/Driver.h
+++ b/ELF/Driver.h
@@ -11,8 +11,8 @@
#define LLD_ELF_DRIVER_H
#include "SymbolTable.h"
-#include "lld/Core/LLVM.h"
-#include "lld/Core/Reproduce.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Common/Reproduce.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
@@ -58,7 +58,7 @@ public:
// Create enum with OPT_xxx values for each option in Options.td
enum {
OPT_INVALID = 0,
-#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11) OPT_##ID,
+#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID,
#include "Options.inc"
#undef OPTION
};
diff --git a/ELF/DriverUtils.cpp b/ELF/DriverUtils.cpp
index f4eadeee9..0af56c301 100644
--- a/ELF/DriverUtils.cpp
+++ b/ELF/DriverUtils.cpp
@@ -16,8 +16,8 @@
#include "Driver.h"
#include "Error.h"
#include "Memory.h"
-#include "lld/Config/Version.h"
-#include "lld/Core/Reproduce.h"
+#include "lld/Common/Reproduce.h"
+#include "lld/Common/Version.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Triple.h"
@@ -42,9 +42,9 @@ 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},
+#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
+ {X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \
+ X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12},
#include "Options.inc"
#undef OPTION
};
@@ -116,7 +116,8 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
void elf::printHelp(const char *Argv0) {
ELFOptTable Table;
- Table.PrintHelp(outs(), Argv0, "lld", false);
+ Table.PrintHelp(outs(), Argv0, "lld", false /*ShowHidden*/,
+ true /*ShowAllAliases*/);
outs() << "\n";
// Scripts generated by Libtool versions up to at least 2.4.6 (the most
@@ -138,10 +139,11 @@ void elf::printHelp(const char *Argv0) {
std::string elf::createResponseFile(const opt::InputArgList &Args) {
SmallString<0> Data;
raw_svector_ostream OS(Data);
+ OS << "--chroot .\n";
// Copy the command line to the output while rewriting paths.
for (auto *Arg : Args) {
- switch (Arg->getOption().getID()) {
+ switch (Arg->getOption().getUnaliasedOption().getID()) {
case OPT_reproduce:
break;
case OPT_INPUT:
@@ -154,11 +156,11 @@ std::string elf::createResponseFile(const opt::InputArgList &Args) {
// Strip directories to prevent the issue.
OS << "-o " << quote(sys::path::filename(Arg->getValue())) << "\n";
break;
- case OPT_L:
case OPT_dynamic_list:
+ case OPT_library_path:
case OPT_rpath:
- case OPT_alias_script_T:
case OPT_script:
+ case OPT_symbol_ordering_file:
case OPT_version_script:
OS << Arg->getSpelling() << " " << quote(rewritePath(Arg->getValue()))
<< "\n";
diff --git a/ELF/EhFrame.cpp b/ELF/EhFrame.cpp
index c4e3f65c7..e29110cea 100644
--- a/ELF/EhFrame.cpp
+++ b/ELF/EhFrame.cpp
@@ -154,8 +154,7 @@ template <class ELFT> void EhReader<ELFT>::skipAugP() {
}
template <class ELFT> uint8_t elf::getFdeEncoding(EhSectionPiece *P) {
- auto *IS = static_cast<InputSectionBase *>(P->ID);
- return EhReader<ELFT>(IS, P->data()).getFdeEncoding();
+ return EhReader<ELFT>(P->Sec, P->data()).getFdeEncoding();
}
template <class ELFT> uint8_t EhReader<ELFT>::getFdeEncoding() {
diff --git a/ELF/EhFrame.h b/ELF/EhFrame.h
index 4e2b6f83a..25ed2b919 100644
--- a/ELF/EhFrame.h
+++ b/ELF/EhFrame.h
@@ -10,7 +10,7 @@
#ifndef LLD_ELF_EHFRAME_H
#define LLD_ELF_EHFRAME_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
namespace lld {
namespace elf {
@@ -19,7 +19,7 @@ struct EhSectionPiece;
template <class ELFT> size_t readEhRecordSize(InputSectionBase *S, size_t Off);
template <class ELFT> uint8_t getFdeEncoding(EhSectionPiece *P);
-}
-}
+} // namespace elf
+} // namespace lld
#endif
diff --git a/ELF/Error.cpp b/ELF/Error.cpp
index 7a58668bd..8173d6ff2 100644
--- a/ELF/Error.cpp
+++ b/ELF/Error.cpp
@@ -10,6 +10,8 @@
#include "Error.h"
#include "Config.h"
+#include "lld/Common/Threads.h"
+
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ManagedStatic.h"
@@ -27,7 +29,6 @@ using namespace lld::elf;
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.
@@ -42,11 +43,11 @@ static void newline(const Twine &Msg) {
if (Flag)
*ErrorOS << "\n";
- Flag = (StringRef(Msg.str()).find('\n') != StringRef::npos);
+ Flag = StringRef(Msg.str()).contains('\n');
}
static void print(StringRef S, raw_ostream::Colors C) {
- *ErrorOS << Argv0 + ": ";
+ *ErrorOS << Config->Argv[0] << ": ";
if (Config->ColorDiagnostics) {
ErrorOS->changeColor(C, true);
*ErrorOS << S;
@@ -59,7 +60,7 @@ static void print(StringRef S, raw_ostream::Colors C) {
void elf::log(const Twine &Msg) {
if (Config->Verbose) {
std::lock_guard<std::mutex> Lock(Mu);
- outs() << Argv0 << ": " << Msg << "\n";
+ outs() << Config->Argv[0] << ": " << Msg << "\n";
outs().flush();
}
}
@@ -101,6 +102,8 @@ void elf::error(const Twine &Msg) {
}
void elf::exitLld(int Val) {
+ waitForBackgroundThreads();
+
// 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.
diff --git a/ELF/Error.h b/ELF/Error.h
index dd6e37c99..c6e4f0e62 100644
--- a/ELF/Error.h
+++ b/ELF/Error.h
@@ -28,7 +28,7 @@
#ifndef LLD_ELF_ERROR_H
#define LLD_ELF_ERROR_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/Support/Error.h"
@@ -37,7 +37,6 @@ namespace elf {
extern uint64_t ErrorCount;
extern llvm::raw_ostream *ErrorOS;
-extern llvm::StringRef Argv0;
void log(const Twine &Msg);
void message(const Twine &Msg);
diff --git a/ELF/Filesystem.cpp b/ELF/Filesystem.cpp
index b63d521a8..35310b052 100644
--- a/ELF/Filesystem.cpp
+++ b/ELF/Filesystem.cpp
@@ -13,9 +13,9 @@
#include "Filesystem.h"
#include "Config.h"
-#include "llvm/Support/FileSystem.h"
+#include "lld/Common/Threads.h"
#include "llvm/Support/FileOutputBuffer.h"
-#include <thread>
+#include "llvm/Support/FileSystem.h"
using namespace llvm;
@@ -38,7 +38,8 @@ using namespace lld::elf;
// This function spawns a background thread to call unlink.
// The calling thread returns almost immediately.
void elf::unlinkAsync(StringRef Path) {
- if (!Config->Threads || !sys::fs::exists(Config->OutputFile))
+ if (!ThreadsEnabled || !sys::fs::exists(Config->OutputFile) ||
+ !sys::fs::is_regular_file(Config->OutputFile))
return;
// First, rename Path to avoid race condition. We cannot remove
@@ -54,7 +55,7 @@ void elf::unlinkAsync(StringRef Path) {
}
// Remove TempPath in background.
- std::thread([=] { ::remove(TempPath.str().str().c_str()); }).detach();
+ runBackground([=] { ::remove(TempPath.str().str().c_str()); });
}
// Simulate file creation to see if Path is writable.
diff --git a/ELF/Filesystem.h b/ELF/Filesystem.h
index d56d067f7..cf170b716 100644
--- a/ELF/Filesystem.h
+++ b/ELF/Filesystem.h
@@ -10,13 +10,13 @@
#ifndef LLD_ELF_FILESYSTEM_H
#define LLD_ELF_FILESYSTEM_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
namespace lld {
namespace elf {
void unlinkAsync(StringRef Path);
std::error_code tryCreateFile(StringRef Path);
-}
-}
+} // namespace elf
+} // namespace lld
#endif
diff --git a/ELF/GdbIndex.cpp b/ELF/GdbIndex.cpp
index 99e02d002..38df67b67 100644
--- a/ELF/GdbIndex.cpp
+++ b/ELF/GdbIndex.cpp
@@ -24,26 +24,70 @@ using namespace llvm::object;
using namespace lld;
using namespace lld::elf;
-std::pair<bool, GdbSymbol *> GdbHashTab::add(uint32_t Hash, size_t Offset) {
- GdbSymbol *&Sym = Map[Offset];
- if (Sym)
- return {false, Sym};
- Sym = make<GdbSymbol>(Hash, Offset);
- return {true, Sym};
+template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *Obj) {
+ for (InputSectionBase *Sec : Obj->getSections()) {
+ if (!Sec)
+ continue;
+ if (LLDDWARFSection *M = StringSwitch<LLDDWARFSection *>(Sec->Name)
+ .Case(".debug_info", &InfoSection)
+ .Case(".debug_ranges", &RangeSection)
+ .Case(".debug_line", &LineSection)
+ .Default(nullptr)) {
+ Sec->maybeUncompress();
+ M->Data = toStringRef(Sec->Data);
+ M->Sec = Sec;
+ continue;
+ }
+ if (Sec->Name == ".debug_abbrev")
+ AbbrevSection = toStringRef(Sec->Data);
+ else if (Sec->Name == ".debug_gnu_pubnames")
+ GnuPubNamesSection = toStringRef(Sec->Data);
+ else if (Sec->Name == ".debug_gnu_pubtypes")
+ GnuPubTypesSection = toStringRef(Sec->Data);
+ }
}
-void GdbHashTab::finalizeContents() {
- uint32_t Size = std::max<uint32_t>(1024, NextPowerOf2(Map.size() * 4 / 3));
- uint32_t Mask = Size - 1;
- Table.resize(Size);
+// Find if there is a relocation at Pos in Sec. The code is a bit
+// more complicated than usual because we need to pass a section index
+// to llvm since it has no idea about InputSection.
+template <class ELFT>
+template <class RelTy>
+Optional<RelocAddrEntry>
+LLDDwarfObj<ELFT>::findAux(const InputSectionBase &Sec, uint64_t Pos,
+ ArrayRef<RelTy> Rels) const {
+ auto It = std::lower_bound(
+ Rels.begin(), Rels.end(), Pos,
+ [](const RelTy &A, uint64_t B) { return A.r_offset < B; });
+ if (It == Rels.end() || It->r_offset != Pos)
+ return None;
+ const RelTy &Rel = *It;
- for (auto &P : Map) {
- GdbSymbol *Sym = P.second;
- uint32_t I = Sym->NameHash & Mask;
- uint32_t Step = ((Sym->NameHash * 17) & Mask) | 1;
+ const ObjFile<ELFT> *File = Sec.getFile<ELFT>();
+ uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL);
+ const typename ELFT::Sym &Sym = File->getELFSyms()[SymIndex];
+ uint32_t SecIndex = File->getSectionIndex(Sym);
+ SymbolBody &B = File->getRelocTargetSym(Rel);
+ auto &DR = cast<DefinedRegular>(B);
+ uint64_t Val = DR.Value + getAddend<ELFT>(Rel);
- while (Table[I])
- I = (I + Step) & Mask;
- Table[I] = Sym;
- }
+ // FIXME: We should be consistent about always adding the file
+ // offset or not.
+ if (DR.Section->Flags & ELF::SHF_ALLOC)
+ Val += cast<InputSection>(DR.Section)->getOffsetInFile();
+
+ return RelocAddrEntry{SecIndex, Val};
}
+
+template <class ELFT>
+Optional<RelocAddrEntry> LLDDwarfObj<ELFT>::find(const llvm::DWARFSection &S,
+ uint64_t Pos) const {
+ auto &Sec = static_cast<const LLDDWARFSection &>(S);
+ if (Sec.Sec->AreRelocsRela)
+ return findAux(*Sec.Sec, Pos, Sec.Sec->template relas<ELFT>());
+ return findAux(*Sec.Sec, Pos, Sec.Sec->template rels<ELFT>());
+}
+
+template class elf::LLDDwarfObj<ELF32LE>;
+template class elf::LLDDwarfObj<ELF32BE>;
+template class elf::LLDDwarfObj<ELF64LE>;
+template class elf::LLDDwarfObj<ELF64BE>;
diff --git a/ELF/GdbIndex.h b/ELF/GdbIndex.h
index 527667f72..375e79699 100644
--- a/ELF/GdbIndex.h
+++ b/ELF/GdbIndex.h
@@ -11,68 +11,56 @@
#define LLD_ELF_GDB_INDEX_H
#include "InputFiles.h"
-#include "llvm/Object/ELF.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
+#include "llvm/Object/ELF.h"
namespace lld {
namespace elf {
class InputSection;
-// Struct represents single entry of address area of gdb index.
-struct AddressEntry {
- InputSection *Section;
- uint64_t LowAddress;
- uint64_t HighAddress;
- uint32_t CuIndex;
-};
-
-// Struct represents single entry of compilation units list area of gdb index.
-// It consist of CU offset in .debug_info section and it's size.
-struct CompilationUnitEntry {
- uint64_t CuOffset;
- uint64_t CuLength;
-};
-
-// Represents data about symbol and type names which are used
-// to build symbol table and constant pool area of gdb index.
-struct NameTypeEntry {
- StringRef Name;
- uint8_t Type;
+struct LLDDWARFSection final : public llvm::DWARFSection {
+ InputSectionBase *Sec = nullptr;
};
-// We fill one GdbIndexDataChunk for each object where scan of
-// debug information performed. That information futher used
-// for filling gdb index section areas.
-struct GdbIndexChunk {
- std::vector<AddressEntry> AddressArea;
- std::vector<CompilationUnitEntry> CompilationUnits;
- std::vector<NameTypeEntry> NamesAndTypes;
-};
+template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
+ LLDDWARFSection InfoSection;
+ LLDDWARFSection RangeSection;
+ LLDDWARFSection LineSection;
+ StringRef AbbrevSection;
+ StringRef GnuPubNamesSection;
+ StringRef GnuPubTypesSection;
-// 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;
-};
+ template <class RelTy>
+ llvm::Optional<llvm::RelocAddrEntry> findAux(const InputSectionBase &Sec,
+ uint64_t Pos,
+ ArrayRef<RelTy> Rels) const;
-// 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);
-
- void finalizeContents();
- size_t getCapacity() { return Table.size(); }
- GdbSymbol *getSymbol(size_t I) { return Table[I]; }
-
-private:
- llvm::DenseMap<size_t, GdbSymbol *> Map;
- std::vector<GdbSymbol *> Table;
+ explicit LLDDwarfObj(ObjFile<ELFT> *Obj);
+ const llvm::DWARFSection &getInfoSection() const override {
+ return InfoSection;
+ }
+ const llvm::DWARFSection &getRangeSection() const override {
+ return RangeSection;
+ }
+ const llvm::DWARFSection &getLineSection() const override {
+ return LineSection;
+ }
+ StringRef getCUIndexSection() const override { return ""; }
+ StringRef getAbbrevSection() const override { return AbbrevSection; }
+ StringRef getStringSection() const override { return ""; }
+ StringRef getGnuPubNamesSection() const override {
+ return GnuPubNamesSection;
+ }
+ StringRef getGnuPubTypesSection() const override {
+ return GnuPubTypesSection;
+ }
+ bool isLittleEndian() const override {
+ return ELFT::TargetEndianness == llvm::support::little;
+ }
+ llvm::Optional<llvm::RelocAddrEntry> find(const llvm::DWARFSection &Sec,
+ uint64_t Pos) const override;
};
} // namespace elf
diff --git a/ELF/ICF.cpp b/ELF/ICF.cpp
index 09512a8b0..4de4054fe 100644
--- a/ELF/ICF.cpp
+++ b/ELF/ICF.cpp
@@ -76,7 +76,7 @@
#include "ICF.h"
#include "Config.h"
#include "SymbolTable.h"
-#include "Threads.h"
+#include "lld/Common/Threads.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Object/ELF.h"
@@ -155,7 +155,7 @@ private:
// Returns a hash value for S. Note that the information about
// relocation targets is not included in the hash value.
template <class ELFT> static uint32_t getHash(InputSection *S) {
- return hash_combine(S->Flags, S->getSize(), S->NumRelocations);
+ return hash_combine(S->Flags, S->getSize(), S->NumRelocations, S->Data);
}
// Returns true if section S is subject of ICF.
@@ -207,19 +207,26 @@ void ICF<ELFT>::segregate(size_t Begin, size_t End, bool Constant) {
// Compare two lists of relocations.
template <class ELFT>
template <class RelTy>
-bool ICF<ELFT>::constantEq(const InputSection *A, ArrayRef<RelTy> RelsA,
- const InputSection *B, ArrayRef<RelTy> RelsB) {
- auto Eq = [&](const RelTy &RA, const RelTy &RB) {
- if (RA.r_offset != RB.r_offset ||
- RA.getType(Config->IsMips64EL) != RB.getType(Config->IsMips64EL))
+bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA,
+ const InputSection *SecB, ArrayRef<RelTy> RB) {
+ if (RA.size() != RB.size())
+ return false;
+
+ for (size_t I = 0; I < RA.size(); ++I) {
+ if (RA[I].r_offset != RB[I].r_offset ||
+ RA[I].getType(Config->IsMips64EL) != RB[I].getType(Config->IsMips64EL))
return false;
- uint64_t AddA = getAddend<ELFT>(RA);
- uint64_t AddB = getAddend<ELFT>(RB);
- SymbolBody &SA = A->template getFile<ELFT>()->getRelocTargetSym(RA);
- SymbolBody &SB = B->template getFile<ELFT>()->getRelocTargetSym(RB);
- if (&SA == &SB)
- return AddA == AddB;
+ uint64_t AddA = getAddend<ELFT>(RA[I]);
+ uint64_t AddB = getAddend<ELFT>(RB[I]);
+
+ SymbolBody &SA = SecA->template getFile<ELFT>()->getRelocTargetSym(RA[I]);
+ SymbolBody &SB = SecB->template getFile<ELFT>()->getRelocTargetSym(RB[I]);
+ if (&SA == &SB) {
+ if (AddA == AddB)
+ continue;
+ return false;
+ }
auto *DA = dyn_cast<DefinedRegular>(&SA);
auto *DB = dyn_cast<DefinedRegular>(&SB);
@@ -228,17 +235,21 @@ bool ICF<ELFT>::constantEq(const InputSection *A, ArrayRef<RelTy> RelsA,
// Relocations referring to absolute symbols are constant-equal if their
// values are equal.
+ if (!DA->Section && !DB->Section && DA->Value + AddA == DB->Value + AddB)
+ continue;
if (!DA->Section || !DB->Section)
- return !DA->Section && !DB->Section &&
- DA->Value + AddA == DB->Value + AddB;
+ return false;
if (DA->Section->kind() != DB->Section->kind())
return false;
// Relocations referring to InputSections are constant-equal if their
// section offsets are equal.
- if (isa<InputSection>(DA->Section))
- return DA->Value + AddA == DB->Value + AddB;
+ if (isa<InputSection>(DA->Section)) {
+ if (DA->Value + AddA == DB->Value + AddB)
+ continue;
+ return false;
+ }
// Relocations referring to MergeInputSections are constant-equal if their
// offsets in the output section are equal.
@@ -253,11 +264,11 @@ bool ICF<ELFT>::constantEq(const InputSection *A, ArrayRef<RelTy> RelsA,
SA.isSection() ? X->getOffset(AddA) : X->getOffset(DA->Value) + AddA;
uint64_t OffsetB =
SB.isSection() ? Y->getOffset(AddB) : Y->getOffset(DB->Value) + AddB;
- return OffsetA == OffsetB;
- };
+ if (OffsetA != OffsetB)
+ return false;
+ }
- return RelsA.size() == RelsB.size() &&
- std::equal(RelsA.begin(), RelsA.end(), RelsB.begin(), Eq);
+ return true;
}
// Compare "non-moving" part of two InputSections, namely everything
@@ -278,14 +289,16 @@ bool ICF<ELFT>::equalsConstant(const InputSection *A, const InputSection *B) {
// relocations point to the same section in terms of ICF.
template <class ELFT>
template <class RelTy>
-bool ICF<ELFT>::variableEq(const InputSection *A, ArrayRef<RelTy> RelsA,
- const InputSection *B, ArrayRef<RelTy> RelsB) {
- auto Eq = [&](const RelTy &RA, const RelTy &RB) {
+bool ICF<ELFT>::variableEq(const InputSection *SecA, ArrayRef<RelTy> RA,
+ const InputSection *SecB, ArrayRef<RelTy> RB) {
+ assert(RA.size() == RB.size());
+
+ for (size_t I = 0; I < RA.size(); ++I) {
// The two sections must be identical.
- SymbolBody &SA = A->template getFile<ELFT>()->getRelocTargetSym(RA);
- SymbolBody &SB = B->template getFile<ELFT>()->getRelocTargetSym(RB);
+ SymbolBody &SA = SecA->template getFile<ELFT>()->getRelocTargetSym(RA[I]);
+ SymbolBody &SB = SecB->template getFile<ELFT>()->getRelocTargetSym(RB[I]);
if (&SA == &SB)
- return true;
+ continue;
auto *DA = cast<DefinedRegular>(&SA);
auto *DB = cast<DefinedRegular>(&SB);
@@ -294,21 +307,21 @@ bool ICF<ELFT>::variableEq(const InputSection *A, ArrayRef<RelTy> RelsA,
// constantEq, and for InputSections we have already checked everything
// except the equivalence class.
if (!DA->Section)
- return true;
+ continue;
auto *X = dyn_cast<InputSection>(DA->Section);
if (!X)
- return true;
+ continue;
auto *Y = cast<InputSection>(DB->Section);
// 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];
+ if (X->Class[Current] != Y->Class[Current])
+ return false;
};
- return std::equal(RelsA.begin(), RelsA.end(), RelsB.begin(), Eq);
+ return true;
}
// Compare "moving" part of two InputSections, namely relocation targets.
@@ -353,7 +366,7 @@ 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) {
+ if (!ThreadsEnabled || Sections.size() < 1024) {
forEachClassRange(0, Sections.size(), Fn);
++Cnt;
return;
@@ -381,9 +394,10 @@ template <class ELFT> void ICF<ELFT>::run() {
Sections.push_back(S);
// Initially, we use hash values to partition sections.
- for (InputSection *S : Sections)
+ parallelForEach(Sections, [&](InputSection *S) {
// Set MSB to 1 to avoid collisions with non-hash IDs.
S->Class[0] = getHash<ELFT>(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.
diff --git a/ELF/ICF.h b/ELF/ICF.h
index 502e128c8..24219855f 100644
--- a/ELF/ICF.h
+++ b/ELF/ICF.h
@@ -14,6 +14,6 @@ namespace lld {
namespace elf {
template <class ELFT> void doIcf();
}
-}
+} // namespace lld
#endif
diff --git a/ELF/InputFiles.cpp b/ELF/InputFiles.cpp
index 3d11239bf..fb9559131 100644
--- a/ELF/InputFiles.cpp
+++ b/ELF/InputFiles.cpp
@@ -35,29 +35,23 @@ using namespace llvm::sys::fs;
using namespace lld;
using namespace lld::elf;
+std::vector<BinaryFile *> elf::BinaryFiles;
+std::vector<BitcodeFile *> elf::BitcodeFiles;
+std::vector<InputFile *> elf::ObjectFiles;
+std::vector<InputFile *> elf::SharedFiles;
+
TarWriter *elf::Tar;
InputFile::InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
-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>();
- }
-};
-}
-
Optional<MemoryBufferRef> elf::readFile(StringRef Path) {
+ // The --chroot option changes our virtual root directory.
+ // This is useful when you are dealing with files created by --reproduce.
+ if (!Config->Chroot.empty() && Path.startswith("/"))
+ Path = Saver.save(Config->Chroot + Path);
+
log(Path);
+
auto MBOrErr = MemoryBuffer::getFile(Path);
if (auto EC = MBOrErr.getError()) {
error("cannot open " + Path + ": " + EC.message());
@@ -73,15 +67,12 @@ Optional<MemoryBufferRef> elf::readFile(StringRef Path) {
return MBRef;
}
-template <class ELFT> void elf::ObjectFile<ELFT>::initializeDwarfLine() {
- std::unique_ptr<object::ObjectFile> Obj =
- check(object::ObjectFile::createObjectFile(this->MB), toString(this));
-
- ObjectInfo ObjInfo;
- DWARFContextInMemory Dwarf(*Obj, &ObjInfo);
- DwarfLine.reset(new DWARFDebugLine(&Dwarf.getLineSection().Relocs));
- DataExtractor LineData(Dwarf.getLineSection().Data, Config->IsLE,
- Config->Wordsize);
+template <class ELFT> void ObjFile<ELFT>::initializeDwarfLine() {
+ DWARFContext Dwarf(make_unique<LLDDwarfObj<ELFT>>(this));
+ const DWARFObject &Obj = Dwarf.getDWARFObj();
+ DwarfLine.reset(new DWARFDebugLine);
+ DWARFDataExtractor LineData(Obj, Obj.getLineSection(), Config->IsLE,
+ Config->Wordsize);
// The second parameter is offset in .debug_line section
// for compilation unit (CU) of interest. We have only one
@@ -92,10 +83,9 @@ template <class ELFT> void elf::ObjectFile<ELFT>::initializeDwarfLine() {
// Returns source line information for a given offset
// using DWARF debug info.
template <class ELFT>
-Optional<DILineInfo> elf::ObjectFile<ELFT>::getDILineInfo(InputSectionBase *S,
- uint64_t Offset) {
- if (!DwarfLine)
- initializeDwarfLine();
+Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *S,
+ uint64_t Offset) {
+ llvm::call_once(InitDwarfLine, [this]() { initializeDwarfLine(); });
// The offset to CU is 0.
const DWARFDebugLine::LineTable *Tbl = DwarfLine->getLineTable(0);
@@ -116,8 +106,7 @@ Optional<DILineInfo> elf::ObjectFile<ELFT>::getDILineInfo(InputSectionBase *S,
// Returns source line information for a given offset
// using DWARF debug info.
template <class ELFT>
-std::string elf::ObjectFile<ELFT>::getLineInfo(InputSectionBase *S,
- uint64_t Offset) {
+std::string ObjFile<ELFT>::getLineInfo(InputSectionBase *S, uint64_t Offset) {
if (Optional<DILineInfo> Info = getDILineInfo(S, Offset))
return Info->FileName + ":" + std::to_string(Info->Line);
return "";
@@ -149,13 +138,13 @@ ELFFileBase<ELFT>::ELFFileBase(Kind K, MemoryBufferRef MB) : InputFile(K, MB) {
}
template <class ELFT>
-typename ELFT::SymRange ELFFileBase<ELFT>::getGlobalSymbols() {
- return makeArrayRef(Symbols.begin() + FirstNonLocal, Symbols.end());
+typename ELFT::SymRange ELFFileBase<ELFT>::getGlobalELFSyms() {
+ return makeArrayRef(ELFSyms.begin() + FirstNonLocal, ELFSyms.end());
}
template <class ELFT>
uint32_t ELFFileBase<ELFT>::getSectionIndex(const Elf_Sym &Sym) const {
- return check(getObj().getSectionIndex(&Sym, Symbols, SymtabSHNDX),
+ return check(getObj().getSectionIndex(&Sym, ELFSyms, SymtabSHNDX),
toString(this));
}
@@ -163,8 +152,8 @@ template <class ELFT>
void ELFFileBase<ELFT>::initSymtab(ArrayRef<Elf_Shdr> Sections,
const Elf_Shdr *Symtab) {
FirstNonLocal = Symtab->sh_info;
- Symbols = check(getObj().symbols(Symtab), toString(this));
- if (FirstNonLocal == 0 || FirstNonLocal > Symbols.size())
+ ELFSyms = check(getObj().symbols(Symtab), toString(this));
+ if (FirstNonLocal == 0 || FirstNonLocal > ELFSyms.size())
fatal(toString(this) + ": invalid sh_info in symbol table");
StringTable = check(getObj().getStringTableForSymtab(*Symtab, Sections),
@@ -172,27 +161,19 @@ void ELFFileBase<ELFT>::initSymtab(ArrayRef<Elf_Shdr> Sections,
}
template <class ELFT>
-elf::ObjectFile<ELFT>::ObjectFile(MemoryBufferRef M, StringRef ArchiveName)
- : ELFFileBase<ELFT>(Base::ObjectKind, M) {
+ObjFile<ELFT>::ObjFile(MemoryBufferRef M, StringRef ArchiveName)
+ : ELFFileBase<ELFT>(Base::ObjKind, M) {
this->ArchiveName = ArchiveName;
}
-template <class ELFT>
-ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getLocalSymbols() {
- if (this->SymbolBodies.empty())
- return this->SymbolBodies;
- return makeArrayRef(this->SymbolBodies).slice(1, this->FirstNonLocal - 1);
-}
-
-template <class ELFT>
-ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getSymbols() {
- if (this->SymbolBodies.empty())
- return this->SymbolBodies;
- return makeArrayRef(this->SymbolBodies).slice(1);
+template <class ELFT> ArrayRef<SymbolBody *> ObjFile<ELFT>::getLocalSymbols() {
+ if (this->Symbols.empty())
+ return {};
+ return makeArrayRef(this->Symbols).slice(1, this->FirstNonLocal - 1);
}
template <class ELFT>
-void elf::ObjectFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
+void ObjFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
// Read section and symbol tables.
initializeSections(ComdatGroups);
initializeSymbols();
@@ -202,18 +183,17 @@ void elf::ObjectFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
// They are identified and deduplicated by group name. This function
// returns a group name.
template <class ELFT>
-StringRef
-elf::ObjectFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
- const Elf_Shdr &Sec) {
+StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
+ const Elf_Shdr &Sec) {
// Group signatures are stored as symbol names in object files.
// sh_info contains a symbol index, so we fetch a symbol and read its name.
- if (this->Symbols.empty())
+ if (this->ELFSyms.empty())
this->initSymtab(
Sections,
check(object::getSection<ELFT>(Sections, Sec.sh_link), toString(this)));
const Elf_Sym *Sym = check(
- object::getSymbol<ELFT>(this->Symbols, Sec.sh_info), toString(this));
+ object::getSymbol<ELFT>(this->ELFSyms, Sec.sh_info), toString(this));
StringRef Signature = check(Sym->getName(this->StringTable), toString(this));
// As a special case, if a symbol is a section symbol and has no name,
@@ -229,8 +209,8 @@ elf::ObjectFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
}
template <class ELFT>
-ArrayRef<typename elf::ObjectFile<ELFT>::Elf_Word>
-elf::ObjectFile<ELFT>::getShtGroupEntries(const Elf_Shdr &Sec) {
+ArrayRef<typename ObjFile<ELFT>::Elf_Word>
+ObjFile<ELFT>::getShtGroupEntries(const Elf_Shdr &Sec) {
const ELFFile<ELFT> &Obj = this->getObj();
ArrayRef<Elf_Word> Entries = check(
Obj.template getSectionContentsAsArray<Elf_Word>(&Sec), toString(this));
@@ -239,8 +219,7 @@ elf::ObjectFile<ELFT>::getShtGroupEntries(const Elf_Shdr &Sec) {
return Entries.slice(1);
}
-template <class ELFT>
-bool elf::ObjectFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
+template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
// We don't merge sections if -O0 (default is -O1). This makes sometimes
// the linker significantly faster, although the output will be bigger.
if (Config->Optimize == 0)
@@ -293,7 +272,7 @@ bool elf::ObjectFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
}
template <class ELFT>
-void elf::ObjectFile<ELFT>::initializeSections(
+void ObjFile<ELFT>::initializeSections(
DenseSet<CachedHashStringRef> &ComdatGroups) {
const ELFFile<ELFT> &Obj = this->getObj();
@@ -365,13 +344,13 @@ void elf::ObjectFile<ELFT>::initializeSections(
fatal(toString(this) + ": invalid sh_link index: " +
Twine(Sec.sh_link));
this->Sections[Sec.sh_link]->DependentSections.push_back(
- this->Sections[I]);
+ cast<InputSection>(this->Sections[I]));
}
}
}
template <class ELFT>
-InputSectionBase *elf::ObjectFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
+InputSectionBase *ObjFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
uint32_t Idx = Sec.sh_info;
if (Idx >= this->Sections.size())
fatal(toString(this) + ": invalid relocated section index: " + Twine(Idx));
@@ -398,8 +377,7 @@ InputSectionBase *toRegularSection(MergeInputSection *Sec) {
}
template <class ELFT>
-InputSectionBase *
-elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
+InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
StringRef Name = getSectionName(Sec);
switch (Sec.sh_type) {
@@ -504,15 +482,6 @@ elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
if (Config->Strip != StripPolicy::None && Name.startswith(".debug"))
return &InputSection::Discarded;
- // If -gdb-index is given, LLD creates .gdb_index section, and that
- // section serves the same purpose as .debug_gnu_pub{names,types} sections.
- // If that's the case, we want to eliminate .debug_gnu_pub{names,types}
- // because they are redundant and can waste large amount of disk space
- // (for example, they are about 400 MiB in total for a clang debug build.)
- if (Config->GdbIndex &&
- (Name == ".debug_gnu_pubnames" || Name == ".debug_gnu_pubtypes"))
- return &InputSection::Discarded;
-
// The linkonce feature is a sort of proto-comdat. Some glibc i386 object
// files contain definitions of symbol "__x86.get_pc_thunk.bx" in linkonce
// sections. Drop those sections to avoid duplicate symbol errors.
@@ -533,19 +502,19 @@ elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
}
template <class ELFT>
-StringRef elf::ObjectFile<ELFT>::getSectionName(const Elf_Shdr &Sec) {
+StringRef ObjFile<ELFT>::getSectionName(const Elf_Shdr &Sec) {
return check(this->getObj().getSectionName(&Sec, SectionStringTable),
toString(this));
}
-template <class ELFT> void elf::ObjectFile<ELFT>::initializeSymbols() {
- SymbolBodies.reserve(this->Symbols.size());
- for (const Elf_Sym &Sym : this->Symbols)
- SymbolBodies.push_back(createSymbolBody(&Sym));
+template <class ELFT> void ObjFile<ELFT>::initializeSymbols() {
+ this->Symbols.reserve(this->ELFSyms.size());
+ for (const Elf_Sym &Sym : this->ELFSyms)
+ this->Symbols.push_back(createSymbolBody(&Sym));
}
template <class ELFT>
-InputSectionBase *elf::ObjectFile<ELFT>::getSection(const Elf_Sym &Sym) const {
+InputSectionBase *ObjFile<ELFT>::getSection(const Elf_Sym &Sym) const {
uint32_t Index = this->getSectionIndex(Sym);
if (Index >= this->Sections.size())
fatal(toString(this) + ": invalid section index: " + Twine(Index));
@@ -569,7 +538,7 @@ InputSectionBase *elf::ObjectFile<ELFT>::getSection(const Elf_Sym &Sym) const {
}
template <class ELFT>
-SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
+SymbolBody *ObjFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
int Binding = Sym->getBinding();
InputSectionBase *Sec = getSection(*Sym);
@@ -587,26 +556,25 @@ SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
StringRefZ Name = this->StringTable.data() + Sym->st_name;
if (Sym->st_shndx == SHN_UNDEF)
- return make<Undefined>(Name, /*IsLocal=*/true, StOther, Type, this);
+ return make<Undefined>(Name, /*IsLocal=*/true, StOther, Type);
return make<DefinedRegular>(Name, /*IsLocal=*/true, StOther, Type, Value,
- Size, Sec, this);
+ Size, Sec);
}
StringRef Name = check(Sym->getName(this->StringTable), toString(this));
switch (Sym->st_shndx) {
case SHN_UNDEF:
- return elf::Symtab<ELFT>::X
- ->addUndefined(Name, /*IsLocal=*/false, Binding, StOther, Type,
- /*CanOmitFromDynSym=*/false, this)
+ return Symtab
+ ->addUndefined<ELFT>(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, Size, Value, Binding, StOther, Type, this)
+ return Symtab->addCommon(Name, Size, Value, Binding, StOther, Type, this)
->body();
}
@@ -617,12 +585,12 @@ SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
case STB_WEAK:
case STB_GNU_UNIQUE:
if (Sec == &InputSection::Discarded)
- return elf::Symtab<ELFT>::X
- ->addUndefined(Name, /*IsLocal=*/false, Binding, StOther, Type,
- /*CanOmitFromDynSym=*/false, this)
+ return Symtab
+ ->addUndefined<ELFT>(Name, /*IsLocal=*/false, Binding, StOther, Type,
+ /*CanOmitFromDynSym=*/false, this)
->body();
- return elf::Symtab<ELFT>::X
- ->addRegular(Name, StOther, Type, Value, Size, Binding, Sec, this)
+ return Symtab
+ ->addRegular<ELFT>(Name, StOther, Type, Value, Size, Binding, Sec, this)
->body();
}
}
@@ -632,8 +600,10 @@ ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&File)
File(std::move(File)) {}
template <class ELFT> void ArchiveFile::parse() {
+ Symbols.reserve(File->getNumberOfSymbols());
for (const Archive::Symbol &Sym : File->symbols())
- Symtab<ELFT>::X->addLazyArchive(this, Sym);
+ Symbols.push_back(
+ Symtab->addLazyArchive<ELFT>(Sym.getName(), this, Sym)->body());
}
// Returns a buffer pointing to a member file containing a given symbol.
@@ -670,7 +640,7 @@ template <class ELFT>
const typename ELFT::Shdr *
SharedFile<ELFT>::getSection(const Elf_Sym &Sym) const {
return check(
- this->getObj().getSection(&Sym, this->Symbols, this->SymtabSHNDX),
+ this->getObj().getSection(&Sym, this->ELFSyms, this->SymtabSHNDX),
toString(this));
}
@@ -705,7 +675,7 @@ template <class ELFT> void SharedFile<ELFT>::parseSoName() {
}
}
- if (this->VersymSec && this->Symbols.empty())
+ if (this->VersymSec && this->ELFSyms.empty())
error("SHT_GNU_versym should be associated with symbol table");
// Search for a DT_SONAME tag to initialize this->SoName.
@@ -772,7 +742,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->getGlobalSymbols();
+ Elf_Sym_Range Syms = this->getGlobalELFSyms();
for (const Elf_Sym &Sym : Syms) {
unsigned VersymIndex = 0;
if (Versym) {
@@ -791,19 +761,26 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() {
// Ignore local symbols.
if (Versym && VersymIndex == VER_NDX_LOCAL)
continue;
-
- const Elf_Verdef *V =
- VersymIndex == VER_NDX_GLOBAL ? nullptr : Verdefs[VersymIndex];
+ const Elf_Verdef *V = nullptr;
+ if (VersymIndex != VER_NDX_GLOBAL) {
+ if (VersymIndex >= Verdefs.size()) {
+ error("corrupt input file: version definition index " +
+ Twine(VersymIndex) + " for symbol " + Name +
+ " is out of bounds\n>>> defined in " + toString(this));
+ continue;
+ }
+ V = Verdefs[VersymIndex];
+ }
if (!Hidden)
- elf::Symtab<ELFT>::X->addShared(this, Name, Sym, V);
+ Symtab->addShared(Name, this, Sym, V);
// Also add the symbol with the versioned name to handle undefined symbols
// with explicit versions.
if (V) {
StringRef VerName = this->StringTable.data() + V->getAux()->vda_name;
Name = Saver.save(Name + "@" + VerName);
- elf::Symtab<ELFT>::X->addShared(this, Name, Sym, V);
+ Symtab->addShared(Name, this, Sym, V);
}
}
}
@@ -890,22 +867,20 @@ static Symbol *createBitcodeSymbol(const std::vector<bool> &KeptComdats,
int C = ObjSym.getComdatIndex();
if (C != -1 && !KeptComdats[C])
- return Symtab<ELFT>::X->addUndefined(NameRef, /*IsLocal=*/false, Binding,
- Visibility, Type, CanOmitFromDynSym,
- F);
+ return Symtab->addUndefined<ELFT>(NameRef, /*IsLocal=*/false, Binding,
+ Visibility, Type, CanOmitFromDynSym, F);
if (ObjSym.isUndefined())
- return Symtab<ELFT>::X->addUndefined(NameRef, /*IsLocal=*/false, Binding,
- Visibility, Type, CanOmitFromDynSym,
- F);
+ return Symtab->addUndefined<ELFT>(NameRef, /*IsLocal=*/false, Binding,
+ Visibility, Type, CanOmitFromDynSym, F);
if (ObjSym.isCommon())
- return Symtab<ELFT>::X->addCommon(NameRef, ObjSym.getCommonSize(),
- ObjSym.getCommonAlignment(), Binding,
- Visibility, STT_OBJECT, F);
+ return Symtab->addCommon(NameRef, ObjSym.getCommonSize(),
+ ObjSym.getCommonAlignment(), Binding, Visibility,
+ STT_OBJECT, F);
- return Symtab<ELFT>::X->addBitcode(NameRef, Binding, Visibility, Type,
- CanOmitFromDynSym, F);
+ return Symtab->addBitcode(NameRef, Binding, Visibility, Type,
+ CanOmitFromDynSym, F);
}
template <class ELFT>
@@ -915,7 +890,8 @@ void BitcodeFile::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
KeptComdats.push_back(ComdatGroups.insert(CachedHashStringRef(S)).second);
for (const lto::InputFile::Symbol &ObjSym : Obj->symbols())
- Symbols.push_back(createBitcodeSymbol<ELFT>(KeptComdats, ObjSym, this));
+ Symbols.push_back(
+ createBitcodeSymbol<ELFT>(KeptComdats, ObjSym, this)->body());
}
static ELFKind getELFKind(MemoryBufferRef MB) {
@@ -950,18 +926,15 @@ template <class ELFT> void BinaryFile::parse() {
// characters in a filename are replaced with underscore.
std::string S = "_binary_" + MB.getBufferIdentifier().str();
for (size_t I = 0; I < S.size(); ++I)
- if (!isalnum(S[I]))
+ if (!isAlnum(S[I]))
S[I] = '_';
- elf::Symtab<ELFT>::X->addRegular(Saver.save(S + "_start"), STV_DEFAULT,
- STT_OBJECT, 0, 0, STB_GLOBAL, Section,
- nullptr);
- elf::Symtab<ELFT>::X->addRegular(Saver.save(S + "_end"), STV_DEFAULT,
- STT_OBJECT, Data.size(), 0, STB_GLOBAL,
- Section, nullptr);
- elf::Symtab<ELFT>::X->addRegular(Saver.save(S + "_size"), STV_DEFAULT,
- STT_OBJECT, Data.size(), 0, STB_GLOBAL,
- nullptr, nullptr);
+ Symtab->addRegular<ELFT>(Saver.save(S + "_start"), STV_DEFAULT, STT_OBJECT,
+ 0, 0, STB_GLOBAL, Section, nullptr);
+ Symtab->addRegular<ELFT>(Saver.save(S + "_end"), STV_DEFAULT, STT_OBJECT,
+ Data.size(), 0, STB_GLOBAL, Section, nullptr);
+ Symtab->addRegular<ELFT>(Saver.save(S + "_size"), STV_DEFAULT, STT_OBJECT,
+ Data.size(), 0, STB_GLOBAL, nullptr, nullptr);
}
static bool isBitcode(MemoryBufferRef MB) {
@@ -976,13 +949,13 @@ InputFile *elf::createObjectFile(MemoryBufferRef MB, StringRef ArchiveName,
switch (getELFKind(MB)) {
case ELF32LEKind:
- return make<ObjectFile<ELF32LE>>(MB, ArchiveName);
+ return make<ObjFile<ELF32LE>>(MB, ArchiveName);
case ELF32BEKind:
- return make<ObjectFile<ELF32BE>>(MB, ArchiveName);
+ return make<ObjFile<ELF32BE>>(MB, ArchiveName);
case ELF64LEKind:
- return make<ObjectFile<ELF64LE>>(MB, ArchiveName);
+ return make<ObjFile<ELF64LE>>(MB, ArchiveName);
case ELF64BEKind:
- return make<ObjectFile<ELF64BE>>(MB, ArchiveName);
+ return make<ObjFile<ELF64BE>>(MB, ArchiveName);
default:
llvm_unreachable("getELFKind");
}
@@ -1003,31 +976,31 @@ InputFile *elf::createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName) {
}
}
-MemoryBufferRef LazyObjectFile::getBuffer() {
+MemoryBufferRef LazyObjFile::getBuffer() {
if (Seen)
return MemoryBufferRef();
Seen = true;
return MB;
}
-InputFile *LazyObjectFile::fetch() {
+InputFile *LazyObjFile::fetch() {
MemoryBufferRef MBRef = getBuffer();
if (MBRef.getBuffer().empty())
return nullptr;
return createObjectFile(MBRef, ArchiveName, OffsetInArchive);
}
-template <class ELFT> void LazyObjectFile::parse() {
- for (StringRef Sym : getSymbols())
- Symtab<ELFT>::X->addLazyObject(Sym, *this);
+template <class ELFT> void LazyObjFile::parse() {
+ for (StringRef Sym : getSymbolNames())
+ Symtab->addLazyObject<ELFT>(Sym, *this);
}
-template <class ELFT> std::vector<StringRef> LazyObjectFile::getElfSymbols() {
+template <class ELFT> std::vector<StringRef> LazyObjFile::getElfSymbols() {
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::SymRange Elf_Sym_Range;
- const ELFFile<ELFT> Obj(this->MB.getBuffer());
+ ELFFile<ELFT> Obj = check(ELFFile<ELFT>::create(this->MB.getBuffer()));
ArrayRef<Elf_Shdr> Sections = check(Obj.sections(), toString(this));
for (const Elf_Shdr &Sec : Sections) {
if (Sec.sh_type != SHT_SYMTAB)
@@ -1047,7 +1020,7 @@ template <class ELFT> std::vector<StringRef> LazyObjectFile::getElfSymbols() {
return {};
}
-std::vector<StringRef> LazyObjectFile::getBitcodeSymbols() {
+std::vector<StringRef> LazyObjFile::getBitcodeSymbols() {
std::unique_ptr<lto::InputFile> Obj =
check(lto::InputFile::create(this->MB), toString(this));
std::vector<StringRef> V;
@@ -1058,7 +1031,7 @@ std::vector<StringRef> LazyObjectFile::getBitcodeSymbols() {
}
// Returns a vector of globally-visible defined symbol names.
-std::vector<StringRef> LazyObjectFile::getSymbols() {
+std::vector<StringRef> LazyObjFile::getSymbolNames() {
if (isBitcode(this->MB))
return getBitcodeSymbols();
@@ -1086,20 +1059,20 @@ 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>();
-template void LazyObjectFile::parse<ELF64LE>();
-template void LazyObjectFile::parse<ELF64BE>();
+template void LazyObjFile::parse<ELF32LE>();
+template void LazyObjFile::parse<ELF32BE>();
+template void LazyObjFile::parse<ELF64LE>();
+template void LazyObjFile::parse<ELF64BE>();
template class elf::ELFFileBase<ELF32LE>;
template class elf::ELFFileBase<ELF32BE>;
template class elf::ELFFileBase<ELF64LE>;
template class elf::ELFFileBase<ELF64BE>;
-template class elf::ObjectFile<ELF32LE>;
-template class elf::ObjectFile<ELF32BE>;
-template class elf::ObjectFile<ELF64LE>;
-template class elf::ObjectFile<ELF64BE>;
+template class elf::ObjFile<ELF32LE>;
+template class elf::ObjFile<ELF32BE>;
+template class elf::ObjFile<ELF64LE>;
+template class elf::ObjFile<ELF64BE>;
template class elf::SharedFile<ELF32LE>;
template class elf::SharedFile<ELF32BE>;
diff --git a/ELF/InputFiles.h b/ELF/InputFiles.h
index 2eec78444..00c8ee936 100644
--- a/ELF/InputFiles.h
+++ b/ELF/InputFiles.h
@@ -11,12 +11,12 @@
#define LLD_ELF_INPUT_FILES_H
#include "Config.h"
-#include "InputSection.h"
#include "Error.h"
+#include "InputSection.h"
#include "Symbols.h"
-#include "lld/Core/LLVM.h"
-#include "lld/Core/Reproduce.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Common/Reproduce.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
@@ -24,6 +24,7 @@
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
#include "llvm/Object/IRObjectFile.h"
+#include "llvm/Support/Threading.h"
#include <map>
@@ -34,7 +35,7 @@ struct DILineInfo;
namespace lto {
class InputFile;
}
-}
+} // namespace llvm
namespace lld {
namespace elf {
@@ -62,9 +63,9 @@ llvm::Optional<MemoryBufferRef> readFile(StringRef Path);
class InputFile {
public:
enum Kind {
- ObjectKind,
+ ObjKind,
SharedKind,
- LazyObjectKind,
+ LazyObjKind,
ArchiveKind,
BitcodeKind,
BinaryKind,
@@ -78,10 +79,18 @@ public:
// Returns sections. It is a runtime error to call this function
// on files that don't have the notion of sections.
ArrayRef<InputSectionBase *> getSections() const {
- assert(FileKind == ObjectKind || FileKind == BinaryKind);
+ assert(FileKind == ObjKind || FileKind == BinaryKind);
return Sections;
}
+ // Returns object file symbols. It is a runtime error to call this
+ // function on files of other types.
+ ArrayRef<SymbolBody *> getSymbols() {
+ assert(FileKind == ObjKind || FileKind == BitcodeKind ||
+ FileKind == ArchiveKind);
+ return Symbols;
+ }
+
// Filename of .a which contained this file. If this file was
// not in an archive file, it is the empty string. We use this
// string for creating error messages.
@@ -99,6 +108,7 @@ public:
protected:
InputFile(Kind K, MemoryBufferRef M);
std::vector<InputSectionBase *> Sections;
+ std::vector<SymbolBody *> Symbols;
private:
const Kind FileKind;
@@ -114,21 +124,22 @@ public:
ELFFileBase(Kind K, MemoryBufferRef M);
static bool classof(const InputFile *F) {
Kind K = F->kind();
- return K == ObjectKind || K == SharedKind;
+ return K == ObjKind || K == SharedKind;
}
llvm::object::ELFFile<ELFT> getObj() const {
- return llvm::object::ELFFile<ELFT>(MB.getBuffer());
+ return check(llvm::object::ELFFile<ELFT>::create(MB.getBuffer()));
}
StringRef getStringTable() const { return StringTable; }
uint32_t getSectionIndex(const Elf_Sym &Sym) const;
- Elf_Sym_Range getGlobalSymbols();
+ Elf_Sym_Range getGlobalELFSyms();
+ Elf_Sym_Range getELFSyms() const { return ELFSyms; }
protected:
- ArrayRef<Elf_Sym> Symbols;
+ ArrayRef<Elf_Sym> ELFSyms;
uint32_t FirstNonLocal = 0;
ArrayRef<Elf_Word> SymtabSHNDX;
StringRef StringTable;
@@ -136,7 +147,7 @@ protected:
};
// .o file.
-template <class ELFT> class ObjectFile : public ELFFileBase<ELFT> {
+template <class ELFT> class ObjFile : public ELFFileBase<ELFT> {
typedef ELFFileBase<ELFT> Base;
typedef typename ELFT::Rel Elf_Rel;
typedef typename ELFT::Rela Elf_Rela;
@@ -149,22 +160,19 @@ template <class ELFT> class ObjectFile : public ELFFileBase<ELFT> {
ArrayRef<Elf_Word> getShtGroupEntries(const Elf_Shdr &Sec);
public:
- static bool classof(const InputFile *F) {
- return F->kind() == Base::ObjectKind;
- }
+ static bool classof(const InputFile *F) { return F->kind() == Base::ObjKind; }
- ArrayRef<SymbolBody *> getSymbols();
ArrayRef<SymbolBody *> getLocalSymbols();
- ObjectFile(MemoryBufferRef M, StringRef ArchiveName);
+ ObjFile(MemoryBufferRef M, StringRef ArchiveName);
void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
InputSectionBase *getSection(const Elf_Sym &Sym) const;
SymbolBody &getSymbolBody(uint32_t SymbolIndex) const {
- if (SymbolIndex >= SymbolBodies.size())
+ if (SymbolIndex >= this->Symbols.size())
fatal(toString(this) + ": invalid symbol index");
- return *SymbolBodies[SymbolIndex];
+ return *this->Symbols[SymbolIndex];
}
template <typename RelT>
@@ -200,9 +208,6 @@ private:
bool shouldMerge(const Elf_Shdr &Sec);
SymbolBody *createSymbolBody(const Elf_Sym *Sym);
- // List of all symbols referenced or defined by this file.
- std::vector<SymbolBody *> SymbolBodies;
-
// .shstrtab contents.
StringRef SectionStringTable;
@@ -211,33 +216,32 @@ private:
// 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;
+ llvm::once_flag InitDwarfLine;
};
-// LazyObjectFile is analogous to ArchiveFile in the sense that
+// LazyObjFile is analogous to ArchiveFile in the sense that
// the file contains lazy symbols. The difference is that
-// LazyObjectFile wraps a single file instead of multiple files.
+// LazyObjFile wraps a single file instead of multiple files.
//
// This class is used for --start-lib and --end-lib options which
// instruct the linker to link object files between them with the
// archive file semantics.
-class LazyObjectFile : public InputFile {
+class LazyObjFile : public InputFile {
public:
- LazyObjectFile(MemoryBufferRef M, StringRef ArchiveName,
- uint64_t OffsetInArchive)
- : InputFile(LazyObjectKind, M), OffsetInArchive(OffsetInArchive) {
+ LazyObjFile(MemoryBufferRef M, StringRef ArchiveName,
+ uint64_t OffsetInArchive)
+ : InputFile(LazyObjKind, M), OffsetInArchive(OffsetInArchive) {
this->ArchiveName = ArchiveName;
}
- static bool classof(const InputFile *F) {
- return F->kind() == LazyObjectKind;
- }
+ static bool classof(const InputFile *F) { return F->kind() == LazyObjKind; }
template <class ELFT> void parse();
MemoryBufferRef getBuffer();
InputFile *fetch();
private:
- std::vector<StringRef> getSymbols();
+ std::vector<StringRef> getSymbolNames();
template <class ELFT> std::vector<StringRef> getElfSymbols();
std::vector<StringRef> getBitcodeSymbols();
@@ -270,11 +274,7 @@ public:
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
template <class ELFT>
void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
- ArrayRef<Symbol *> getSymbols() { return Symbols; }
std::unique_ptr<llvm::lto::InputFile> Obj;
-
-private:
- std::vector<Symbol *> Symbols;
};
// .so file.
@@ -336,6 +336,11 @@ InputFile *createObjectFile(MemoryBufferRef MB, StringRef ArchiveName = "",
uint64_t OffsetInArchive = 0);
InputFile *createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName);
+extern std::vector<BinaryFile *> BinaryFiles;
+extern std::vector<BitcodeFile *> BitcodeFiles;
+extern std::vector<InputFile *> ObjectFiles;
+extern std::vector<InputFile *> SharedFiles;
+
} // namespace elf
} // namespace lld
diff --git a/ELF/InputSection.cpp b/ELF/InputSection.cpp
index 9aae82bc2..4593cc4cf 100644
--- a/ELF/InputSection.cpp
+++ b/ELF/InputSection.cpp
@@ -25,6 +25,7 @@
#include "llvm/Support/Endian.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Threading.h"
+#include "llvm/Support/xxhash.h"
#include <mutex>
using namespace llvm;
@@ -44,8 +45,31 @@ std::string lld::toString(const InputSectionBase *Sec) {
return (toString(Sec->File) + ":(" + Sec->Name + ")").str();
}
+DenseMap<SectionBase *, int> elf::buildSectionOrder() {
+ // 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<SectionBase *, int> SectionOrder;
+ for (InputFile *File : ObjectFiles) {
+ for (SymbolBody *Body : File->getSymbols()) {
+ auto *D = dyn_cast<DefinedRegular>(Body);
+ if (!D || !D->Section)
+ continue;
+ int &Priority = SectionOrder[D->Section];
+ Priority = std::min(Priority, SymbolOrder.lookup(D->getName()));
+ }
+ }
+ return SectionOrder;
+}
+
template <class ELFT>
-static ArrayRef<uint8_t> getSectionContents(elf::ObjectFile<ELFT> *File,
+static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> *File,
const typename ELFT::Shdr *Hdr) {
if (!File || Hdr->sh_type == SHT_NOBITS)
return makeArrayRef<uint8_t>(nullptr, Hdr->sh_size);
@@ -60,7 +84,6 @@ InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags,
: SectionBase(SectionKind, Name, Flags, Entsize, Alignment, Type, Info,
Link),
File(File), Data(Data), Repl(this) {
- Live = !Config->GcSections || !(Flags & SHF_ALLOC);
Assigned = false;
NumRelocations = 0;
AreRelocsRela = false;
@@ -102,7 +125,7 @@ static uint64_t getType(uint64_t Type, StringRef Name) {
}
template <class ELFT>
-InputSectionBase::InputSectionBase(elf::ObjectFile<ELFT> *File,
+InputSectionBase::InputSectionBase(ObjFile<ELFT> *File,
const typename ELFT::Shdr *Hdr,
StringRef Name, Kind SectionKind)
: InputSectionBase(File, getFlags(Hdr->sh_flags),
@@ -160,7 +183,7 @@ uint64_t SectionBase::getOffset(uint64_t Offset) const {
OutputSection *SectionBase::getOutputSection() {
InputSection *Sec;
if (auto *IS = dyn_cast<InputSection>(this))
- Sec = IS;
+ Sec = cast<InputSection>(IS->Repl);
else if (auto *MS = dyn_cast<MergeInputSection>(this))
Sec = MS->getParent();
else if (auto *EH = dyn_cast<EhInputSection>(this))
@@ -170,24 +193,22 @@ OutputSection *SectionBase::getOutputSection() {
return Sec ? Sec->getParent() : nullptr;
}
-// Uncompress section contents. Note that this function is called
-// from parallel_for_each, so it must be thread-safe.
-void InputSectionBase::uncompress() {
+// Uncompress section contents if required. Note that this function
+// is called from parallelForEach, so it must be thread-safe.
+void InputSectionBase::maybeUncompress() {
+ if (UncompressBuf || !Decompressor::isCompressedELFSection(Flags, Name))
+ return;
+
Decompressor Dec = check(Decompressor::create(Name, toStringRef(Data),
Config->IsLE, Config->Is64));
size_t Size = Dec.getDecompressedSize();
- char *OutputBuf;
- {
- static std::mutex Mu;
- std::lock_guard<std::mutex> Lock(Mu);
- OutputBuf = BAlloc.Allocate<char>(Size);
- }
-
- if (Error E = Dec.decompress({OutputBuf, Size}))
+ UncompressBuf.reset(new char[Size]());
+ if (Error E = Dec.decompress({UncompressBuf.get(), Size}))
fatal(toString(this) +
": decompress failed: " + llvm::toString(std::move(E)));
- this->Data = ArrayRef<uint8_t>((uint8_t *)OutputBuf, Size);
+
+ this->Data = makeArrayRef((uint8_t *)UncompressBuf.get(), Size);
this->Flags &= ~(uint64_t)SHF_COMPRESSED;
}
@@ -200,9 +221,9 @@ InputSection *InputSectionBase::getLinkOrderDep() const {
InputSectionBase *L = File->getSections()[Link];
if (auto *IS = dyn_cast<InputSection>(L))
return IS;
- error(
- "Merge and .eh_frame sections are not supported with SHF_LINK_ORDER " +
- toString(L));
+ error("a section with SHF_LINK_ORDER should not refer a non-regular "
+ "section: " +
+ toString(L));
}
return nullptr;
}
@@ -246,7 +267,7 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
// Returns an empty string if there's no way to get line info.
template <class ELFT> std::string InputSectionBase::getSrcMsg(uint64_t Offset) {
// Synthetic sections don't have input files.
- elf::ObjectFile<ELFT> *File = getFile<ELFT>();
+ ObjFile<ELFT> *File = getFile<ELFT>();
if (!File)
return "";
@@ -275,8 +296,10 @@ template <class ELFT> std::string InputSectionBase::getSrcMsg(uint64_t Offset) {
// path/to/foo.o:(function bar) in archive path/to/bar.a
template <class ELFT> std::string InputSectionBase::getObjMsg(uint64_t Off) {
// Synthetic sections don't have input files.
- elf::ObjectFile<ELFT> *File = getFile<ELFT>();
- std::string Filename = File ? File->getName() : "(internal)";
+ ObjFile<ELFT> *File = getFile<ELFT>();
+ if (!File)
+ return ("(internal):(" + Name + "+0x" + utohexstr(Off) + ")").str();
+ std::string Filename = File->getName();
std::string Archive;
if (!File->ArchiveName.empty())
@@ -302,8 +325,8 @@ InputSection::InputSection(uint64_t Flags, uint32_t Type, uint32_t Alignment,
Name, K) {}
template <class ELFT>
-InputSection::InputSection(elf::ObjectFile<ELFT> *F,
- const typename ELFT::Shdr *Header, StringRef Name)
+InputSection::InputSection(ObjFile<ELFT> *F, const typename ELFT::Shdr *Header,
+ StringRef Name)
: InputSectionBase(F, Header, Name, InputSectionBase::Regular) {}
bool InputSection::classof(const SectionBase *S) {
@@ -311,10 +334,6 @@ bool InputSection::classof(const SectionBase *S) {
S->kind() == SectionBase::Synthetic;
}
-bool InputSectionBase::classof(const SectionBase *S) {
- return S->kind() != Output;
-}
-
OutputSection *InputSection::getParent() const {
return cast_or_null<OutputSection>(Parent);
}
@@ -347,14 +366,10 @@ InputSectionBase *InputSection::getRelocatedSection() {
// for each relocation. So we copy relocations one by one.
template <class ELFT, class RelTy>
void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
- InputSectionBase *RelocatedSection = getRelocatedSection();
+ InputSectionBase *Sec = getRelocatedSection();
- // Loop is slow and have complexity O(N*M), where N - amount of
- // relocations and M - amount of symbols in symbol table.
- // That happens because getSymbolIndex(...) call below performs
- // simple linear search.
for (const RelTy &Rel : Rels) {
- uint32_t Type = Rel.getType(Config->IsMips64EL);
+ RelType Type = Rel.getType(Config->IsMips64EL);
SymbolBody &Body = this->getFile<ELFT>()->getRelocTargetSym(Rel);
auto *P = reinterpret_cast<typename ELFT::Rela *>(Buf);
@@ -365,8 +380,7 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
// Output section VA is zero for -r, so r_offset is an offset within the
// section, but for --emit-relocs it is an virtual address.
- P->r_offset = RelocatedSection->getOutputSection()->Addr +
- RelocatedSection->getOffset(Rel.r_offset);
+ P->r_offset = Sec->getOutputSection()->Addr + Sec->getOffset(Rel.r_offset);
P->setSymbolAndType(InX::SymTab->getSymbolIndex(&Body), Type,
Config->IsMips64EL);
@@ -389,19 +403,26 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
if (Config->IsRela) {
P->r_addend += Body.getVA() - Section->getOutputSection()->Addr;
} else if (Config->Relocatable) {
- const uint8_t *BufLoc = RelocatedSection->Data.begin() + Rel.r_offset;
- RelocatedSection->Relocations.push_back(
- {R_ABS, Type, Rel.r_offset, Target->getImplicitAddend(BufLoc, Type),
- &Body});
+ const uint8_t *BufLoc = Sec->Data.begin() + Rel.r_offset;
+ Sec->Relocations.push_back({R_ABS, Type, Rel.r_offset,
+ Target->getImplicitAddend(BufLoc, Type),
+ &Body});
}
}
}
}
-static uint32_t getARMUndefinedRelativeWeakVA(uint32_t Type, uint32_t A,
+// The ARM and AArch64 ABI handle pc-relative relocations to undefined weak
+// references specially. The general rule is that the value of the symbol in
+// this context is the address of the place P. A further special case is that
+// branch relocations to an undefined weak reference resolve to the next
+// instruction.
+static uint32_t getARMUndefinedRelativeWeakVA(RelType Type, uint32_t A,
uint32_t P) {
switch (Type) {
+ // Unresolved branch relocations to weak references resolve to next
+ // instruction, this will be either 2 or 4 bytes on from P.
case R_ARM_THM_JUMP11:
return P + 2 + A;
case R_ARM_CALL:
@@ -415,22 +436,39 @@ static uint32_t getARMUndefinedRelativeWeakVA(uint32_t Type, uint32_t A,
case R_ARM_THM_CALL:
// We don't want an interworking BLX to ARM
return P + 5 + A;
- default:
+ // Unresolved non branch pc-relative relocations
+ // R_ARM_TARGET2 which can be resolved relatively is not present as it never
+ // targets a weak-reference.
+ case R_ARM_MOVW_PREL_NC:
+ case R_ARM_MOVT_PREL:
+ case R_ARM_REL32:
+ case R_ARM_THM_MOVW_PREL_NC:
+ case R_ARM_THM_MOVT_PREL:
return P + A;
}
+ llvm_unreachable("ARM pc-relative relocation expected\n");
}
+// The comment above getARMUndefinedRelativeWeakVA applies to this function.
static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t Type, uint64_t A,
uint64_t P) {
switch (Type) {
+ // Unresolved branch relocations to weak references resolve to next
+ // instruction, this is 4 bytes on from P.
case R_AARCH64_CALL26:
case R_AARCH64_CONDBR19:
case R_AARCH64_JUMP26:
case R_AARCH64_TSTBR14:
return P + 4 + A;
- default:
+ // Unresolved non branch pc-relative relocations
+ case R_AARCH64_PREL16:
+ case R_AARCH64_PREL32:
+ case R_AARCH64_PREL64:
+ case R_AARCH64_ADR_PREL_LO21:
+ case R_AARCH64_LD_PREL_LO19:
return P + A;
}
+ llvm_unreachable("AArch64 pc-relative relocation expected\n");
}
// ARM SBREL relocations are of the form S + A - B where B is the static base
@@ -442,14 +480,16 @@ static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t Type, uint64_t A,
// of the RW segment.
static uint64_t getARMStaticBase(const SymbolBody &Body) {
OutputSection *OS = Body.getOutputSection();
- if (!OS || !OS->FirstInPtLoad)
- fatal("SBREL relocation to " + Body.getName() + " without static base\n");
- return OS->FirstInPtLoad->Addr;
+ if (!OS || !OS->PtLoad || !OS->PtLoad->FirstSec)
+ fatal("SBREL relocation to " + Body.getName() + " without static base");
+ return OS->PtLoad->FirstSec->Addr;
}
-static uint64_t getRelocTargetVA(uint32_t Type, int64_t A, uint64_t P,
+static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
const SymbolBody &Body, RelExpr Expr) {
switch (Expr) {
+ case R_INVALID:
+ return 0;
case R_ABS:
case R_RELAX_GOT_PC_NOPIC:
return Body.getVA(A);
@@ -491,7 +531,7 @@ static uint64_t getRelocTargetVA(uint32_t Type, int64_t A, uint64_t P,
// formula for calculation "AHL + GP - P + 4". For details see p. 4-19 at
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
uint64_t V = InX::MipsGot->getGp() + A - P;
- if (Type == R_MIPS_LO16)
+ if (Type == R_MIPS_LO16 || Type == R_MICROMIPS_LO16)
V += 4;
return V;
}
@@ -517,7 +557,7 @@ static uint64_t getRelocTargetVA(uint32_t Type, int64_t A, uint64_t P,
case R_PAGE_PC:
case R_PLT_PAGE_PC: {
uint64_t Dest;
- if (Body.isUndefined() && !Body.isLocal() && Body.symbol()->isWeak())
+ if (Body.isUndefWeak())
Dest = getAArch64Page(A);
else
Dest = getAArch64Page(Body.getVA(A));
@@ -525,7 +565,7 @@ static uint64_t getRelocTargetVA(uint32_t Type, int64_t A, uint64_t P,
}
case R_PC: {
uint64_t Dest;
- if (Body.isUndefined() && !Body.isLocal() && Body.symbol()->isWeak()) {
+ if (Body.isUndefWeak()) {
// On ARM and AArch64 a branch to an undefined weak resolves to the
// next instruction, otherwise the place.
if (Config->EMachine == EM_ARM)
@@ -575,8 +615,7 @@ static uint64_t getRelocTargetVA(uint32_t Type, int64_t A, uint64_t P,
// 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())
+ if (Body.isTls() && Body.isUndefWeak())
return 0;
if (Target->TcbSize)
return Body.getVA(A) + alignTo(Target->TcbSize, Out::TlsPhdr->p_align);
@@ -612,8 +651,10 @@ static uint64_t getRelocTargetVA(uint32_t Type, int64_t A, uint64_t P,
// function as a performance optimization.
template <class ELFT, class RelTy>
void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
+ const unsigned Bits = sizeof(typename ELFT::uint) * 8;
+
for (const RelTy &Rel : Rels) {
- uint32_t Type = Rel.getType(Config->IsMips64EL);
+ RelType Type = Rel.getType(Config->IsMips64EL);
uint64_t Offset = getOffset(Rel.r_offset);
uint8_t *BufLoc = Buf + Offset;
int64_t Addend = getAddend<ELFT>(Rel);
@@ -625,51 +666,40 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef<RelTy> Rels) {
if (Expr == R_NONE)
continue;
if (Expr != R_ABS) {
- error(this->getLocation<ELFT>(Offset) + ": has non-ABS reloc");
+ error(this->getLocation<ELFT>(Offset) + ": has non-ABS relocation " +
+ toString(Type) + " against symbol '" + toString(Sym) + "'");
return;
}
- uint64_t AddrLoc = getParent()->Addr + Offset;
- uint64_t SymVA = 0;
- if (!Sym.isTls() || Out::TlsPhdr)
- SymVA = SignExtend64<sizeof(typename ELFT::uint) * 8>(
- getRelocTargetVA(Type, Addend, AddrLoc, Sym, R_ABS));
- Target->relocateOne(BufLoc, Type, SymVA);
+ if (Sym.isTls() && !Out::TlsPhdr)
+ Target->relocateOne(BufLoc, Type, 0);
+ else
+ Target->relocateOne(BufLoc, Type, SignExtend64<Bits>(Sym.getVA(Addend)));
}
}
-template <class ELFT> elf::ObjectFile<ELFT> *InputSectionBase::getFile() const {
- return cast_or_null<elf::ObjectFile<ELFT>>(File);
-}
-
template <class ELFT>
void InputSectionBase::relocate(uint8_t *Buf, uint8_t *BufEnd) {
- if (Flags & SHF_ALLOC)
+ if (Flags & SHF_ALLOC) {
relocateAlloc(Buf, BufEnd);
- else
- relocateNonAlloc<ELFT>(Buf, BufEnd);
-}
+ return;
+ }
-template <class ELFT>
-void InputSectionBase::relocateNonAlloc(uint8_t *Buf, uint8_t *BufEnd) {
- // scanReloc function in Writer.cpp constructs Relocations
- // vector only for SHF_ALLOC'ed sections. For other sections,
- // we handle relocations directly here.
- auto *IS = cast<InputSection>(this);
- assert(!(IS->Flags & SHF_ALLOC));
- if (IS->AreRelocsRela)
- IS->relocateNonAlloc<ELFT>(Buf, IS->template relas<ELFT>());
+ auto *Sec = cast<InputSection>(this);
+ if (Sec->AreRelocsRela)
+ Sec->relocateNonAlloc<ELFT>(Buf, Sec->template relas<ELFT>());
else
- IS->relocateNonAlloc<ELFT>(Buf, IS->template rels<ELFT>());
+ Sec->relocateNonAlloc<ELFT>(Buf, Sec->template rels<ELFT>());
}
void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) {
assert(Flags & SHF_ALLOC);
const unsigned Bits = Config->Wordsize * 8;
+
for (const Relocation &Rel : Relocations) {
uint64_t Offset = getOffset(Rel.Offset);
uint8_t *BufLoc = Buf + Offset;
- uint32_t Type = Rel.Type;
+ RelType Type = Rel.Type;
uint64_t AddrLoc = getOutputSection()->Addr + Offset;
RelExpr Expr = Rel.Expr;
@@ -751,7 +781,7 @@ void InputSection::replace(InputSection *Other) {
}
template <class ELFT>
-EhInputSection::EhInputSection(elf::ObjectFile<ELFT> *F,
+EhInputSection::EhInputSection(ObjFile<ELFT> *F,
const typename ELFT::Shdr *Header,
StringRef Name)
: InputSectionBase(F, Header, Name, InputSectionBase::EHFrame) {
@@ -765,10 +795,6 @@ SyntheticSection *EhInputSection::getParent() const {
return cast_or_null<SyntheticSection>(Parent);
}
-bool EhInputSection::classof(const SectionBase *S) {
- return S->kind() == InputSectionBase::EHFrame;
-}
-
// Returns the index of the first relocation that points to a region between
// Begin and Begin+Size.
template <class IntTy, class RelTy>
@@ -848,7 +874,7 @@ void MergeInputSection::splitStrings(ArrayRef<uint8_t> Data, size_t EntSize) {
fatal(toString(this) + ": string is not null terminated");
size_t Size = End + EntSize;
Pieces.emplace_back(Off, !IsAlloc);
- Hashes.push_back(hash_value(toStringRef(Data.slice(0, Size))));
+ Hashes.push_back(xxHash64(toStringRef(Data.slice(0, Size))));
Data = Data.slice(Size);
Off += Size;
}
@@ -862,13 +888,13 @@ void MergeInputSection::splitNonStrings(ArrayRef<uint8_t> Data,
assert((Size % EntSize) == 0);
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))));
+ Hashes.push_back(xxHash64(toStringRef(Data.slice(I, EntSize))));
Pieces.emplace_back(I, !IsAlloc);
}
}
template <class ELFT>
-MergeInputSection::MergeInputSection(elf::ObjectFile<ELFT> *F,
+MergeInputSection::MergeInputSection(ObjFile<ELFT> *F,
const typename ELFT::Shdr *Header,
StringRef Name)
: InputSectionBase(F, Header, Name, InputSectionBase::Merge) {}
@@ -877,9 +903,10 @@ MergeInputSection::MergeInputSection(elf::ObjectFile<ELFT> *F,
// 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
+// Note that this function is called from parallelForEach. This must be
// thread-safe (i.e. no memory allocation from the pools).
void MergeInputSection::splitIntoPieces() {
+ assert(Pieces.empty());
ArrayRef<uint8_t> Data = this->Data;
uint64_t EntSize = this->Entsize;
if (this->Flags & SHF_STRINGS)
@@ -892,10 +919,6 @@ void MergeInputSection::splitIntoPieces() {
this->getSectionPiece(Off)->Live = true;
}
-bool MergeInputSection::classof(const SectionBase *S) {
- return S->kind() == InputSectionBase::Merge;
-}
-
// Do binary search to get a section piece at a given input offset.
SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) {
auto *This = static_cast<const MergeInputSection *>(this);
@@ -957,14 +980,14 @@ uint64_t MergeInputSection::getOffset(uint64_t Offset) const {
return Piece.OutputOff + Addend;
}
-template InputSection::InputSection(elf::ObjectFile<ELF32LE> *,
- const ELF32LE::Shdr *, StringRef);
-template InputSection::InputSection(elf::ObjectFile<ELF32BE> *,
- const ELF32BE::Shdr *, StringRef);
-template InputSection::InputSection(elf::ObjectFile<ELF64LE> *,
- const ELF64LE::Shdr *, StringRef);
-template InputSection::InputSection(elf::ObjectFile<ELF64BE> *,
- const ELF64BE::Shdr *, StringRef);
+template InputSection::InputSection(ObjFile<ELF32LE> *, const ELF32LE::Shdr *,
+ StringRef);
+template InputSection::InputSection(ObjFile<ELF32BE> *, const ELF32BE::Shdr *,
+ StringRef);
+template InputSection::InputSection(ObjFile<ELF64LE> *, const ELF64LE::Shdr *,
+ StringRef);
+template InputSection::InputSection(ObjFile<ELF64BE> *, const ELF64BE::Shdr *,
+ StringRef);
template std::string InputSectionBase::getLocation<ELF32LE>(uint64_t);
template std::string InputSectionBase::getLocation<ELF32BE>(uint64_t);
@@ -986,27 +1009,22 @@ template void InputSection::writeTo<ELF32BE>(uint8_t *);
template void InputSection::writeTo<ELF64LE>(uint8_t *);
template void InputSection::writeTo<ELF64BE>(uint8_t *);
-template elf::ObjectFile<ELF32LE> *InputSectionBase::getFile<ELF32LE>() const;
-template elf::ObjectFile<ELF32BE> *InputSectionBase::getFile<ELF32BE>() const;
-template elf::ObjectFile<ELF64LE> *InputSectionBase::getFile<ELF64LE>() const;
-template elf::ObjectFile<ELF64BE> *InputSectionBase::getFile<ELF64BE>() const;
-
-template MergeInputSection::MergeInputSection(elf::ObjectFile<ELF32LE> *,
+template MergeInputSection::MergeInputSection(ObjFile<ELF32LE> *,
const ELF32LE::Shdr *, StringRef);
-template MergeInputSection::MergeInputSection(elf::ObjectFile<ELF32BE> *,
+template MergeInputSection::MergeInputSection(ObjFile<ELF32BE> *,
const ELF32BE::Shdr *, StringRef);
-template MergeInputSection::MergeInputSection(elf::ObjectFile<ELF64LE> *,
+template MergeInputSection::MergeInputSection(ObjFile<ELF64LE> *,
const ELF64LE::Shdr *, StringRef);
-template MergeInputSection::MergeInputSection(elf::ObjectFile<ELF64BE> *,
+template MergeInputSection::MergeInputSection(ObjFile<ELF64BE> *,
const ELF64BE::Shdr *, StringRef);
-template EhInputSection::EhInputSection(elf::ObjectFile<ELF32LE> *,
+template EhInputSection::EhInputSection(ObjFile<ELF32LE> *,
const ELF32LE::Shdr *, StringRef);
-template EhInputSection::EhInputSection(elf::ObjectFile<ELF32BE> *,
+template EhInputSection::EhInputSection(ObjFile<ELF32BE> *,
const ELF32BE::Shdr *, StringRef);
-template EhInputSection::EhInputSection(elf::ObjectFile<ELF64LE> *,
+template EhInputSection::EhInputSection(ObjFile<ELF64LE> *,
const ELF64LE::Shdr *, StringRef);
-template EhInputSection::EhInputSection(elf::ObjectFile<ELF64BE> *,
+template EhInputSection::EhInputSection(ObjFile<ELF64BE> *,
const ELF64BE::Shdr *, StringRef);
template void EhInputSection::split<ELF32LE>();
diff --git a/ELF/InputSection.h b/ELF/InputSection.h
index d262b5892..51b22599a 100644
--- a/ELF/InputSection.h
+++ b/ELF/InputSection.h
@@ -13,7 +13,7 @@
#include "Config.h"
#include "Relocations.h"
#include "Thunks.h"
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/TinyPtrVector.h"
@@ -32,7 +32,7 @@ class DefinedRegular;
class SyntheticSection;
template <class ELFT> class EhFrameSection;
class MergeSyntheticSection;
-template <class ELFT> class ObjectFile;
+template <class ELFT> class ObjFile;
class OutputSection;
// This is the base class of all sections that lld handles. Some are sections in
@@ -54,12 +54,23 @@ public:
// The garbage collector sets sections' Live bits.
// If GC is disabled, all sections are considered live by default.
- unsigned Live : 1; // for garbage collection
- unsigned Assigned : 1; // for linker script
-
- uint32_t Alignment;
+ unsigned Live : 1;
+
+ // True if this section has already been placed to a linker script
+ // output section. This is needed because, in a linker script, you
+ // can refer to the same section more than once. For example, in
+ // the following linker script,
+ //
+ // .foo : { *(.text) }
+ // .bar : { *(.text) }
+ //
+ // .foo takes all .text sections, and .bar becomes empty. To achieve
+ // this, we need to memorize whether a section has been placed or
+ // not for each input section.
+ unsigned Assigned : 1;
// These corresponds to the fields in Elf_Shdr.
+ uint32_t Alignment;
uint64_t Flags;
uint64_t Entsize;
uint32_t Type;
@@ -91,29 +102,17 @@ protected:
// This corresponds to a section of an input file.
class InputSectionBase : public SectionBase {
public:
- static bool classof(const SectionBase *S);
-
- // The file this section is from.
- InputFile *File;
-
- ArrayRef<uint8_t> Data;
- uint64_t getOffsetInFile() const;
-
- static InputSectionBase Discarded;
-
InputSectionBase()
: SectionBase(Regular, "", /*Flags*/ 0, /*Entsize*/ 0, /*Alignment*/ 0,
/*Type*/ 0,
/*Info*/ 0, /*Link*/ 0),
Repl(this) {
- Live = false;
- Assigned = false;
NumRelocations = 0;
AreRelocsRela = false;
}
template <class ELFT>
- InputSectionBase(ObjectFile<ELFT> *File, const typename ELFT::Shdr *Header,
+ InputSectionBase(ObjFile<ELFT> *File, const typename ELFT::Shdr *Header,
StringRef Name, Kind SectionKind);
InputSectionBase(InputFile *File, uint64_t Flags, uint32_t Type,
@@ -121,6 +120,22 @@ public:
uint32_t Alignment, ArrayRef<uint8_t> Data, StringRef Name,
Kind SectionKind);
+ static bool classof(const SectionBase *S) { return S->kind() != Output; }
+
+ // The file which contains this section. It's dynamic type is always
+ // ObjFile<ELFT>, but in order to avoid ELFT, we use InputFile as
+ // its static type.
+ InputFile *File;
+
+ template <class ELFT> ObjFile<ELFT> *getFile() const {
+ return cast_or_null<ObjFile<ELFT>>(File);
+ }
+
+ ArrayRef<uint8_t> Data;
+ uint64_t getOffsetInFile() const;
+
+ static InputSectionBase Discarded;
+
// Input sections are part of an output section. Special sections
// like .eh_frame and merge sections are first combined into a
// synthetic section that is then added to an output section. In all
@@ -131,12 +146,14 @@ public:
const void *FirstRelocation = nullptr;
unsigned NumRelocations : 31;
unsigned AreRelocsRela : 1;
+
template <class ELFT> ArrayRef<typename ELFT::Rel> rels() const {
assert(!AreRelocsRela);
return llvm::makeArrayRef(
static_cast<const typename ELFT::Rel *>(FirstRelocation),
NumRelocations);
}
+
template <class ELFT> ArrayRef<typename ELFT::Rela> relas() const {
assert(AreRelocsRela);
return llvm::makeArrayRef(
@@ -152,30 +169,32 @@ public:
InputSectionBase *Repl;
// InputSections that are dependent on us (reverse dependency for GC)
- llvm::TinyPtrVector<InputSectionBase *> DependentSections;
+ llvm::TinyPtrVector<InputSection *> DependentSections;
// Returns the size of this section (even if this is a common or BSS.)
size_t getSize() const;
- template <class ELFT> ObjectFile<ELFT> *getFile() const;
-
- template <class ELFT> llvm::object::ELFFile<ELFT> getObj() const {
- return getFile<ELFT>()->getObj();
- }
-
InputSection *getLinkOrderDep() const;
- void uncompress();
+ // Compilers emit zlib-compressed debug sections if the -gz option
+ // is given. This function checks if this section is compressed, and
+ // if so, decompress in memory.
+ void maybeUncompress();
// Returns a source location string. Used to construct an error message.
template <class ELFT> std::string getLocation(uint64_t Offset);
template <class ELFT> std::string getSrcMsg(uint64_t Offset);
template <class ELFT> std::string getObjMsg(uint64_t Offset);
+ // Each section knows how to relocate itself. These functions apply
+ // relocations, assuming that Buf points to this section's copy in
+ // the mmap'ed output buffer.
template <class ELFT> void relocate(uint8_t *Buf, uint8_t *BufEnd);
void relocateAlloc(uint8_t *Buf, uint8_t *BufEnd);
- template <class ELFT> void relocateNonAlloc(uint8_t *Buf, uint8_t *BufEnd);
+ // The native ELF reloc data type is not very convenient to handle.
+ // So we convert ELF reloc records to our own records in Relocations.cpp.
+ // This vector contains such "cooked" relocations.
std::vector<Relocation> Relocations;
template <typename T> llvm::ArrayRef<T> getDataAs() const {
@@ -183,6 +202,11 @@ public:
assert(S % sizeof(T) == 0);
return llvm::makeArrayRef<T>((const T *)Data.data(), S / sizeof(T));
}
+
+private:
+ // A pointer that owns uncompressed data if a section is compressed by zlib.
+ // Since the feature is not used often, this is usually a nullptr.
+ std::unique_ptr<char[]> UncompressBuf;
};
// SectionPiece represents a piece of splittable section contents.
@@ -190,12 +214,12 @@ public:
// 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, bool Live = false)
- : InputOff(Off), OutputOff(-1), Live(Live || !Config->GcSections) {}
+ SectionPiece(size_t Off, bool Live)
+ : InputOff(Off), Live(Live || !Config->GcSections), OutputOff(-1) {}
- size_t InputOff;
- ssize_t OutputOff : 8 * sizeof(ssize_t) - 1;
+ size_t InputOff : 8 * sizeof(ssize_t) - 1;
size_t Live : 1;
+ ssize_t OutputOff;
};
static_assert(sizeof(SectionPiece) == 2 * sizeof(size_t),
"SectionPiece is too big");
@@ -204,9 +228,9 @@ static_assert(sizeof(SectionPiece) == 2 * sizeof(size_t),
class MergeInputSection : public InputSectionBase {
public:
template <class ELFT>
- MergeInputSection(ObjectFile<ELFT> *F, const typename ELFT::Shdr *Header,
+ MergeInputSection(ObjFile<ELFT> *F, const typename ELFT::Shdr *Header,
StringRef Name);
- static bool classof(const SectionBase *S);
+ static bool classof(const SectionBase *S) { return S->kind() == Merge; }
void splitIntoPieces();
// Mark the piece at a given offset live. Used by GC.
@@ -256,16 +280,17 @@ private:
llvm::DenseSet<uint64_t> LiveOffsets;
};
-struct EhSectionPiece : public SectionPiece {
- EhSectionPiece(size_t Off, InputSectionBase *ID, uint32_t Size,
+struct EhSectionPiece {
+ EhSectionPiece(size_t Off, InputSectionBase *Sec, uint32_t Size,
unsigned FirstRelocation)
- : SectionPiece(Off, false), ID(ID), Size(Size),
- FirstRelocation(FirstRelocation) {}
- InputSectionBase *ID;
- uint32_t Size;
- uint32_t size() const { return Size; }
+ : InputOff(Off), Sec(Sec), Size(Size), FirstRelocation(FirstRelocation) {}
+
+ ArrayRef<uint8_t> data() { return {Sec->Data.data() + this->InputOff, Size}; }
- ArrayRef<uint8_t> data() { return {ID->Data.data() + this->InputOff, Size}; }
+ size_t InputOff;
+ ssize_t OutputOff = -1;
+ InputSectionBase *Sec;
+ uint32_t Size;
unsigned FirstRelocation;
};
@@ -273,9 +298,9 @@ struct EhSectionPiece : public SectionPiece {
class EhInputSection : public InputSectionBase {
public:
template <class ELFT>
- EhInputSection(ObjectFile<ELFT> *F, const typename ELFT::Shdr *Header,
+ EhInputSection(ObjFile<ELFT> *F, const typename ELFT::Shdr *Header,
StringRef Name);
- static bool classof(const SectionBase *S);
+ static bool classof(const SectionBase *S) { return S->kind() == EHFrame; }
template <class ELFT> void split();
template <class ELFT, class RelTy> void split(ArrayRef<RelTy> Rels);
@@ -295,7 +320,7 @@ public:
InputSection(uint64_t Flags, uint32_t Type, uint32_t Alignment,
ArrayRef<uint8_t> Data, StringRef Name, Kind K = Regular);
template <class ELFT>
- InputSection(ObjectFile<ELFT> *F, const typename ELFT::Shdr *Header,
+ InputSection(ObjFile<ELFT> *F, const typename ELFT::Shdr *Header,
StringRef Name);
// Write this section to a mmap'ed file, assuming Buf is pointing to
@@ -331,6 +356,9 @@ private:
// The list of all input sections.
extern std::vector<InputSectionBase *> InputSections;
+// Builds section order for handling --symbol-ordering-file.
+llvm::DenseMap<SectionBase *, int> buildSectionOrder();
+
} // namespace elf
std::string toString(const elf::InputSectionBase *);
diff --git a/ELF/LTO.cpp b/ELF/LTO.cpp
index 3a536271d..34eb98185 100644
--- a/ELF/LTO.cpp
+++ b/ELF/LTO.cpp
@@ -11,8 +11,10 @@
#include "Config.h"
#include "Error.h"
#include "InputFiles.h"
+#include "LinkerScript.h"
+#include "SymbolTable.h"
#include "Symbols.h"
-#include "lld/Core/TargetOptionsCommandFlags.h"
+#include "lld/Common/TargetOptionsCommandFlags.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
@@ -60,10 +62,8 @@ static void diagnosticHandler(const DiagnosticInfo &DI) {
}
static void checkError(Error E) {
- handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) -> Error {
- error(EIB.message());
- return Error::success();
- });
+ handleAllErrors(std::move(E),
+ [&](ErrorInfoBase &EIB) { error(EIB.message()); });
}
static std::unique_ptr<lto::LTO> createLTO() {
@@ -73,6 +73,10 @@ static std::unique_ptr<lto::LTO> createLTO() {
Conf.Options = InitTargetOptionsFromCodeGenFlags();
Conf.Options.RelaxELFRelocations = true;
+ // Always emit a section per function/datum with LTO.
+ Conf.Options.FunctionSections = true;
+ Conf.Options.DataSections = true;
+
if (Config->Relocatable)
Conf.RelocModel = None;
else if (Config->Pic)
@@ -103,40 +107,65 @@ static std::unique_ptr<lto::LTO> createLTO() {
Config->LTOPartitions);
}
-BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {}
+BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {
+ for (Symbol *Sym : Symtab->getSymbols()) {
+ StringRef Name = Sym->body()->getName();
+ for (StringRef Prefix : {"__start_", "__stop_"})
+ if (Name.startswith(Prefix))
+ UsedStartStop.insert(Name.substr(Prefix.size()));
+ }
+}
BitcodeCompiler::~BitcodeCompiler() = default;
static void undefine(Symbol *S) {
- replaceBody<Undefined>(S, S->body()->getName(), /*IsLocal=*/false,
- STV_DEFAULT, S->body()->Type, nullptr);
+ replaceBody<Undefined>(S, nullptr, S->body()->getName(), /*IsLocal=*/false,
+ STV_DEFAULT, S->body()->Type);
}
void BitcodeCompiler::add(BitcodeFile &F) {
lto::InputFile &Obj = *F.Obj;
unsigned SymNum = 0;
- std::vector<Symbol *> Syms = F.getSymbols();
+ std::vector<SymbolBody *> Syms = F.getSymbols();
std::vector<lto::SymbolResolution> Resols(Syms.size());
+ DenseSet<StringRef> ScriptSymbols;
+ for (BaseCommand *Base : Script->SectionCommands)
+ if (auto *Cmd = dyn_cast<SymbolAssignment>(Base))
+ ScriptSymbols.insert(Cmd->Name);
+
// Provide a resolution to the LTO API for each symbol.
for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
- Symbol *Sym = Syms[SymNum];
+ SymbolBody *B = Syms[SymNum];
+ Symbol *Sym = B->symbol();
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.isUndefined() && B->File == &F;
-
- R.VisibleToRegularObj =
- Sym->IsUsedInRegularObj || (R.Prevailing && Sym->includeInDynsym());
+ R.Prevailing = !ObjSym.isUndefined() && B->getFile() == &F;
+
+ // We ask LTO to preserve following global symbols:
+ // 1) All symbols when doing relocatable link, so that them can be used
+ // for doing final link.
+ // 2) Symbols that are used in regular objects.
+ // 3) C named sections if we have corresponding __start_/__stop_ symbol.
+ // 4) Symbols that are defined in bitcode files and used for dynamic linking.
+ R.VisibleToRegularObj = Config->Relocatable || Sym->IsUsedInRegularObj ||
+ (R.Prevailing && Sym->includeInDynsym()) ||
+ UsedStartStop.count(ObjSym.getSectionName());
if (R.Prevailing)
undefine(Sym);
- R.LinkerRedefined = Config->RenamedSymbols.count(Sym);
+
+ // We tell LTO to not apply interprocedural optimization for following
+ // symbols because otherwise LTO would inline them while their values are
+ // still not final:
+ // 1) Aliased (with --defsym) or wrapped (with --wrap) symbols.
+ // 2) Symbols redefined in linker script.
+ R.LinkerRedefined = !Sym->CanInline || ScriptSymbols.count(B->getName());
}
checkError(LTOObj->add(std::move(F.Obj), Resols));
}
@@ -156,9 +185,8 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
if (!Config->ThinLTOCacheDir.empty())
Cache = check(
lto::localCache(Config->ThinLTOCacheDir,
- [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
- Files[Task] = std::move(MB);
- }));
+ [&](size_t Task, std::unique_ptr<MemoryBuffer> MB,
+ StringRef Path) { Files[Task] = std::move(MB); }));
checkError(LTOObj->run(
[&](size_t Task) {
diff --git a/ELF/LTO.h b/ELF/LTO.h
index 28afa0e83..223af507a 100644
--- a/ELF/LTO.h
+++ b/ELF/LTO.h
@@ -21,7 +21,8 @@
#ifndef LLD_ELF_LTO_H
#define LLD_ELF_LTO_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallString.h"
#include <memory>
#include <vector>
@@ -30,7 +31,7 @@ namespace llvm {
namespace lto {
class LTO;
}
-}
+} // namespace llvm
namespace lld {
namespace elf {
@@ -50,8 +51,9 @@ private:
std::unique_ptr<llvm::lto::LTO> LTOObj;
std::vector<SmallString<0>> Buff;
std::vector<std::unique_ptr<MemoryBuffer>> Files;
+ llvm::DenseSet<StringRef> UsedStartStop;
};
-}
-}
+} // namespace elf
+} // namespace lld
#endif
diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp
index 8c20bf604..3149c98e3 100644
--- a/ELF/LinkerScript.cpp
+++ b/ELF/LinkerScript.cpp
@@ -21,13 +21,12 @@
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
-#include "Threads.h"
#include "Writer.h"
+#include "lld/Common/Threads.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Support/Casting.h"
-#include "llvm/Support/Compression.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
@@ -50,83 +49,114 @@ using namespace lld::elf;
LinkerScript *elf::Script;
+static uint64_t getOutputSectionVA(SectionBase *InputSec, StringRef Loc) {
+ if (OutputSection *OS = InputSec->getOutputSection())
+ return OS->Addr;
+ error(Loc + ": unable to evaluate expression: input section " +
+ InputSec->Name + " has no output section assigned");
+ return 0;
+}
+
uint64_t ExprValue::getValue() const {
- if (Sec) {
- if (OutputSection *OS = Sec->getOutputSection())
- return alignTo(Sec->getOffset(Val) + OS->Addr, Alignment);
- error(Loc + ": unable to evaluate expression: input section " + Sec->Name +
- " has no output section assigned");
- }
+ if (Sec)
+ return alignTo(Sec->getOffset(Val) + getOutputSectionVA(Sec, Loc),
+ Alignment);
return alignTo(Val, Alignment);
}
uint64_t ExprValue::getSecAddr() const {
if (Sec)
- return Sec->getOffset(0) + Sec->getOutputSection()->Addr;
+ return Sec->getOffset(0) + getOutputSectionVA(Sec, Loc);
return 0;
}
-template <class ELFT> static SymbolBody *addRegular(SymbolAssignment *Cmd) {
- Symbol *Sym;
- uint8_t Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT;
- std::tie(Sym, std::ignore) = Symtab<ELFT>::X->insert(
- Cmd->Name, /*Type*/ 0, Visibility, /*CanOmitFromDynSym*/ false,
- /*File*/ nullptr);
- Sym->Binding = STB_GLOBAL;
- ExprValue Value = Cmd->Expression();
- SectionBase *Sec = Value.isAbsolute() ? nullptr : Value.Sec;
-
- // We want to set symbol values early if we can. This allows us to use symbols
- // as variables in linker scripts. Doing so allows us to write expressions
- // like this: `alignment = 16; . = ALIGN(., alignment)`
- uint64_t SymValue = Value.isAbsolute() ? Value.getValue() : 0;
- replaceBody<DefinedRegular>(Sym, Cmd->Name, /*IsLocal=*/false, Visibility,
- STT_NOTYPE, SymValue, 0, Sec, nullptr);
- return Sym->body();
+uint64_t ExprValue::getSectionOffset() const {
+ // If the alignment is trivial, we don't have to compute the full
+ // value to know the offset. This allows this function to succeed in
+ // cases where the output section is not yet known.
+ if (Alignment == 1)
+ return Val;
+ return getValue() - getSecAddr();
}
-OutputSectionCommand *
-LinkerScript::createOutputSectionCommand(StringRef Name, StringRef Location) {
- OutputSectionCommand *&CmdRef = NameToOutputSectionCommand[Name];
- OutputSectionCommand *Cmd;
- if (CmdRef && CmdRef->Location.empty()) {
+OutputSection *LinkerScript::createOutputSection(StringRef Name,
+ StringRef Location) {
+ OutputSection *&SecRef = NameToOutputSection[Name];
+ OutputSection *Sec;
+ if (SecRef && SecRef->Location.empty()) {
// There was a forward reference.
- Cmd = CmdRef;
+ Sec = SecRef;
} else {
- Cmd = make<OutputSectionCommand>(Name);
- if (!CmdRef)
- CmdRef = Cmd;
+ Sec = make<OutputSection>(Name, SHT_PROGBITS, 0);
+ if (!SecRef)
+ SecRef = Sec;
}
- Cmd->Location = Location;
- return Cmd;
+ Sec->Location = Location;
+ return Sec;
}
-OutputSectionCommand *
-LinkerScript::getOrCreateOutputSectionCommand(StringRef Name) {
- OutputSectionCommand *&CmdRef = NameToOutputSectionCommand[Name];
+OutputSection *LinkerScript::getOrCreateOutputSection(StringRef Name) {
+ OutputSection *&CmdRef = NameToOutputSection[Name];
if (!CmdRef)
- CmdRef = make<OutputSectionCommand>(Name);
+ CmdRef = make<OutputSection>(Name, SHT_PROGBITS, 0);
return CmdRef;
}
void LinkerScript::setDot(Expr E, const Twine &Loc, bool InSec) {
uint64_t Val = E().getValue();
- if (Val < Dot) {
- if (InSec)
- error(Loc + ": unable to move location counter backward for: " +
- CurOutSec->Name);
- else
- error(Loc + ": unable to move location counter backward");
- }
+ if (Val < Dot && InSec)
+ error(Loc + ": unable to move location counter backward for: " +
+ Ctx->OutSec->Name);
Dot = Val;
+
// Update to location counter means update to section size.
if (InSec)
- CurOutSec->Size = Dot - CurOutSec->Addr;
+ Ctx->OutSec->Size = Dot - Ctx->OutSec->Addr;
}
-// Sets value of a 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.
+// This function is called from processSectionCommands,
+// while we are fixing the output section layout.
+void LinkerScript::addSymbol(SymbolAssignment *Cmd) {
+ if (Cmd->Name == ".")
+ return;
+
+ // If a symbol was in PROVIDE(), we need to define it only when
+ // it is a referenced undefined symbol.
+ SymbolBody *B = Symtab->find(Cmd->Name);
+ if (Cmd->Provide && (!B || B->isDefined()))
+ return;
+
+ // Define a symbol.
+ Symbol *Sym;
+ uint8_t Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT;
+ std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, /*Type*/ 0, Visibility,
+ /*CanOmitFromDynSym*/ false,
+ /*File*/ nullptr);
+ Sym->Binding = STB_GLOBAL;
+ ExprValue Value = Cmd->Expression();
+ SectionBase *Sec = Value.isAbsolute() ? nullptr : Value.Sec;
+
+ // When this function is called, section addresses have not been
+ // fixed yet. So, we may or may not know the value of the RHS
+ // expression.
+ //
+ // For example, if an expression is `x = 42`, we know x is always 42.
+ // However, if an expression is `x = .`, there's no way to know its
+ // value at the moment.
+ //
+ // We want to set symbol values early if we can. This allows us to
+ // use symbols as variables in linker scripts. Doing so allows us to
+ // write expressions like this: `alignment = 16; . = ALIGN(., alignment)`.
+ uint64_t SymValue = Value.Sec ? 0 : Value.getValue();
+
+ replaceBody<DefinedRegular>(Sym, nullptr, Cmd->Name, /*IsLocal=*/false,
+ Visibility, STT_NOTYPE, SymValue, 0, Sec);
+ Cmd->Sym = cast<DefinedRegular>(Sym->body());
+}
+
+// This function is called from assignAddresses, while we are
+// fixing the output section addresses. This function is supposed
+// to set the final value for a given symbol assignment.
void LinkerScript::assignSymbol(SymbolAssignment *Cmd, bool InSec) {
if (Cmd->Name == ".") {
setDot(Cmd->Expression, Cmd->Location, InSec);
@@ -136,97 +166,28 @@ void LinkerScript::assignSymbol(SymbolAssignment *Cmd, bool InSec) {
if (!Cmd->Sym)
return;
- auto *Sym = cast<DefinedRegular>(Cmd->Sym);
ExprValue V = Cmd->Expression();
if (V.isAbsolute()) {
- Sym->Value = V.getValue();
+ Cmd->Sym->Section = nullptr;
+ Cmd->Sym->Value = V.getValue();
} else {
- Sym->Section = V.Sec;
- Sym->Value = alignTo(V.Val, V.Alignment);
+ Cmd->Sym->Section = V.Sec;
+ Cmd->Sym->Value = V.getSectionOffset();
}
}
-static SymbolBody *findSymbol(StringRef S) {
- switch (Config->EKind) {
- case ELF32LEKind:
- return Symtab<ELF32LE>::X->find(S);
- case ELF32BEKind:
- return Symtab<ELF32BE>::X->find(S);
- case ELF64LEKind:
- return Symtab<ELF64LE>::X->find(S);
- case ELF64BEKind:
- return Symtab<ELF64BE>::X->find(S);
- default:
- llvm_unreachable("unknown Config->EKind");
- }
-}
-
-static SymbolBody *addRegularSymbol(SymbolAssignment *Cmd) {
- switch (Config->EKind) {
- case ELF32LEKind:
- return addRegular<ELF32LE>(Cmd);
- case ELF32BEKind:
- return addRegular<ELF32BE>(Cmd);
- case ELF64LEKind:
- return addRegular<ELF64LE>(Cmd);
- case ELF64BEKind:
- return addRegular<ELF64BE>(Cmd);
- default:
- llvm_unreachable("unknown Config->EKind");
- }
-}
-
-void LinkerScript::addSymbol(SymbolAssignment *Cmd) {
- if (Cmd->Name == ".")
- return;
-
- // If a symbol was in PROVIDE(), we need to define it only when
- // it is a referenced undefined symbol.
- SymbolBody *B = findSymbol(Cmd->Name);
- if (Cmd->Provide && (!B || B->isDefined()))
- return;
-
- Cmd->Sym = addRegularSymbol(Cmd);
-}
-
-bool SymbolAssignment::classof(const BaseCommand *C) {
- return C->Kind == AssignmentKind;
-}
-
-bool OutputSectionCommand::classof(const BaseCommand *C) {
- return C->Kind == OutputSectionKind;
-}
-
-// Fill [Buf, Buf + Size) with Filler.
-// This is used for linker script "=fillexp" command.
-static void fill(uint8_t *Buf, size_t Size, uint32_t Filler) {
- size_t I = 0;
- for (; I + 4 < Size; I += 4)
- memcpy(Buf + I, &Filler, 4);
- memcpy(Buf + I, &Filler, Size - I);
-}
-
-bool InputSectionDescription::classof(const BaseCommand *C) {
- return C->Kind == InputSectionKind;
-}
-
-bool AssertCommand::classof(const BaseCommand *C) {
- return C->Kind == AssertKind;
-}
-
-bool BytesDataCommand::classof(const BaseCommand *C) {
- return C->Kind == BytesDataKind;
-}
-
-static StringRef basename(InputSectionBase *S) {
- if (S->File)
- return sys::path::filename(S->File->getName());
- return "";
+static std::string getFilename(InputFile *File) {
+ if (!File)
+ return "";
+ if (File->ArchiveName.empty())
+ return File->getName();
+ return (File->ArchiveName + "(" + File->getName() + ")").str();
}
bool LinkerScript::shouldKeep(InputSectionBase *S) {
- for (InputSectionDescription *ID : Opt.KeptSections)
- if (ID->FilePat.match(basename(S)))
+ std::string Filename = getFilename(S->File);
+ for (InputSectionDescription *ID : KeptSections)
+ if (ID->FilePat.match(Filename))
for (SectionPattern &P : ID->SectionPatterns)
if (P.SectionPat.match(S->Name))
return true;
@@ -258,29 +219,64 @@ getComparator(SortSectionPolicy K) {
}
// A helper function for the SORT() command.
-static bool matchConstraints(ArrayRef<InputSectionBase *> Sections,
+static bool matchConstraints(ArrayRef<InputSection *> Sections,
ConstraintKind Kind) {
if (Kind == ConstraintKind::NoConstraint)
return true;
- bool IsRW = llvm::any_of(Sections, [](InputSectionBase *Sec) {
- return static_cast<InputSectionBase *>(Sec)->Flags & SHF_WRITE;
- });
+ bool IsRW = llvm::any_of(
+ Sections, [](InputSection *Sec) { return Sec->Flags & SHF_WRITE; });
return (IsRW && Kind == ConstraintKind::ReadWrite) ||
(!IsRW && Kind == ConstraintKind::ReadOnly);
}
-static void sortSections(InputSection **Begin, InputSection **End,
+static void sortSections(MutableArrayRef<InputSection *> Vec,
SortSectionPolicy K) {
if (K != SortSectionPolicy::Default && K != SortSectionPolicy::None)
- std::stable_sort(Begin, End, getComparator(K));
+ std::stable_sort(Vec.begin(), Vec.end(), getComparator(K));
+}
+
+// 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.
+// 5. If no SORT commands are given and --sort-section is not specified,
+// apply sorting provided by --symbol-ordering-file if any exist.
+static void sortInputSections(
+ MutableArrayRef<InputSection *> Vec, const SectionPattern &Pat,
+ const DenseMap<SectionBase *, int> &Order) {
+ if (Pat.SortOuter == SortSectionPolicy::None)
+ return;
+
+ if (Pat.SortOuter == SortSectionPolicy::Default &&
+ Config->SortSection == SortSectionPolicy::Default) {
+ // If -symbol-ordering-file was given, sort accordingly.
+ // Usually, Order is empty.
+ if (!Order.empty())
+ sortByOrder(Vec, [&](InputSectionBase *S) { return Order.lookup(S); });
+ return;
+ }
+
+ if (Pat.SortInner == SortSectionPolicy::Default)
+ sortSections(Vec, Config->SortSection);
+ else
+ sortSections(Vec, Pat.SortInner);
+ sortSections(Vec, Pat.SortOuter);
}
// Compute and remember which sections the InputSectionDescription matches.
std::vector<InputSection *>
LinkerScript::computeInputSections(const InputSectionDescription *Cmd) {
std::vector<InputSection *> Ret;
+ DenseMap<SectionBase *, int> Order = buildSectionOrder();
// Collects all sections that satisfy constraints of Cmd.
for (const SectionPattern &Pat : Cmd->SectionPatterns) {
@@ -301,42 +297,27 @@ LinkerScript::computeInputSections(const InputSectionDescription *Cmd) {
if (Sec->Type == SHT_REL || Sec->Type == SHT_RELA)
continue;
- StringRef Filename = basename(Sec);
+ std::string Filename = getFilename(Sec->File);
if (!Cmd->FilePat.match(Filename) ||
Pat.ExcludedFilePat.match(Filename) ||
!Pat.SectionPat.match(Sec->Name))
continue;
+ // It is safe to assume that Sec is an InputSection
+ // because mergeable or EH input sections have already been
+ // handled and eliminated.
Ret.push_back(cast<InputSection>(Sec));
Sec->Assigned = true;
}
- // 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.
- InputSection **Begin = Ret.data() + SizeBefore;
- InputSection **End = Ret.data() + Ret.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);
- }
+ sortInputSections(MutableArrayRef<InputSection *>(Ret).slice(SizeBefore),
+ Pat, Order);
}
return Ret;
}
-void LinkerScript::discard(ArrayRef<InputSectionBase *> V) {
- for (InputSectionBase *S : V) {
+void LinkerScript::discard(ArrayRef<InputSection *> V) {
+ for (InputSection *S : V) {
S->Live = false;
if (S == InX::ShStrTab || S == InX::Dynamic || S == InX::DynSymTab ||
S == InX::DynStrTab)
@@ -345,23 +326,20 @@ void LinkerScript::discard(ArrayRef<InputSectionBase *> V) {
}
}
-std::vector<InputSectionBase *>
-LinkerScript::createInputSectionList(OutputSectionCommand &OutCmd) {
- std::vector<InputSectionBase *> Ret;
-
- for (BaseCommand *Base : OutCmd.Commands) {
- auto *Cmd = dyn_cast<InputSectionDescription>(Base);
- if (!Cmd)
- continue;
+std::vector<InputSection *>
+LinkerScript::createInputSectionList(OutputSection &OutCmd) {
+ std::vector<InputSection *> Ret;
- Cmd->Sections = computeInputSections(Cmd);
- Ret.insert(Ret.end(), Cmd->Sections.begin(), Cmd->Sections.end());
+ for (BaseCommand *Base : OutCmd.SectionCommands) {
+ if (auto *Cmd = dyn_cast<InputSectionDescription>(Base)) {
+ Cmd->Sections = computeInputSections(Cmd);
+ Ret.insert(Ret.end(), Cmd->Sections.begin(), Cmd->Sections.end());
+ }
}
-
return Ret;
}
-void LinkerScript::processCommands(OutputSectionFactory &Factory) {
+void LinkerScript::processSectionCommands(OutputSectionFactory &Factory) {
// A symbol can be assigned before any section is mentioned in the linker
// script. In an DSO, the symbol values are addresses, so the only important
// section values are:
@@ -373,22 +351,28 @@ void LinkerScript::processCommands(OutputSectionFactory &Factory) {
// which will map to whatever the first actual section is.
Aether = make<OutputSection>("", 0, SHF_ALLOC);
Aether->SectionIndex = 1;
- CurOutSec = Aether;
- Dot = 0;
- for (size_t I = 0; I < Opt.Commands.size(); ++I) {
+ // Ctx captures the local AddressState and makes it accessible deliberately.
+ // This is needed as there are some cases where we cannot just
+ // thread the current state through to a lambda function created by the
+ // script parser.
+ Ctx = make_unique<AddressState>();
+ Ctx->OutSec = Aether;
+
+ // Add input sections to output sections.
+ for (size_t I = 0; I < SectionCommands.size(); ++I) {
// Handle symbol assignments outside of any output section.
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Opt.Commands[I])) {
+ if (auto *Cmd = dyn_cast<SymbolAssignment>(SectionCommands[I])) {
addSymbol(Cmd);
continue;
}
- if (auto *Cmd = dyn_cast<OutputSectionCommand>(Opt.Commands[I])) {
- std::vector<InputSectionBase *> V = createInputSectionList(*Cmd);
+ if (auto *Sec = dyn_cast<OutputSection>(SectionCommands[I])) {
+ std::vector<InputSection *> V = createInputSectionList(*Sec);
// The output section name `/DISCARD/' is special.
// Any input section assigned to it is discarded.
- if (Cmd->Name == "/DISCARD/") {
+ if (Sec->Name == "/DISCARD/") {
discard(V);
continue;
}
@@ -398,240 +382,181 @@ void LinkerScript::processCommands(OutputSectionFactory &Factory) {
// 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
+ // Because we'll iterate over SectionCommands many more times, the easiest
// way to "make it as if it wasn't present" is to just remove it.
- if (!matchConstraints(V, Cmd->Constraint)) {
+ if (!matchConstraints(V, Sec->Constraint)) {
for (InputSectionBase *S : V)
S->Assigned = false;
- Opt.Commands.erase(Opt.Commands.begin() + I);
+ SectionCommands.erase(SectionCommands.begin() + I);
--I;
continue;
}
// A directive may contain symbol definitions like this:
// ".foo : { ...; bar = .; }". Handle them.
- for (BaseCommand *Base : Cmd->Commands)
+ for (BaseCommand *Base : Sec->SectionCommands)
if (auto *OutCmd = dyn_cast<SymbolAssignment>(Base))
addSymbol(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().getValue();
+ if (Sec->SubalignExpr) {
+ uint32_t Subalign = Sec->SubalignExpr().getValue();
for (InputSectionBase *S : V)
S->Alignment = Subalign;
}
// Add input sections to an output section.
- for (InputSectionBase *S : V)
- Factory.addInputSec(S, Cmd->Name, Cmd->Sec);
- if (OutputSection *Sec = Cmd->Sec) {
- assert(Sec->SectionIndex == INT_MAX);
- Sec->SectionIndex = I;
- if (Cmd->Noload)
- Sec->Type = SHT_NOBITS;
- SecToCommand[Sec] = Cmd;
- }
+ for (InputSection *S : V)
+ Sec->addSection(S);
}
}
- CurOutSec = nullptr;
+ Ctx = nullptr;
+
+ // Output sections are emitted in the exact same order as
+ // appeared in SECTIONS command, so we know their section indices.
+ for (size_t I = 0; I < SectionCommands.size(); ++I) {
+ auto *Sec = dyn_cast<OutputSection>(SectionCommands[I]);
+ if (!Sec)
+ continue;
+ assert(Sec->SectionIndex == INT_MAX);
+ Sec->SectionIndex = I;
+ if (Sec->Noload)
+ Sec->Type = SHT_NOBITS;
+ }
}
+// If no SECTIONS command was given, we create simple SectionCommands
+// as if a minimum SECTIONS command were given. This function does that.
void LinkerScript::fabricateDefaultCommands() {
- std::vector<BaseCommand *> Commands;
-
// Define start address
- uint64_t StartAddr = -1;
+ uint64_t StartAddr = UINT64_MAX;
// The Sections with -T<section> have been sorted in order of ascending
// address. We must lower StartAddr if the lowest -T<section address> as
// calls to setDot() must be monotonically increasing.
- for (auto& KV : Config->SectionStartMap)
+ for (auto &KV : Config->SectionStartMap)
StartAddr = std::min(StartAddr, KV.second);
- Commands.push_back(make<SymbolAssignment>(
- ".",
- [=] {
- return std::min(StartAddr, Config->ImageBase + elf::getHeaderSize());
- },
- ""));
+ auto Expr = [=] {
+ return std::min(StartAddr, Target->getImageBase() + elf::getHeaderSize());
+ };
+ SectionCommands.insert(SectionCommands.begin(),
+ make<SymbolAssignment>(".", Expr, ""));
+}
- // For each OutputSection that needs a VA fabricate an OutputSectionCommand
- // with an InputSectionDescription describing the InputSections
- for (OutputSection *Sec : OutputSections) {
- auto *OSCmd = createOutputSectionCommand(Sec->Name, "<internal>");
- OSCmd->Sec = Sec;
- SecToCommand[Sec] = OSCmd;
-
- // Prefer user supplied address over additional alignment constraint
- auto I = Config->SectionStartMap.find(Sec->Name);
- if (I != Config->SectionStartMap.end())
- OSCmd->AddrExpr = [=] { return I->second; };
-
- Commands.push_back(OSCmd);
- if (Sec->Sections.size()) {
- auto *ISD = make<InputSectionDescription>("");
- OSCmd->Commands.push_back(ISD);
- for (InputSection *ISec : Sec->Sections) {
- ISD->Sections.push_back(ISec);
- ISec->Assigned = true;
- }
- }
- }
- // SECTIONS commands run before other non SECTIONS commands
- Commands.insert(Commands.end(), Opt.Commands.begin(), Opt.Commands.end());
- Opt.Commands = std::move(Commands);
+static OutputSection *findByName(ArrayRef<BaseCommand *> Vec,
+ StringRef Name) {
+ for (BaseCommand *Base : Vec)
+ if (auto *Sec = dyn_cast<OutputSection>(Base))
+ if (Sec->Name == Name)
+ return Sec;
+ return nullptr;
}
// Add sections that didn't match any sections command.
void LinkerScript::addOrphanSections(OutputSectionFactory &Factory) {
+ unsigned End = SectionCommands.size();
+
for (InputSectionBase *S : InputSections) {
if (!S->Live || S->Parent)
continue;
+
StringRef Name = getOutputSectionName(S->Name);
- auto I = std::find_if(
- Opt.Commands.begin(), Opt.Commands.end(), [&](BaseCommand *Base) {
- if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base))
- return Cmd->Name == Name;
- return false;
- });
- if (I == Opt.Commands.end()) {
- Factory.addInputSec(S, Name);
- } else {
- auto *Cmd = cast<OutputSectionCommand>(*I);
- Factory.addInputSec(S, Name, Cmd->Sec);
- if (OutputSection *Sec = Cmd->Sec) {
- SecToCommand[Sec] = Cmd;
- unsigned Index = std::distance(Opt.Commands.begin(), I);
- assert(Sec->SectionIndex == INT_MAX || Sec->SectionIndex == Index);
- Sec->SectionIndex = Index;
- }
- auto *ISD = make<InputSectionDescription>("");
- ISD->Sections.push_back(cast<InputSection>(S));
- Cmd->Commands.push_back(ISD);
+ log(toString(S) + " is being placed in '" + Name + "'");
+
+ if (OutputSection *Sec =
+ findByName(makeArrayRef(SectionCommands).slice(0, End), Name)) {
+ Sec->addSection(cast<InputSection>(S));
+ continue;
}
+
+ if (OutputSection *OS = Factory.addInputSec(S, Name))
+ SectionCommands.push_back(OS);
+ assert(S->getOutputSection()->SectionIndex == INT_MAX);
}
}
-uint64_t LinkerScript::advance(uint64_t Size, unsigned Align) {
- bool IsTbss = (CurOutSec->Flags & SHF_TLS) && CurOutSec->Type == SHT_NOBITS;
- uint64_t Start = IsTbss ? Dot + ThreadBssOffset : Dot;
- Start = alignTo(Start, Align);
+uint64_t LinkerScript::advance(uint64_t Size, unsigned Alignment) {
+ bool IsTbss =
+ (Ctx->OutSec->Flags & SHF_TLS) && Ctx->OutSec->Type == SHT_NOBITS;
+ uint64_t Start = IsTbss ? Dot + Ctx->ThreadBssOffset : Dot;
+ Start = alignTo(Start, Alignment);
uint64_t End = Start + Size;
if (IsTbss)
- ThreadBssOffset = End - Dot;
+ Ctx->ThreadBssOffset = End - Dot;
else
Dot = End;
return End;
}
void LinkerScript::output(InputSection *S) {
+ uint64_t Before = advance(0, 1);
uint64_t Pos = advance(S->getSize(), S->Alignment);
- S->OutSecOff = Pos - S->getSize() - CurOutSec->Addr;
+ S->OutSecOff = Pos - S->getSize() - Ctx->OutSec->Addr;
// 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;
+ Ctx->OutSec->Size = Pos - Ctx->OutSec->Addr;
// If there is a memory region associated with this input section, then
// place the section in that region and update the region index.
- if (CurMemRegion) {
- CurMemRegion->Offset += CurOutSec->Size;
- uint64_t CurSize = CurMemRegion->Offset - CurMemRegion->Origin;
- if (CurSize > CurMemRegion->Length) {
- uint64_t OverflowAmt = CurSize - CurMemRegion->Length;
- error("section '" + CurOutSec->Name + "' will not fit in region '" +
- CurMemRegion->Name + "': overflowed by " + Twine(OverflowAmt) +
+ if (Ctx->MemRegion) {
+ uint64_t &CurOffset = Ctx->MemRegionOffset[Ctx->MemRegion];
+ CurOffset += Pos - Before;
+ uint64_t CurSize = CurOffset - Ctx->MemRegion->Origin;
+ if (CurSize > Ctx->MemRegion->Length) {
+ uint64_t OverflowAmt = CurSize - Ctx->MemRegion->Length;
+ error("section '" + Ctx->OutSec->Name + "' will not fit in region '" +
+ Ctx->MemRegion->Name + "': overflowed by " + Twine(OverflowAmt) +
" bytes");
}
}
}
void LinkerScript::switchTo(OutputSection *Sec) {
- if (CurOutSec == Sec)
+ if (Ctx->OutSec == Sec)
return;
- CurOutSec = Sec;
- CurOutSec->Addr = advance(0, CurOutSec->Alignment);
+ Ctx->OutSec = Sec;
+ Ctx->OutSec->Addr = advance(0, Ctx->OutSec->Alignment);
// If neither AT nor AT> is specified for an allocatable section, the linker
// will set the LMA such that the difference between VMA and LMA for the
// section is the same as the preceding output section in the same region
// https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html
- if (LMAOffset)
- CurOutSec->LMAOffset = LMAOffset();
-}
-
-void LinkerScript::process(BaseCommand &Base) {
- // This handles the assignments to symbol or to the dot.
- if (auto *Cmd = dyn_cast<SymbolAssignment>(&Base)) {
- assignSymbol(Cmd, true);
- return;
- }
-
- // Handle BYTE(), SHORT(), LONG(), or QUAD().
- if (auto *Cmd = dyn_cast<BytesDataCommand>(&Base)) {
- Cmd->Offset = Dot - CurOutSec->Addr;
- Dot += Cmd->Size;
- CurOutSec->Size = Dot - CurOutSec->Addr;
- return;
- }
-
- // Handle ASSERT().
- if (auto *Cmd = dyn_cast<AssertCommand>(&Base)) {
- Cmd->Expression();
- return;
- }
-
- // Handle a single input section description command.
- // It calculates and assigns the offsets for each section and also
- // updates the output section size.
- auto &Cmd = cast<InputSectionDescription>(Base);
- for (InputSection *Sec : Cmd.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 *S = dyn_cast<SyntheticSection>(Sec))
- if (S->empty())
- continue;
-
- if (!Sec->Live)
- continue;
- assert(CurOutSec == Sec->getParent());
- output(Sec);
- }
+ if (Ctx->LMAOffset)
+ Ctx->OutSec->LMAOffset = Ctx->LMAOffset();
}
// This function searches for a memory region to place the given output
// section in. If found, a pointer to the appropriate memory region is
// returned. Otherwise, a nullptr is returned.
-MemoryRegion *LinkerScript::findMemoryRegion(OutputSectionCommand *Cmd) {
+MemoryRegion *LinkerScript::findMemoryRegion(OutputSection *Sec) {
// If a memory region name was specified in the output section command,
// then try to find that region first.
- if (!Cmd->MemoryRegionName.empty()) {
- auto It = Opt.MemoryRegions.find(Cmd->MemoryRegionName);
- if (It != Opt.MemoryRegions.end())
- return &It->second;
- error("memory region '" + Cmd->MemoryRegionName + "' not declared");
+ if (!Sec->MemoryRegionName.empty()) {
+ auto It = MemoryRegions.find(Sec->MemoryRegionName);
+ if (It != MemoryRegions.end())
+ return It->second;
+ error("memory region '" + Sec->MemoryRegionName + "' not declared");
return nullptr;
}
// If at least one memory region is defined, all sections must
// belong to some memory region. Otherwise, we don't need to do
// anything for memory regions.
- if (Opt.MemoryRegions.empty())
+ if (MemoryRegions.empty())
return nullptr;
- OutputSection *Sec = Cmd->Sec;
// See if a region can be found by matching section flags.
- for (auto &Pair : Opt.MemoryRegions) {
- MemoryRegion &M = Pair.second;
- if ((M.Flags & Sec->Flags) && (M.NegFlags & Sec->Flags) == 0)
- return &M;
+ for (auto &Pair : MemoryRegions) {
+ MemoryRegion *M = Pair.second;
+ if ((M->Flags & Sec->Flags) && (M->NegFlags & Sec->Flags) == 0)
+ return M;
}
// Otherwise, no suitable region was found.
@@ -642,33 +567,71 @@ MemoryRegion *LinkerScript::findMemoryRegion(OutputSectionCommand *Cmd) {
// This function assigns offsets to input sections and an output section
// for a single sections command (e.g. ".text { *(.text); }").
-void LinkerScript::assignOffsets(OutputSectionCommand *Cmd) {
- OutputSection *Sec = Cmd->Sec;
- if (!Sec)
- return;
-
+void LinkerScript::assignOffsets(OutputSection *Sec) {
if (!(Sec->Flags & SHF_ALLOC))
Dot = 0;
- else if (Cmd->AddrExpr)
- setDot(Cmd->AddrExpr, Cmd->Location, false);
+ else if (Sec->AddrExpr)
+ setDot(Sec->AddrExpr, Sec->Location, false);
+
+ Ctx->MemRegion = Sec->MemRegion;
+ if (Ctx->MemRegion)
+ Dot = Ctx->MemRegionOffset[Ctx->MemRegion];
- if (Cmd->LMAExpr) {
+ if (Sec->LMAExpr) {
uint64_t D = Dot;
- LMAOffset = [=] { return Cmd->LMAExpr().getValue() - D; };
+ Ctx->LMAOffset = [=] { return Sec->LMAExpr().getValue() - D; };
}
- CurMemRegion = Cmd->MemRegion;
- if (CurMemRegion)
- Dot = CurMemRegion->Offset;
switchTo(Sec);
// We do not support custom layout for compressed debug sectons.
// At this point we already know their size and have compressed content.
- if (CurOutSec->Flags & SHF_COMPRESSED)
+ if (Ctx->OutSec->Flags & SHF_COMPRESSED)
return;
- for (BaseCommand *C : Cmd->Commands)
- process(*C);
+ // We visited SectionsCommands from processSectionCommands to
+ // layout sections. Now, we visit SectionsCommands again to fix
+ // section offsets.
+ for (BaseCommand *Base : Sec->SectionCommands) {
+ // This handles the assignments to symbol or to the dot.
+ if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
+ assignSymbol(Cmd, true);
+ continue;
+ }
+
+ // Handle BYTE(), SHORT(), LONG(), or QUAD().
+ if (auto *Cmd = dyn_cast<ByteCommand>(Base)) {
+ Cmd->Offset = Dot - Ctx->OutSec->Addr;
+ Dot += Cmd->Size;
+ Ctx->OutSec->Size = Dot - Ctx->OutSec->Addr;
+ continue;
+ }
+
+ // Handle ASSERT().
+ if (auto *Cmd = dyn_cast<AssertCommand>(Base)) {
+ Cmd->Expression();
+ continue;
+ }
+
+ // Handle a single input section description command.
+ // It calculates and assigns the offsets for each section and also
+ // updates the output section size.
+ auto *Cmd = cast<InputSectionDescription>(Base);
+ for (InputSection *Sec : Cmd->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 *S = dyn_cast<SyntheticSection>(Sec))
+ if (S->empty())
+ continue;
+
+ if (!Sec->Live)
+ continue;
+ assert(Ctx->OutSec == Sec->getParent());
+ output(Sec);
+ }
+ }
}
void LinkerScript::removeEmptyCommands() {
@@ -678,17 +641,15 @@ void LinkerScript::removeEmptyCommands() {
// 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(), [&](BaseCommand *Base) {
- if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base))
- return Cmd->Sec == nullptr;
- return false;
- });
- Opt.Commands.erase(Pos, Opt.Commands.end());
+ llvm::erase_if(SectionCommands, [&](BaseCommand *Base) {
+ if (auto *Sec = dyn_cast<OutputSection>(Base))
+ return !Sec->Live;
+ return false;
+ });
}
-static bool isAllSectionDescription(const OutputSectionCommand &Cmd) {
- for (BaseCommand *Base : Cmd.Commands)
+static bool isAllSectionDescription(const OutputSection &Cmd) {
+ for (BaseCommand *Base : Cmd.SectionCommands)
if (!isa<InputSectionDescription>(*Base))
return false;
return true;
@@ -696,38 +657,55 @@ static bool isAllSectionDescription(const OutputSectionCommand &Cmd) {
void LinkerScript::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.
+ // corresponding output section. The issue is what to do with linker script
+ // like ".foo : { symbol = 42; }". One option would be to convert it to
+ // "symbol = 42;". That is, move the symbol out of the empty section
+ // description. That seems to be what bfd does for this simple case. The
+ // problem is that this is not completely general. bfd will give up and
+ // create a dummy section too if there is a ". = . + 1" inside the section
+ // for example.
+ // Given that we want to create the section, we have to worry what impact
+ // it will have on the link. For example, if we just create a section with
+ // 0 for flags, it would change which PT_LOADs are created.
+ // We could remember that that particular section is dummy and ignore it in
+ // other parts of the linker, but unfortunately there are quite a few places
+ // that would need to change:
+ // * The program header creation.
+ // * The orphan section placement.
+ // * The address assignment.
+ // The other option is to pick flags that minimize the impact the section
+ // will have on the rest of the linker. That is why we copy the flags from
+ // the previous sections. Only a few flags are needed to keep the impact low.
uint64_t Flags = SHF_ALLOC;
- for (int I = 0, E = Opt.Commands.size(); I != E; ++I) {
- auto *Cmd = dyn_cast<OutputSectionCommand>(Opt.Commands[I]);
- if (!Cmd)
+ for (BaseCommand *Cmd : SectionCommands) {
+ auto *Sec = dyn_cast<OutputSection>(Cmd);
+ if (!Sec)
continue;
- if (OutputSection *Sec = Cmd->Sec) {
- Flags = Sec->Flags;
+ if (Sec->Live) {
+ Flags = Sec->Flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR);
continue;
}
- if (isAllSectionDescription(*Cmd))
+ if (isAllSectionDescription(*Sec))
continue;
- auto *OutSec = make<OutputSection>(Cmd->Name, SHT_PROGBITS, Flags);
- OutSec->SectionIndex = I;
- Cmd->Sec = OutSec;
- SecToCommand[OutSec] = Cmd;
+ Sec->Live = true;
+ Sec->Flags = Flags;
}
}
void LinkerScript::adjustSectionsAfterSorting() {
// Try and find an appropriate memory region to assign offsets in.
- for (BaseCommand *Base : Opt.Commands) {
- if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base)) {
- Cmd->MemRegion = findMemoryRegion(Cmd);
+ for (BaseCommand *Base : SectionCommands) {
+ if (auto *Sec = dyn_cast<OutputSection>(Base)) {
+ if (!Sec->Live)
+ continue;
+ Sec->MemRegion = findMemoryRegion(Sec);
// Handle align (e.g. ".foo : ALIGN(16) { ... }").
- if (Cmd->AlignExpr)
- Cmd->Sec->updateAlignment(Cmd->AlignExpr().getValue());
+ if (Sec->AlignExpr)
+ Sec->Alignment =
+ std::max<uint32_t>(Sec->Alignment, Sec->AlignExpr().getValue());
}
}
@@ -739,104 +717,100 @@ void LinkerScript::adjustSectionsAfterSorting() {
// SECTIONS { .aaa : { *(.aaa) } }
std::vector<StringRef> DefPhdrs;
auto FirstPtLoad =
- std::find_if(Opt.PhdrsCommands.begin(), Opt.PhdrsCommands.end(),
+ std::find_if(PhdrsCommands.begin(), PhdrsCommands.end(),
[](const PhdrsCommand &Cmd) { return Cmd.Type == PT_LOAD; });
- if (FirstPtLoad != Opt.PhdrsCommands.end())
+ if (FirstPtLoad != PhdrsCommands.end())
DefPhdrs.push_back(FirstPtLoad->Name);
// Walk the commands and propagate the program headers to commands that don't
// explicitly specify them.
- for (BaseCommand *Base : Opt.Commands) {
- auto *Cmd = dyn_cast<OutputSectionCommand>(Base);
- if (!Cmd)
+ for (BaseCommand *Base : SectionCommands) {
+ auto *Sec = dyn_cast<OutputSection>(Base);
+ if (!Sec)
continue;
- if (Cmd->Phdrs.empty())
- Cmd->Phdrs = DefPhdrs;
- else
- DefPhdrs = Cmd->Phdrs;
+ if (Sec->Phdrs.empty()) {
+ // To match the bfd linker script behaviour, only propagate program
+ // headers to sections that are allocated.
+ if (Sec->Flags & SHF_ALLOC)
+ Sec->Phdrs = DefPhdrs;
+ } else {
+ DefPhdrs = Sec->Phdrs;
+ }
}
-
- removeEmptyCommands();
}
-void LinkerScript::createOrphanCommands() {
- for (OutputSection *Sec : OutputSections) {
- if (Sec->SectionIndex != INT_MAX)
- continue;
- OutputSectionCommand *Cmd =
- createOutputSectionCommand(Sec->Name, "<internal>");
- Cmd->Sec = Sec;
- SecToCommand[Sec] = Cmd;
- auto *ISD = make<InputSectionDescription>("");
- ISD->Sections = Sec->Sections;
- Cmd->Commands.push_back(ISD);
- Opt.Commands.push_back(Cmd);
- }
+static OutputSection *findFirstSection(PhdrEntry *Load) {
+ for (OutputSection *Sec : OutputSections)
+ if (Sec->PtLoad == Load)
+ return Sec;
+ return nullptr;
}
-void LinkerScript::processNonSectionCommands() {
- for (BaseCommand *Base : Opt.Commands) {
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Base))
- assignSymbol(Cmd, false);
- else if (auto *Cmd = dyn_cast<AssertCommand>(Base))
- Cmd->Expression();
- }
-}
+// Try to find an address for the file and program headers output sections,
+// which were unconditionally added to the first PT_LOAD segment earlier.
+//
+// When using the default layout, we check if the headers fit below the first
+// allocated section. When using a linker script, we also check if the headers
+// are covered by the output section. This allows omitting the headers by not
+// leaving enough space for them in the linker script; this pattern is common
+// in embedded systems.
+//
+// If there isn't enough space for these sections, we'll remove them from the
+// PT_LOAD segment, and we'll also remove the PT_PHDR segment.
+void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &Phdrs) {
+ uint64_t Min = std::numeric_limits<uint64_t>::max();
+ for (OutputSection *Sec : OutputSections)
+ if (Sec->Flags & SHF_ALLOC)
+ Min = std::min<uint64_t>(Min, Sec->Addr);
-static bool
-allocateHeaders(std::vector<PhdrEntry> &Phdrs,
- ArrayRef<OutputSectionCommand *> OutputSectionCommands,
- uint64_t Min) {
- auto FirstPTLoad =
- std::find_if(Phdrs.begin(), Phdrs.end(),
- [](const PhdrEntry &E) { return E.p_type == PT_LOAD; });
- if (FirstPTLoad == Phdrs.end())
- return false;
+ auto It = llvm::find_if(
+ Phdrs, [](const PhdrEntry *E) { return E->p_type == PT_LOAD; });
+ if (It == Phdrs.end())
+ return;
+ PhdrEntry *FirstPTLoad = *It;
uint64_t HeaderSize = getHeaderSize();
- if (HeaderSize <= Min || Script->hasPhdrsCommands()) {
+ // When linker script with SECTIONS is being used, don't output headers
+ // unless there's a space for them.
+ uint64_t Base = HasSectionsCommand ? alignDown(Min, Config->MaxPageSize) : 0;
+ if (HeaderSize <= Min - Base || Script->hasPhdrsCommands()) {
Min = alignDown(Min - HeaderSize, Config->MaxPageSize);
Out::ElfHeader->Addr = Min;
Out::ProgramHeaders->Addr = Min + Out::ElfHeader->Size;
- return true;
+ return;
}
- assert(FirstPTLoad->First == Out::ElfHeader);
- OutputSection *ActualFirst = nullptr;
- for (OutputSectionCommand *Cmd : OutputSectionCommands) {
- OutputSection *Sec = Cmd->Sec;
- if (Sec->FirstInPtLoad == Out::ElfHeader) {
- ActualFirst = Sec;
- break;
- }
- }
- if (ActualFirst) {
- for (OutputSectionCommand *Cmd : OutputSectionCommands) {
- OutputSection *Sec = Cmd->Sec;
- if (Sec->FirstInPtLoad == Out::ElfHeader)
- Sec->FirstInPtLoad = ActualFirst;
- }
- FirstPTLoad->First = ActualFirst;
- } else {
- Phdrs.erase(FirstPTLoad);
- }
+ Out::ElfHeader->PtLoad = nullptr;
+ Out::ProgramHeaders->PtLoad = nullptr;
+ FirstPTLoad->FirstSec = findFirstSection(FirstPTLoad);
- auto PhdrI = std::find_if(Phdrs.begin(), Phdrs.end(), [](const PhdrEntry &E) {
- return E.p_type == PT_PHDR;
- });
- if (PhdrI != Phdrs.end())
- Phdrs.erase(PhdrI);
- return false;
+ llvm::erase_if(Phdrs,
+ [](const PhdrEntry *E) { return E->p_type == PT_PHDR; });
+}
+
+LinkerScript::AddressState::AddressState() {
+ for (auto &MRI : Script->MemoryRegions) {
+ const MemoryRegion *MR = MRI.second;
+ MemRegionOffset[MR] = MR->Origin;
+ }
}
-void LinkerScript::assignAddresses(std::vector<PhdrEntry> &Phdrs) {
- // Assign addresses as instructed by linker script SECTIONS sub-commands.
- Dot = 0;
+// Assign addresses as instructed by linker script SECTIONS sub-commands.
+void LinkerScript::assignAddresses() {
+ // By default linker scripts use an initial value of 0 for '.', but prefer
+ // -image-base if set.
+ Dot = Config->ImageBase ? *Config->ImageBase : 0;
+
+ // Ctx captures the local AddressState and makes it accessible
+ // deliberately. This is needed as there are some cases where we cannot just
+ // thread the current state through to a lambda function created by the
+ // script parser.
+ Ctx = make_unique<AddressState>();
ErrorOnMissingSection = true;
switchTo(Aether);
- for (BaseCommand *Base : Opt.Commands) {
+ for (BaseCommand *Base : SectionCommands) {
if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
assignSymbol(Cmd, false);
continue;
@@ -847,304 +821,93 @@ void LinkerScript::assignAddresses(std::vector<PhdrEntry> &Phdrs) {
continue;
}
- auto *Cmd = cast<OutputSectionCommand>(Base);
- assignOffsets(Cmd);
+ assignOffsets(cast<OutputSection>(Base));
}
-
- uint64_t MinVA = std::numeric_limits<uint64_t>::max();
- for (OutputSectionCommand *Cmd : OutputSectionCommands) {
- OutputSection *Sec = Cmd->Sec;
- if (Sec->Flags & SHF_ALLOC)
- MinVA = std::min<uint64_t>(MinVA, Sec->Addr);
- }
-
- allocateHeaders(Phdrs, OutputSectionCommands, MinVA);
+ Ctx = nullptr;
}
// Creates program headers as instructed by PHDRS linker script command.
-std::vector<PhdrEntry> LinkerScript::createPhdrs() {
- std::vector<PhdrEntry> Ret;
+std::vector<PhdrEntry *> LinkerScript::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();
+ for (const PhdrsCommand &Cmd : PhdrsCommands) {
+ PhdrEntry *Phdr = make<PhdrEntry>(Cmd.Type, Cmd.Flags ? *Cmd.Flags : PF_R);
if (Cmd.HasFilehdr)
- Phdr.add(Out::ElfHeader);
+ Phdr->add(Out::ElfHeader);
if (Cmd.HasPhdrs)
- Phdr.add(Out::ProgramHeaders);
+ Phdr->add(Out::ProgramHeaders);
if (Cmd.LMAExpr) {
- Phdr.p_paddr = Cmd.LMAExpr().getValue();
- Phdr.HasLMA = true;
+ Phdr->p_paddr = Cmd.LMAExpr().getValue();
+ Phdr->HasLMA = true;
}
+ Ret.push_back(Phdr);
}
// Add output sections to program headers.
- for (OutputSectionCommand *Cmd : OutputSectionCommands) {
- OutputSection *Sec = Cmd->Sec;
- if (!(Sec->Flags & SHF_ALLOC))
- break;
-
+ for (OutputSection *Sec : OutputSections) {
// Assign headers specified by linker script
for (size_t Id : getPhdrIndices(Sec)) {
- Ret[Id].add(Sec);
- if (Opt.PhdrsCommands[Id].Flags == UINT_MAX)
- Ret[Id].p_flags |= Sec->getPhdrFlags();
+ Ret[Id]->add(Sec);
+ if (!PhdrsCommands[Id].Flags.hasValue())
+ Ret[Id]->p_flags |= Sec->getPhdrFlags();
}
}
return Ret;
}
-bool LinkerScript::ignoreInterpSection() {
- // Ignore .interp section in case we have PHDRS specification
- // and PT_INTERP isn't listed.
- if (Opt.PhdrsCommands.empty())
- return false;
- for (PhdrsCommand &Cmd : Opt.PhdrsCommands)
+// Returns true if we should emit an .interp section.
+//
+// We usually do. But if PHDRS commands are given, and
+// no PT_INTERP is there, there's no place to emit an
+// .interp, so we don't do that in that case.
+bool LinkerScript::needsInterpSection() {
+ if (PhdrsCommands.empty())
+ return true;
+ for (PhdrsCommand &Cmd : PhdrsCommands)
if (Cmd.Type == PT_INTERP)
- return false;
- return true;
-}
-
-OutputSectionCommand *LinkerScript::getCmd(OutputSection *Sec) const {
- auto I = SecToCommand.find(Sec);
- if (I == SecToCommand.end())
- return nullptr;
- return I->second;
-}
-
-uint32_t OutputSectionCommand::getFiller() {
- if (Filler)
- return *Filler;
- if (Sec->Flags & SHF_EXECINSTR)
- return Target->TrapInstr;
- return 0;
-}
-
-static void writeInt(uint8_t *Buf, uint64_t Data, uint64_t Size) {
- if (Size == 1)
- *Buf = Data;
- else if (Size == 2)
- write16(Buf, Data, Config->Endianness);
- else if (Size == 4)
- write32(Buf, Data, Config->Endianness);
- else if (Size == 8)
- write64(Buf, Data, Config->Endianness);
- else
- llvm_unreachable("unsupported Size argument");
-}
-
-static bool compareByFilePosition(InputSection *A, InputSection *B) {
- // Synthetic doesn't have link order dependecy, stable_sort will keep it last
- if (A->kind() == InputSectionBase::Synthetic ||
- B->kind() == InputSectionBase::Synthetic)
- return false;
- InputSection *LA = A->getLinkOrderDep();
- InputSection *LB = B->getLinkOrderDep();
- OutputSection *AOut = LA->getParent();
- OutputSection *BOut = LB->getParent();
- if (AOut != BOut)
- return AOut->SectionIndex < BOut->SectionIndex;
- return LA->OutSecOff < LB->OutSecOff;
-}
-
-template <class ELFT>
-static void finalizeShtGroup(OutputSection *OS,
- ArrayRef<InputSection *> Sections) {
- // sh_link field for SHT_GROUP sections should contain the section index of
- // the symbol table.
- OS->Link = InX::SymTab->getParent()->SectionIndex;
-
- // sh_info then contain index of an entry in symbol table section which
- // provides signature of the section group.
- elf::ObjectFile<ELFT> *Obj = Sections[0]->getFile<ELFT>();
- assert(Config->Relocatable && Sections.size() == 1);
- ArrayRef<SymbolBody *> Symbols = Obj->getSymbols();
- OS->Info = InX::SymTab->getSymbolIndex(Symbols[Sections[0]->Info - 1]);
-}
-
-template <class ELFT> void OutputSectionCommand::finalize() {
- // Link order may be distributed across several InputSectionDescriptions
- // but sort must consider them all at once.
- std::vector<InputSection **> ScriptSections;
- std::vector<InputSection *> Sections;
- for (BaseCommand *Base : Commands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(Base))
- for (InputSection *&IS : ISD->Sections) {
- ScriptSections.push_back(&IS);
- Sections.push_back(IS);
- }
-
- if ((Sec->Flags & SHF_LINK_ORDER)) {
- std::sort(Sections.begin(), Sections.end(), compareByFilePosition);
- for (int I = 0, N = Sections.size(); I < N; ++I)
- *ScriptSections[I] = Sections[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 = Sections.front()->getLinkOrderDep())
- Sec->Link = D->getParent()->SectionIndex;
- }
-
- uint32_t Type = Sec->Type;
- if (Type == SHT_GROUP) {
- finalizeShtGroup<ELFT>(Sec, Sections);
- return;
- }
-
- if (!Config->CopyRelocs || (Type != SHT_RELA && Type != SHT_REL))
- return;
-
- InputSection *First = Sections[0];
- if (isa<SyntheticSection>(First))
- return;
-
- Sec->Link = InX::SymTab->getParent()->SectionIndex;
- // sh_info for SHT_REL[A] sections should contain the section header index of
- // the section to which the relocation applies.
- InputSectionBase *S = First->getRelocatedSection();
- Sec->Info = S->getOutputSection()->SectionIndex;
- Sec->Flags |= SHF_INFO_LINK;
-}
-
-// Compress section contents if this section contains debug info.
-template <class ELFT> void OutputSectionCommand::maybeCompress() {
- typedef typename ELFT::Chdr Elf_Chdr;
-
- // Compress only DWARF debug sections.
- if (!Config->CompressDebugSections || (Sec->Flags & SHF_ALLOC) ||
- !Name.startswith(".debug_"))
- return;
-
- // Create a section header.
- Sec->ZDebugHeader.resize(sizeof(Elf_Chdr));
- auto *Hdr = reinterpret_cast<Elf_Chdr *>(Sec->ZDebugHeader.data());
- Hdr->ch_type = ELFCOMPRESS_ZLIB;
- Hdr->ch_size = Sec->Size;
- Hdr->ch_addralign = Sec->Alignment;
-
- // Write section contents to a temporary buffer and compress it.
- std::vector<uint8_t> Buf(Sec->Size);
- writeTo<ELFT>(Buf.data());
- if (Error E = zlib::compress(toStringRef(Buf), Sec->CompressedData))
- fatal("compress failed: " + llvm::toString(std::move(E)));
-
- // Update section headers.
- Sec->Size = sizeof(Elf_Chdr) + Sec->CompressedData.size();
- Sec->Flags |= SHF_COMPRESSED;
-}
-
-template <class ELFT> void OutputSectionCommand::writeTo(uint8_t *Buf) {
- if (Sec->Type == SHT_NOBITS)
- return;
-
- Sec->Loc = Buf;
-
- // If -compress-debug-section is specified and if this is a debug seciton,
- // we've already compressed section contents. If that's the case,
- // just write it down.
- if (!Sec->CompressedData.empty()) {
- memcpy(Buf, Sec->ZDebugHeader.data(), Sec->ZDebugHeader.size());
- memcpy(Buf + Sec->ZDebugHeader.size(), Sec->CompressedData.data(),
- Sec->CompressedData.size());
- return;
- }
-
- // Write leading padding.
- std::vector<InputSection *> Sections;
- for (BaseCommand *Cmd : Commands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(Cmd))
- for (InputSection *IS : ISD->Sections)
- if (IS->Live)
- Sections.push_back(IS);
- uint32_t Filler = getFiller();
- if (Filler)
- fill(Buf, Sections.empty() ? Sec->Size : Sections[0]->OutSecOff, Filler);
-
- parallelForEachN(0, Sections.size(), [=](size_t I) {
- InputSection *IS = Sections[I];
- IS->writeTo<ELFT>(Buf);
-
- // Fill gaps between sections.
- if (Filler) {
- uint8_t *Start = Buf + IS->OutSecOff + IS->getSize();
- uint8_t *End;
- if (I + 1 == Sections.size())
- End = Buf + Sec->Size;
- else
- End = Buf + Sections[I + 1]->OutSecOff;
- fill(Start, End - Start, Filler);
- }
- });
-
- // Linker scripts may have BYTE()-family commands with which you
- // can write arbitrary bytes to the output. Process them if any.
- for (BaseCommand *Base : Commands)
- if (auto *Data = dyn_cast<BytesDataCommand>(Base))
- writeInt(Buf + Data->Offset, Data->Expression().getValue(), Data->Size);
-}
-
-bool LinkerScript::hasLMA(OutputSection *Sec) {
- if (OutputSectionCommand *Cmd = getCmd(Sec))
- if (Cmd->LMAExpr)
return true;
return false;
}
-ExprValue LinkerScript::getSymbolValue(const Twine &Loc, StringRef S) {
- if (S == ".")
- return {CurOutSec, Dot - CurOutSec->Addr, Loc};
- if (SymbolBody *B = findSymbol(S)) {
- if (auto *D = dyn_cast<DefinedRegular>(B))
- return {D->Section, D->Value, Loc};
- if (auto *C = dyn_cast<DefinedCommon>(B))
- return {InX::Common, C->Offset, Loc};
+ExprValue LinkerScript::getSymbolValue(StringRef Name, const Twine &Loc) {
+ if (Name == ".") {
+ if (Ctx)
+ return {Ctx->OutSec, false, Dot - Ctx->OutSec->Addr, Loc};
+ error(Loc + ": unable to get location counter value");
+ return 0;
}
- error(Loc + ": symbol not found: " + S);
+
+ if (auto *Sym = dyn_cast_or_null<DefinedRegular>(Symtab->find(Name)))
+ return {Sym->Section, false, Sym->Value, Loc};
+
+ error(Loc + ": symbol not found: " + Name);
return 0;
}
-bool LinkerScript::isDefined(StringRef S) { return findSymbol(S) != nullptr; }
+// Returns the index of the segment named Name.
+static Optional<size_t> getPhdrIndex(ArrayRef<PhdrsCommand> Vec,
+ StringRef Name) {
+ for (size_t I = 0; I < Vec.size(); ++I)
+ if (Vec[I].Name == Name)
+ return I;
+ return None;
+}
// Returns indices of ELF headers containing specific section. Each index is a
// zero based number of ELF header listed within PHDRS {} script block.
-std::vector<size_t> LinkerScript::getPhdrIndices(OutputSection *Sec) {
- if (OutputSectionCommand *Cmd = getCmd(Sec)) {
- std::vector<size_t> Ret;
- for (StringRef PhdrName : Cmd->Phdrs)
- Ret.push_back(getPhdrIndex(Cmd->Location, PhdrName));
- return Ret;
- }
- return {};
-}
+std::vector<size_t> LinkerScript::getPhdrIndices(OutputSection *Cmd) {
+ std::vector<size_t> Ret;
-size_t LinkerScript::getPhdrIndex(const Twine &Loc, StringRef PhdrName) {
- size_t I = 0;
- for (PhdrsCommand &Cmd : Opt.PhdrsCommands) {
- if (Cmd.Name == PhdrName)
- return I;
- ++I;
+ for (StringRef S : Cmd->Phdrs) {
+ if (Optional<size_t> Idx = getPhdrIndex(PhdrsCommands, S))
+ Ret.push_back(*Idx);
+ else if (S != "NONE")
+ error(Cmd->Location + ": section header '" + S +
+ "' is not listed in PHDRS");
}
- error(Loc + ": section header '" + PhdrName + "' is not listed in PHDRS");
- return 0;
+ return Ret;
}
-
-template void OutputSectionCommand::writeTo<ELF32LE>(uint8_t *Buf);
-template void OutputSectionCommand::writeTo<ELF32BE>(uint8_t *Buf);
-template void OutputSectionCommand::writeTo<ELF64LE>(uint8_t *Buf);
-template void OutputSectionCommand::writeTo<ELF64BE>(uint8_t *Buf);
-
-template void OutputSectionCommand::maybeCompress<ELF32LE>();
-template void OutputSectionCommand::maybeCompress<ELF32BE>();
-template void OutputSectionCommand::maybeCompress<ELF64LE>();
-template void OutputSectionCommand::maybeCompress<ELF64BE>();
-
-template void OutputSectionCommand::finalize<ELF32LE>();
-template void OutputSectionCommand::finalize<ELF32BE>();
-template void OutputSectionCommand::finalize<ELF64LE>();
-template void OutputSectionCommand::finalize<ELF64BE>();
diff --git a/ELF/LinkerScript.h b/ELF/LinkerScript.h
index f8a34a1e9..992fbb6cf 100644
--- a/ELF/LinkerScript.h
+++ b/ELF/LinkerScript.h
@@ -13,7 +13,7 @@
#include "Config.h"
#include "Strings.h"
#include "Writer.h"
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
@@ -28,7 +28,7 @@
namespace lld {
namespace elf {
-class DefinedCommon;
+class DefinedRegular;
class SymbolBody;
class InputSectionBase;
class InputSection;
@@ -37,22 +37,31 @@ class OutputSectionFactory;
class InputSectionBase;
class SectionBase;
+// This represents an r-value in the linker script.
struct ExprValue {
- SectionBase *Sec;
- uint64_t Val;
- bool ForceAbsolute;
- uint64_t Alignment = 1;
- std::string Loc;
-
ExprValue(SectionBase *Sec, bool ForceAbsolute, uint64_t Val,
const Twine &Loc)
- : Sec(Sec), Val(Val), ForceAbsolute(ForceAbsolute), Loc(Loc.str()) {}
- ExprValue(SectionBase *Sec, uint64_t Val, const Twine &Loc)
- : ExprValue(Sec, false, Val, Loc) {}
- ExprValue(uint64_t Val) : ExprValue(nullptr, Val, "") {}
+ : Sec(Sec), ForceAbsolute(ForceAbsolute), Val(Val), Loc(Loc.str()) {}
+
+ ExprValue(uint64_t Val) : ExprValue(nullptr, false, Val, "") {}
+
bool isAbsolute() const { return ForceAbsolute || Sec == nullptr; }
uint64_t getValue() const;
uint64_t getSecAddr() const;
+ uint64_t getSectionOffset() const;
+
+ // If a value is relative to a section, it has a non-null Sec.
+ SectionBase *Sec;
+
+ // True if this expression is enclosed in ABSOLUTE().
+ // This flag affects the return value of getValue().
+ bool ForceAbsolute;
+
+ uint64_t Val;
+ uint64_t Alignment = 1;
+
+ // Original source location. Used for error messages.
+ std::string Loc;
};
// This represents an expression in the linker script.
@@ -66,8 +75,8 @@ enum SectionsCommandKind {
AssignmentKind, // . = expr or <sym> = expr
OutputSectionKind,
InputSectionKind,
- AssertKind, // ASSERT(expr)
- BytesDataKind // BYTE(expr), SHORT(expr), LONG(expr) or QUAD(expr)
+ AssertKind, // ASSERT(expr)
+ ByteKind // BYTE(expr), SHORT(expr), LONG(expr) or QUAD(expr)
};
struct BaseCommand {
@@ -80,11 +89,13 @@ struct SymbolAssignment : BaseCommand {
SymbolAssignment(StringRef Name, Expr E, std::string Loc)
: BaseCommand(AssignmentKind), Name(Name), Expression(E), Location(Loc) {}
- static bool classof(const BaseCommand *C);
+ static bool classof(const BaseCommand *C) {
+ return C->Kind == AssignmentKind;
+ }
// The LHS of an expression. Name is either a symbol name or ".".
StringRef Name;
- SymbolBody *Sym = nullptr;
+ DefinedRegular *Sym = nullptr;
// The RHS of an expression.
Expr Expression;
@@ -110,38 +121,10 @@ struct MemoryRegion {
std::string Name;
uint64_t Origin;
uint64_t Length;
- uint64_t Offset;
uint32_t Flags;
uint32_t NegFlags;
};
-struct OutputSectionCommand : BaseCommand {
- OutputSectionCommand(StringRef Name)
- : BaseCommand(OutputSectionKind), Name(Name) {}
-
- static bool classof(const BaseCommand *C);
-
- OutputSection *Sec = nullptr;
- MemoryRegion *MemRegion = nullptr;
- StringRef Name;
- Expr AddrExpr;
- Expr AlignExpr;
- Expr LMAExpr;
- Expr SubalignExpr;
- std::vector<BaseCommand *> Commands;
- std::vector<StringRef> Phdrs;
- llvm::Optional<uint32_t> Filler;
- ConstraintKind Constraint = ConstraintKind::NoConstraint;
- std::string Location;
- std::string MemoryRegionName;
- bool Noload = false;
-
- template <class ELFT> void finalize();
- template <class ELFT> void writeTo(uint8_t *Buf);
- template <class ELFT> void maybeCompress();
- uint32_t getFiller();
-};
-
// 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.
@@ -159,7 +142,9 @@ struct InputSectionDescription : BaseCommand {
InputSectionDescription(StringRef FilePattern)
: BaseCommand(InputSectionKind), FilePat(FilePattern) {}
- static bool classof(const BaseCommand *C);
+ static bool classof(const BaseCommand *C) {
+ return C->Kind == InputSectionKind;
+ }
StringMatcher FilePat;
@@ -174,17 +159,17 @@ struct InputSectionDescription : BaseCommand {
struct AssertCommand : BaseCommand {
AssertCommand(Expr E) : BaseCommand(AssertKind), Expression(E) {}
- static bool classof(const BaseCommand *C);
+ static bool classof(const BaseCommand *C) { return C->Kind == AssertKind; }
Expr Expression;
};
// Represents BYTE(), SHORT(), LONG(), or QUAD().
-struct BytesDataCommand : BaseCommand {
- BytesDataCommand(Expr E, unsigned Size)
- : BaseCommand(BytesDataKind), Expression(E), Size(Size) {}
+struct ByteCommand : BaseCommand {
+ ByteCommand(Expr E, unsigned Size)
+ : BaseCommand(ByteKind), Expression(E), Size(Size) {}
- static bool classof(const BaseCommand *C);
+ static bool classof(const BaseCommand *C) { return C->Kind == ByteKind; }
Expr Expression;
unsigned Offset;
@@ -193,79 +178,61 @@ struct BytesDataCommand : BaseCommand {
struct PhdrsCommand {
StringRef Name;
- unsigned Type;
- bool HasFilehdr;
- bool HasPhdrs;
- unsigned Flags;
- Expr LMAExpr;
-};
-
-// ScriptConfiguration holds linker script parse results.
-struct ScriptConfiguration {
- // Used to assign addresses to sections.
- std::vector<BaseCommand *> Commands;
-
- // Used to assign sections to headers.
- std::vector<PhdrsCommand> PhdrsCommands;
-
- 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<InputSectionDescription *> KeptSections;
-
- // A map from memory region name to a memory region descriptor.
- llvm::DenseMap<llvm::StringRef, MemoryRegion> MemoryRegions;
-
- // A list of symbols referenced by the script.
- std::vector<llvm::StringRef> ReferencedSymbols;
+ unsigned Type = llvm::ELF::PT_NULL;
+ bool HasFilehdr = false;
+ bool HasPhdrs = false;
+ llvm::Optional<unsigned> Flags;
+ Expr LMAExpr = nullptr;
};
class LinkerScript final {
- llvm::DenseMap<OutputSection *, OutputSectionCommand *> SecToCommand;
- llvm::DenseMap<StringRef, OutputSectionCommand *> NameToOutputSectionCommand;
+ // Temporary state used in processSectionCommands() and assignAddresses()
+ // that must be reinitialized for each call to the above functions, and must
+ // not be used outside of the scope of a call to the above functions.
+ struct AddressState {
+ AddressState();
+ uint64_t ThreadBssOffset = 0;
+ OutputSection *OutSec = nullptr;
+ MemoryRegion *MemRegion = nullptr;
+ llvm::DenseMap<const MemoryRegion *, uint64_t> MemRegionOffset;
+ std::function<uint64_t()> LMAOffset;
+ };
+
+ llvm::DenseMap<StringRef, OutputSection *> NameToOutputSection;
+ void addSymbol(SymbolAssignment *Cmd);
void assignSymbol(SymbolAssignment *Cmd, bool InSec);
void setDot(Expr E, const Twine &Loc, bool InSec);
std::vector<InputSection *>
computeInputSections(const InputSectionDescription *);
- std::vector<InputSectionBase *>
- createInputSectionList(OutputSectionCommand &Cmd);
+ std::vector<InputSection *> createInputSectionList(OutputSection &Cmd);
std::vector<size_t> getPhdrIndices(OutputSection *Sec);
- size_t getPhdrIndex(const Twine &Loc, StringRef PhdrName);
- MemoryRegion *findMemoryRegion(OutputSectionCommand *Cmd);
+ MemoryRegion *findMemoryRegion(OutputSection *Sec);
void switchTo(OutputSection *Sec);
uint64_t advance(uint64_t Size, unsigned Align);
void output(InputSection *Sec);
- void process(BaseCommand &Base);
+ void assignOffsets(OutputSection *Sec);
+
+ std::unique_ptr<AddressState> Ctx;
OutputSection *Aether;
uint64_t Dot;
- uint64_t ThreadBssOffset = 0;
-
- std::function<uint64_t()> LMAOffset;
- OutputSection *CurOutSec = nullptr;
- MemoryRegion *CurMemRegion = nullptr;
public:
- bool ErrorOnMissingSection = false;
- OutputSectionCommand *createOutputSectionCommand(StringRef Name,
- StringRef Location);
- OutputSectionCommand *getOrCreateOutputSectionCommand(StringRef Name);
+ OutputSection *createOutputSection(StringRef Name, StringRef Location);
+ OutputSection *getOrCreateOutputSection(StringRef Name);
- OutputSectionCommand *getCmd(OutputSection *Sec) const;
- bool hasPhdrsCommands() { return !Opt.PhdrsCommands.empty(); }
+ bool hasPhdrsCommands() { return !PhdrsCommands.empty(); }
uint64_t getDot() { return Dot; }
- void discard(ArrayRef<InputSectionBase *> V);
+ void discard(ArrayRef<InputSection *> V);
- ExprValue getSymbolValue(const Twine &Loc, StringRef S);
- bool isDefined(StringRef S);
+ ExprValue getSymbolValue(StringRef Name, const Twine &Loc);
void fabricateDefaultCommands();
void addOrphanSections(OutputSectionFactory &Factory);
@@ -273,21 +240,32 @@ public:
void adjustSectionsBeforeSorting();
void adjustSectionsAfterSorting();
- std::vector<PhdrEntry> createPhdrs();
- bool ignoreInterpSection();
+ std::vector<PhdrEntry *> createPhdrs();
+ bool needsInterpSection();
- bool hasLMA(OutputSection *Sec);
bool shouldKeep(InputSectionBase *S);
- void assignOffsets(OutputSectionCommand *Cmd);
- void createOrphanCommands();
- void processNonSectionCommands();
- void assignAddresses(std::vector<PhdrEntry> &Phdrs);
+ void assignAddresses();
+ void allocateHeaders(std::vector<PhdrEntry *> &Phdrs);
+ void processSectionCommands(OutputSectionFactory &Factory);
- void addSymbol(SymbolAssignment *Cmd);
- void processCommands(OutputSectionFactory &Factory);
+ // SECTIONS command list.
+ std::vector<BaseCommand *> SectionCommands;
+
+ // PHDRS command list.
+ std::vector<PhdrsCommand> PhdrsCommands;
- // Parsed linker script configurations are set to this struct.
- ScriptConfiguration Opt;
+ bool HasSectionsCommand = false;
+ bool ErrorOnMissingSection = 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<InputSectionDescription *> KeptSections;
+
+ // A map from memory region name to a memory region descriptor.
+ llvm::DenseMap<llvm::StringRef, MemoryRegion *> MemoryRegions;
+
+ // A list of symbols referenced by the script.
+ std::vector<llvm::StringRef> ReferencedSymbols;
};
extern LinkerScript *Script;
diff --git a/ELF/MapFile.cpp b/ELF/MapFile.cpp
index e0c7d8cd8..ad80b0696 100644
--- a/ELF/MapFile.cpp
+++ b/ELF/MapFile.cpp
@@ -25,7 +25,9 @@
#include "OutputSections.h"
#include "Strings.h"
#include "SymbolTable.h"
-#include "Threads.h"
+#include "SyntheticSections.h"
+
+#include "lld/Common/Threads.h"
#include "llvm/Support/raw_ostream.h"
@@ -35,46 +37,52 @@ using namespace llvm::object;
using namespace lld;
using namespace lld::elf;
-typedef DenseMap<const SectionBase *, SmallVector<DefinedRegular *, 4>>
- SymbolMapTy;
+typedef DenseMap<const SectionBase *, SmallVector<Defined *, 4>> SymbolMapTy;
// Print out the first three columns of a line.
-template <class ELFT>
static void writeHeader(raw_ostream &OS, uint64_t Addr, uint64_t Size,
uint64_t Align) {
- int W = ELFT::Is64Bits ? 16 : 8;
+ int W = Config->Is64 ? 16 : 8;
OS << format("%0*llx %0*llx %5lld ", W, Addr, W, Size, Align);
}
static std::string indent(int Depth) { return std::string(Depth * 8, ' '); }
// Returns a list of all symbols that we want to print out.
-template <class ELFT> std::vector<DefinedRegular *> getSymbols() {
- std::vector<DefinedRegular *> V;
- for (elf::ObjectFile<ELFT> *File : Symtab<ELFT>::X->getObjectFiles())
- for (SymbolBody *B : File->getSymbols())
- if (B->File == File && !B->isSection())
- if (auto *Sym = dyn_cast<DefinedRegular>(B))
- if (Sym->Section)
- V.push_back(Sym);
+template <class ELFT> static std::vector<Defined *> getSymbols() {
+ std::vector<Defined *> V;
+ for (InputFile *File : ObjectFiles) {
+ for (SymbolBody *B : File->getSymbols()) {
+ if (auto *DR = dyn_cast<DefinedRegular>(B)) {
+ if (DR->getFile() == File && !DR->isSection() && DR->Section &&
+ DR->Section->Live)
+ V.push_back(DR);
+ } else if (auto *DC = dyn_cast<DefinedCommon>(B)) {
+ if (DC->Section)
+ V.push_back(DC);
+ }
+ }
+ }
return V;
}
// Returns a map from sections to their symbols.
-template <class ELFT>
-SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> Syms) {
+static SymbolMapTy getSectionSyms(ArrayRef<Defined *> Syms) {
SymbolMapTy Ret;
- for (DefinedRegular *S : Syms)
- Ret[S->Section].push_back(S);
+ for (Defined *S : Syms) {
+ if (auto *DR = dyn_cast<DefinedRegular>(S))
+ Ret[DR->Section].push_back(S);
+ else if (auto *DC = dyn_cast<DefinedCommon>(S))
+ Ret[DC->Section].push_back(S);
+ }
// Sort symbols by address. We want to print out symbols in the
// order in the output file rather than the order they appeared
// in the input files.
for (auto &It : Ret) {
- SmallVectorImpl<DefinedRegular *> &V = It.second;
- std::sort(V.begin(), V.end(), [](DefinedRegular *A, DefinedRegular *B) {
- return A->getVA() < B->getVA();
- });
+ SmallVectorImpl<Defined *> &V = It.second;
+ std::sort(V.begin(), V.end(),
+ [](Defined *A, Defined *B) { return A->getVA() < B->getVA(); });
}
return Ret;
}
@@ -83,24 +91,22 @@ SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> Syms) {
// Demangling symbols (which is what toString() does) is slow, so
// we do that in batch using parallel-for.
template <class ELFT>
-DenseMap<DefinedRegular *, std::string>
-getSymbolStrings(ArrayRef<DefinedRegular *> Syms) {
+static DenseMap<Defined *, std::string>
+getSymbolStrings(ArrayRef<Defined *> Syms) {
std::vector<std::string> Str(Syms.size());
parallelForEachN(0, Syms.size(), [&](size_t I) {
raw_string_ostream OS(Str[I]);
- writeHeader<ELFT>(OS, Syms[I]->getVA(), Syms[I]->template getSize<ELFT>(),
- 0);
+ writeHeader(OS, Syms[I]->getVA(), Syms[I]->template getSize<ELFT>(), 0);
OS << indent(2) << toString(*Syms[I]);
});
- DenseMap<DefinedRegular *, std::string> Ret;
+ DenseMap<Defined *, std::string> Ret;
for (size_t I = 0, E = Syms.size(); I < E; ++I)
Ret[Syms[I]] = std::move(Str[I]);
return Ret;
}
-template <class ELFT>
-void elf::writeMapFile(llvm::ArrayRef<OutputSectionCommand *> Script) {
+template <class ELFT> void elf::writeMapFile() {
if (Config->MapFile.empty())
return;
@@ -113,9 +119,9 @@ void elf::writeMapFile(llvm::ArrayRef<OutputSectionCommand *> Script) {
}
// Collect symbol info that we want to print out.
- std::vector<DefinedRegular *> Syms = getSymbols<ELFT>();
- SymbolMapTy SectionSyms = getSectionSyms<ELFT>(Syms);
- DenseMap<DefinedRegular *, std::string> SymStr = getSymbolStrings<ELFT>(Syms);
+ std::vector<Defined *> Syms = getSymbols<ELFT>();
+ SymbolMapTy SectionSyms = getSectionSyms(Syms);
+ DenseMap<Defined *, std::string> SymStr = getSymbolStrings<ELFT>(Syms);
// Print out the header line.
int W = ELFT::Is64Bits ? 16 : 8;
@@ -123,28 +129,27 @@ void elf::writeMapFile(llvm::ArrayRef<OutputSectionCommand *> Script) {
<< " Align Out In Symbol\n";
// Print out file contents.
- for (OutputSectionCommand *Cmd : Script) {
- OutputSection *OSec = Cmd->Sec;
- writeHeader<ELFT>(OS, OSec->Addr, OSec->Size, OSec->Alignment);
+ for (OutputSection *OSec : OutputSections) {
+ writeHeader(OS, OSec->Addr, OSec->Size, OSec->Alignment);
OS << OSec->Name << '\n';
// Dump symbols for each input section.
- for (BaseCommand *Base : Cmd->Commands) {
+ for (BaseCommand *Base : OSec->SectionCommands) {
auto *ISD = dyn_cast<InputSectionDescription>(Base);
if (!ISD)
continue;
for (InputSection *IS : ISD->Sections) {
- writeHeader<ELFT>(OS, OSec->Addr + IS->OutSecOff, IS->getSize(),
- IS->Alignment);
+ writeHeader(OS, OSec->Addr + IS->OutSecOff, IS->getSize(),
+ IS->Alignment);
OS << indent(1) << toString(IS) << '\n';
- for (DefinedRegular *Sym : SectionSyms[IS])
+ for (Defined *Sym : SectionSyms[IS])
OS << SymStr[Sym] << '\n';
}
}
}
}
-template void elf::writeMapFile<ELF32LE>(ArrayRef<OutputSectionCommand *>);
-template void elf::writeMapFile<ELF32BE>(ArrayRef<OutputSectionCommand *>);
-template void elf::writeMapFile<ELF64LE>(ArrayRef<OutputSectionCommand *>);
-template void elf::writeMapFile<ELF64BE>(ArrayRef<OutputSectionCommand *>);
+template void elf::writeMapFile<ELF32LE>();
+template void elf::writeMapFile<ELF32BE>();
+template void elf::writeMapFile<ELF64LE>();
+template void elf::writeMapFile<ELF64BE>();
diff --git a/ELF/MapFile.h b/ELF/MapFile.h
index 68d8ba8d4..1c6364420 100644
--- a/ELF/MapFile.h
+++ b/ELF/MapFile.h
@@ -14,10 +14,9 @@
namespace lld {
namespace elf {
-struct OutputSectionCommand;
-template <class ELFT>
-void writeMapFile(llvm::ArrayRef<OutputSectionCommand *> Script);
-}
-}
+class OutputSection;
+template <class ELFT> void writeMapFile();
+} // namespace elf
+} // namespace lld
#endif
diff --git a/ELF/MarkLive.cpp b/ELF/MarkLive.cpp
index 0b4a78f8d..62a0039ef 100644
--- a/ELF/MarkLive.cpp
+++ b/ELF/MarkLive.cpp
@@ -42,15 +42,6 @@ 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.
-struct ResolvedReloc {
- InputSectionBase *Sec;
- uint64_t Offset;
-};
-} // end anonymous namespace
-
template <class ELFT>
static typename ELFT::uint getAddend(InputSectionBase &Sec,
const typename ELFT::Rel &Rel) {
@@ -70,25 +61,29 @@ static DenseMap<StringRef, std::vector<InputSectionBase *>> CNamedSections;
template <class ELFT, class RelT>
static void resolveReloc(InputSectionBase &Sec, RelT &Rel,
- std::function<void(ResolvedReloc)> Fn) {
+ std::function<void(InputSectionBase *, uint64_t)> Fn) {
SymbolBody &B = Sec.getFile<ELFT>()->getRelocTargetSym(Rel);
+
if (auto *D = dyn_cast<DefinedRegular>(&B)) {
if (!D->Section)
return;
- typename ELFT::uint Offset = D->Value;
+ uint64_t Offset = D->Value;
if (D->isSection())
Offset += getAddend<ELFT>(Sec, Rel);
- Fn({cast<InputSectionBase>(D->Section)->Repl, Offset});
- } else if (auto *U = dyn_cast<Undefined>(&B)) {
- for (InputSectionBase *Sec : CNamedSections.lookup(U->getName()))
- Fn({Sec, 0});
+ Fn(cast<InputSectionBase>(D->Section), Offset);
+ return;
}
+
+ if (!B.isInCurrentDSO())
+ for (InputSectionBase *Sec : CNamedSections.lookup(B.getName()))
+ Fn(Sec, 0);
}
// Calls Fn for each section that Sec refers to via relocations.
template <class ELFT>
-static void forEachSuccessor(InputSection &Sec,
- std::function<void(ResolvedReloc)> Fn) {
+static void
+forEachSuccessor(InputSection &Sec,
+ std::function<void(InputSectionBase *, uint64_t)> Fn) {
if (Sec.AreRelocsRela) {
for (const typename ELFT::Rela &Rel : Sec.template relas<ELFT>())
resolveReloc<ELFT>(Sec, Rel, Fn);
@@ -96,8 +91,9 @@ static void forEachSuccessor(InputSection &Sec,
for (const typename ELFT::Rel &Rel : Sec.template rels<ELFT>())
resolveReloc<ELFT>(Sec, Rel, Fn);
}
+
for (InputSectionBase *IS : Sec.DependentSections)
- Fn({IS, 0});
+ Fn(IS, 0);
}
// The .eh_frame section is an unfortunate special case.
@@ -115,9 +111,11 @@ static void forEachSuccessor(InputSection &Sec,
// 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 &EH, ArrayRef<RelTy> Rels,
- std::function<void(ResolvedReloc)> Enqueue) {
+static void
+scanEhFrameSection(EhInputSection &EH, ArrayRef<RelTy> Rels,
+ std::function<void(InputSectionBase *, uint64_t)> Fn) {
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;
@@ -126,31 +124,31 @@ static void scanEhFrameSection(EhInputSection &EH, ArrayRef<RelTy> Rels,
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.
- resolveReloc<ELFT>(EH, Rels[FirstRelI], Enqueue);
+ resolveReloc<ELFT>(EH, Rels[FirstRelI], Fn);
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();
+ 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;
- resolveReloc<ELFT>(EH, Rels[I2], [&](ResolvedReloc R) {
- if (!R.Sec || R.Sec == &InputSection::Discarded)
- return;
- if (R.Sec->Flags & SHF_EXECINSTR)
- return;
- Enqueue({R.Sec, 0});
- });
+ resolveReloc<ELFT>(EH, Rels[I2],
+ [&](InputSectionBase *Sec, uint64_t Offset) {
+ if (Sec && Sec != &InputSection::Discarded &&
+ !(Sec->Flags & SHF_EXECINSTR))
+ Fn(Sec, 0);
+ });
}
}
}
template <class ELFT>
-static void scanEhFrameSection(EhInputSection &EH,
- std::function<void(ResolvedReloc)> Enqueue) {
+static void
+scanEhFrameSection(EhInputSection &EH,
+ std::function<void(InputSectionBase *, uint64_t)> Fn) {
if (!EH.NumRelocations)
return;
@@ -159,9 +157,9 @@ static void scanEhFrameSection(EhInputSection &EH,
EH.split<ELFT>();
if (EH.AreRelocsRela)
- scanEhFrameSection<ELFT>(EH, EH.template relas<ELFT>(), Enqueue);
+ scanEhFrameSection<ELFT>(EH, EH.template relas<ELFT>(), Fn);
else
- scanEhFrameSection<ELFT>(EH, EH.template rels<ELFT>(), Enqueue);
+ scanEhFrameSection<ELFT>(EH, EH.template rels<ELFT>(), Fn);
}
// We do not garbage-collect two types of sections:
@@ -188,54 +186,55 @@ template <class ELFT> static bool isReserved(InputSectionBase *Sec) {
// This is the main function of the garbage collector.
// Starting from GC-root sections, this function visits all reachable
// sections to set their "Live" bits.
-template <class ELFT> void elf::markLive() {
+template <class ELFT> static void doGcSections() {
SmallVector<InputSection *, 256> Q;
CNamedSections.clear();
- auto Enqueue = [&](ResolvedReloc R) {
+ auto Enqueue = [&](InputSectionBase *Sec, uint64_t Offset) {
// 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 == &InputSection::Discarded)
+ if (Sec == &InputSection::Discarded)
return;
// We don't gc non alloc sections.
- if (!(R.Sec->Flags & SHF_ALLOC))
+ if (!(Sec->Flags & SHF_ALLOC))
return;
// Usually, a whole section is marked as live or dead, but in mergeable
// (splittable) sections, each piece of data has independent liveness bit.
// So we explicitly tell it which offset is in use.
- if (auto *MS = dyn_cast<MergeInputSection>(R.Sec))
- MS->markLiveAt(R.Offset);
+ if (auto *MS = dyn_cast<MergeInputSection>(Sec))
+ MS->markLiveAt(Offset);
- if (R.Sec->Live)
+ if (Sec->Live)
return;
- R.Sec->Live = true;
+ Sec->Live = true;
+
// Add input section to the queue.
- if (InputSection *S = dyn_cast<InputSection>(R.Sec))
+ if (InputSection *S = dyn_cast<InputSection>(Sec))
Q.push_back(S);
};
- auto MarkSymbol = [&](const SymbolBody *Sym) {
+ auto MarkSymbol = [&](SymbolBody *Sym) {
if (auto *D = dyn_cast_or_null<DefinedRegular>(Sym))
if (auto *IS = cast_or_null<InputSectionBase>(D->Section))
- Enqueue({IS, D->Value});
+ Enqueue(IS, D->Value);
};
// Add GC root symbols.
- MarkSymbol(Symtab<ELFT>::X->find(Config->Entry));
- MarkSymbol(Symtab<ELFT>::X->find(Config->Init));
- MarkSymbol(Symtab<ELFT>::X->find(Config->Fini));
+ MarkSymbol(Symtab->find(Config->Entry));
+ MarkSymbol(Symtab->find(Config->Init));
+ MarkSymbol(Symtab->find(Config->Fini));
for (StringRef S : Config->Undefined)
- MarkSymbol(Symtab<ELFT>::X->find(S));
- for (StringRef S : Script->Opt.ReferencedSymbols)
- MarkSymbol(Symtab<ELFT>::X->find(S));
+ MarkSymbol(Symtab->find(S));
+ for (StringRef S : Script->ReferencedSymbols)
+ MarkSymbol(Symtab->find(S));
// Preserve externally-visible symbols if the symbols defined by this
// file can interrupt other ELF file's symbols at runtime.
- for (const Symbol *S : Symtab<ELFT>::X->getSymbols())
+ for (Symbol *S : Symtab->getSymbols())
if (S->includeInDynsym())
MarkSymbol(S->body());
@@ -250,10 +249,10 @@ template <class ELFT> void elf::markLive() {
if (Sec->Flags & SHF_LINK_ORDER)
continue;
if (isReserved<ELFT>(Sec) || Script->shouldKeep(Sec))
- Enqueue({Sec, 0});
+ Enqueue(Sec, 0);
else if (isValidCIdentifier(Sec->Name)) {
CNamedSections[Saver.save("__start_" + Sec->Name)].push_back(Sec);
- CNamedSections[Saver.save("__end_" + Sec->Name)].push_back(Sec);
+ CNamedSections[Saver.save("__stop_" + Sec->Name)].push_back(Sec);
}
}
@@ -262,6 +261,42 @@ template <class ELFT> void elf::markLive() {
forEachSuccessor<ELFT>(*Q.pop_back_val(), Enqueue);
}
+// Before calling this function, Live bits are off for all
+// input sections. This function make some or all of them on
+// so that they are emitted to the output file.
+template <class ELFT> void elf::markLive() {
+ // If -gc-sections is missing, no sections are removed.
+ if (!Config->GcSections) {
+ for (InputSectionBase *Sec : InputSections)
+ Sec->Live = true;
+ return;
+ }
+
+ // The -gc-sections option works only for SHF_ALLOC sections
+ // (sections that are memory-mapped at runtime). So we can
+ // unconditionally make non-SHF_ALLOC sections alive.
+ //
+ // Non SHF_ALLOC sections are not removed even if they are
+ // unreachable through relocations because reachability is not
+ // a good signal whether they are garbage or not (e.g. there is
+ // usually no section referring to a .comment section, but we
+ // want to keep it.)
+ //
+ // Note on SHF_REL{,A}: Such sections reach here only when -r
+ // or -emit-reloc were given. And they are subject of garbage
+ // collection because, if we remove a text section, we also
+ // remove its relocation section.
+ for (InputSectionBase *Sec : InputSections) {
+ bool IsAlloc = (Sec->Flags & SHF_ALLOC);
+ bool IsRel = (Sec->Type == SHT_REL || Sec->Type == SHT_RELA);
+ if (!IsAlloc && !IsRel)
+ Sec->Live = true;
+ }
+
+ // Follow the graph to mark all live sections.
+ doGcSections<ELFT>();
+}
+
template void elf::markLive<ELF32LE>();
template void elf::markLive<ELF32BE>();
template void elf::markLive<ELF64LE>();
diff --git a/ELF/Memory.h b/ELF/Memory.h
index e5a04ed1e..4000f2f9f 100644
--- a/ELF/Memory.h
+++ b/ELF/Memory.h
@@ -61,7 +61,7 @@ inline void freeArena() {
Alloc->reset();
BAlloc.Reset();
}
-}
-}
+} // namespace elf
+} // namespace lld
#endif
diff --git a/ELF/Options.td b/ELF/Options.td
index 335c7ade6..316c1162d 100644
--- a/ELF/Options.td
+++ b/ELF/Options.td
@@ -5,7 +5,11 @@ include "llvm/Option/OptParser.td"
class F<string name>: Flag<["--", "-"], name>;
class J<string name>: Joined<["--", "-"], name>;
class S<string name>: Separate<["--", "-"], name>;
-class JS<string name>: JoinedOrSeparate<["--", "-"], name>;
+
+multiclass Eq<string name> {
+ def "": Separate<["--", "-"], name>;
+ def _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>;
+}
def auxiliary: S<"auxiliary">, HelpText<"Set DT_AUXILIARY field to the specified name">;
@@ -22,21 +26,24 @@ def build_id: F<"build-id">, HelpText<"Generate build ID note">;
def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">;
-def compress_debug_sections : J<"compress-debug-sections=">,
+defm compress_debug_sections : Eq<"compress-debug-sections">,
HelpText<"Compress DWARF debug sections">;
-def defsym: J<"defsym=">, HelpText<"Define a symbol alias">;
+defm defsym: Eq<"defsym">, HelpText<"Define a symbol alias">;
-def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
- HelpText<"Add a directory to the library search path">;
+defm library_path: Eq<"library-path">,
+ HelpText<"Add a directory to the library search path">, MetaVarName<"<dir>">;
-def O: Joined<["-"], "O">, HelpText<"Optimize output file size">;
+def O: JoinedOrSeparate<["-"], "O">, HelpText<"Optimize output file size">;
-def Tbss: S<"Tbss">, HelpText<"Same as --section-start with .bss as the sectionname">;
+defm Tbss: Eq<"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">;
+defm Tdata: Eq<"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">;
+defm Ttext: Eq<"Ttext">,
+ HelpText<"Same as --section-start with .text as the sectionname">;
def allow_multiple_definition: F<"allow-multiple-definition">,
HelpText<"Allow multiple definitions">;
@@ -44,6 +51,9 @@ def allow_multiple_definition: F<"allow-multiple-definition">,
def as_needed: F<"as-needed">,
HelpText<"Only set DT_NEEDED for shared libraries if used">;
+// -chroot doesn't have a help text because it is an internal option.
+def chroot: S<"chroot">;
+
def color_diagnostics: F<"color-diagnostics">,
HelpText<"Use colors in diagnostics">;
@@ -69,7 +79,7 @@ def discard_none: F<"discard-none">,
def dynamic_linker: S<"dynamic-linker">,
HelpText<"Which dynamic linker to use">;
-def dynamic_list: S<"dynamic-list">,
+defm dynamic_list: Eq<"dynamic-list">,
HelpText<"Read a list of dynamic symbols">;
def eh_frame_hdr: F<"eh-frame-hdr">,
@@ -83,32 +93,39 @@ def enable_new_dtags: F<"enable-new-dtags">,
def end_lib: F<"end-lib">,
HelpText<"End a grouping of objects that should be treated as if they were together in an archive">;
-def entry: S<"entry">, MetaVarName<"<entry>">,
- HelpText<"Name of entry point symbol">;
+defm entry: Eq<"entry">, HelpText<"Name of entry point symbol">,
+ MetaVarName<"<entry>">;
-def error_limit: S<"error-limit">,
+defm error_limit: Eq<"error-limit">,
HelpText<"Maximum number of errors to emit before stopping (0 = no limit)">;
def error_unresolved_symbols: F<"error-unresolved-symbols">,
HelpText<"Report unresolved symbols as errors">;
+defm exclude_libs: Eq<"exclude-libs">,
+ HelpText<"Exclude static libraries from automatic export">;
+
def export_dynamic: F<"export-dynamic">,
HelpText<"Put symbols in the dynamic symbol table">;
-def export_dynamic_symbol: S<"export-dynamic-symbol">,
+defm export_dynamic_symbol: Eq<"export-dynamic-symbol">,
HelpText<"Put a symbol in the dynamic symbol table">;
def fatal_warnings: F<"fatal-warnings">,
HelpText<"Treat warnings as errors">;
-def fini: S<"fini">, MetaVarName<"<symbol>">,
- HelpText<"Specify a finalizer function">;
+defm filter: Eq<"filter">,
+ HelpText<"Set DT_FILTER field to the specified name">;
+
+defm fini: Eq<"fini">,
+ HelpText<"Specify a finalizer function">, MetaVarName<"<symbol>">;
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">;
+defm format: Eq<"format">,
+ HelpText<"Change the input format of the inputs following this option">,
+ MetaVarName<"<input-format>">;
def gc_sections: F<"gc-sections">,
HelpText<"Enable garbage collection of unused sections">;
@@ -116,27 +133,29 @@ def gc_sections: F<"gc-sections">,
def gdb_index: F<"gdb-index">,
HelpText<"Generate .gdb_index section">;
-def hash_style: S<"hash-style">,
+defm hash_style: Eq<"hash-style">,
HelpText<"Specify hash style (sysv, gnu or both)">;
def help: F<"help">, HelpText<"Print option help">;
-def icf: F<"icf=all">, HelpText<"Enable identical code folding">;
+def icf_all: F<"icf=all">, HelpText<"Enable identical code folding">;
-def image_base : J<"image-base=">, HelpText<"Set the base address">;
+def icf_none: F<"icf=none">, HelpText<"Disable identical code folding">;
-def init: S<"init">, MetaVarName<"<symbol>">,
- HelpText<"Specify an initializer function">;
+defm image_base : Eq<"image-base">, HelpText<"Set the base address">;
-def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">,
- HelpText<"Root name of library to use">;
+defm init: Eq<"init">, HelpText<"Specify an initializer function">,
+ MetaVarName<"<symbol>">;
+
+defm library: Eq<"library">, HelpText<"Root name of library to use">,
+ MetaVarName<"<libName>">;
def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">,
HelpText<"Optimization level for LTO">;
def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
-def Map: JS<"Map">, HelpText<"Print a link map to the specified file">;
+defm Map: Eq<"Map">, HelpText<"Print a link map to the specified file">;
def nostdlib: F<"nostdlib">,
HelpText<"Only search directories specified on the command line">;
@@ -156,12 +175,18 @@ def no_demangle: F<"no-demangle">,
def no_dynamic_linker: F<"no-dynamic-linker">,
HelpText<"Inhibit output of .interp section">;
+def no_eh_frame_hdr: F<"no-eh-frame-hdr">,
+ HelpText<"Do not create .eh_frame_hdr section">;
+
def no_export_dynamic: F<"no-export-dynamic">;
def no_fatal_warnings: F<"no-fatal-warnings">;
def no_gc_sections: F<"no-gc-sections">,
HelpText<"Disable garbage collection of unused sections">;
+def no_gdb_index: F<"no-gdb-index">,
+ HelpText<"Do not generate .gdb_index section">;
+
def no_gnu_unique: F<"no-gnu-unique">,
HelpText<"Disable STB_GNU_UNIQUE symbol binding">;
@@ -176,7 +201,8 @@ def noinhibit_exec: F<"noinhibit-exec">,
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_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">;
@@ -201,26 +227,28 @@ def print_gc_sections: F<"print-gc-sections">,
def print_map: F<"print-map">,
HelpText<"Print a link map to the standard output">;
-def reproduce: S<"reproduce">,
+defm reproduce: Eq<"reproduce">,
HelpText<"Dump linker invocation and input files for debugging">;
-def rpath: S<"rpath">, HelpText<"Add a DT_RUNPATH to the output">;
+defm rpath: Eq<"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">;
+defm retain_symbols_file: Eq<"retain-symbols-file">,
+ HelpText<"Retain only the symbols listed in the file">,
+ MetaVarName<"<file>">;
-def script: S<"script">, HelpText<"Read linker script">;
+defm script: Eq<"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">;
+defm soname: Eq<"soname">, HelpText<"Set DT_SONAME">;
-def sort_section: S<"sort-section">, HelpText<"Specifies sections sorting rule when linkerscript is used">;
+defm sort_section: Eq<"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">;
@@ -232,27 +260,29 @@ 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">;
+defm sysroot: Eq<"sysroot">, HelpText<"Set the system root">;
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">;
+defm target2: Eq<"target2">,
+ HelpText<"Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel">,
+ MetaVarName<"<type>">;
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 : S<"trace-symbol">, HelpText<"Trace references to symbols">;
+defm trace_symbol : Eq<"trace-symbol">, HelpText<"Trace references to symbols">;
-def undefined: S<"undefined">,
+defm undefined: Eq<"undefined">,
HelpText<"Force undefined symbol during linking">;
-def unresolved_symbols: J<"unresolved-symbols=">,
+defm unresolved_symbols: Eq<"unresolved-symbols">,
HelpText<"Determine how to handle unresolved symbols">;
-def rsp_quoting: J<"rsp-quoting=">,
+defm rsp_quoting: Eq<"rsp-quoting">,
HelpText<"Quoting style for response files. Values supported: windows|posix">;
def v: Flag<["-"], "v">, HelpText<"Display the version number">;
@@ -261,8 +291,7 @@ def verbose: F<"verbose">, HelpText<"Verbose mode">;
def version: F<"version">, HelpText<"Display the version number and exit">;
-def version_script: S<"version-script">,
- HelpText<"Read a version script">;
+defm version_script: Eq<"version-script">, HelpText<"Read a version script">;
def warn_common: F<"warn-common">,
HelpText<"Warn about duplicate common symbols">;
@@ -273,8 +302,8 @@ def warn_unresolved_symbols: F<"warn-unresolved-symbols">,
def whole_archive: F<"whole-archive">,
HelpText<"Force load of all members in a static library">;
-def wrap: S<"wrap">, MetaVarName<"<symbol>">,
- HelpText<"Use wrapper functions for symbol">;
+defm wrap: Eq<"wrap">, HelpText<"Use wrapper functions for symbol">,
+ MetaVarName<"<symbol>">;
def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
HelpText<"Linker option extensions">;
@@ -286,57 +315,36 @@ def alias_Bdynamic_dy: F<"dy">, Alias<Bdynamic>;
def alias_Bstatic_dn: F<"dn">, Alias<Bstatic>;
def alias_Bstatic_non_shared: F<"non_shared">, Alias<Bstatic>;
def alias_Bstatic_static: F<"static">, Alias<Bstatic>;
-def alias_L__library_path: J<"library-path=">, Alias<L>;
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_defsym: S<"defsym">, Alias<defsym>;
def alias_discard_all_x: Flag<["-"], "x">, Alias<discard_all>;
def alias_discard_locals_X: Flag<["-"], "X">, Alias<discard_locals>;
-def alias_dynamic_list: J<"dynamic-list=">, Alias<dynamic_list>;
def alias_emit_relocs: Flag<["-"], "q">, Alias<emit_relocs>;
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_filter: Separate<["-"], "F">, Alias<filter>;
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_Map_eq: J<"Map=">, Alias<Map>;
+def alias_library: JoinedOrSeparate<["-"], "l">, Alias<library>;
+def alias_library_path: JoinedOrSeparate<["-"], "L">, Alias<library_path>;
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_print_map_M: Flag<["-"], "M">, Alias<print_map>;
def alias_relocatable_r: Flag<["-"], "r">, Alias<relocatable>;
-def alias_reproduce_eq: J<"reproduce=">, Alias<reproduce>;
-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 trace_trace_symbol_eq : J<"trace-symbol=">, Alias<trace_symbol>;
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_wrap_wrap: J<"wrap=">, Alias<wrap>;
// Our symbol resolution algorithm handles symbols in archive files differently
// than traditional linkers, so we don't need --start-group and --end-group.
@@ -359,6 +367,8 @@ def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">,
HelpText<"YAML output file for optimization remarks">;
def opt_remarks_with_hotness: Flag<["--"], "opt-remarks-with-hotness">,
HelpText<"Include hotness informations in the optimization remarks file">;
+defm plugin_opt: Eq<"plugin-opt">,
+ HelpText<"specifies LTO options for compatibility with GNU linkers">;
def save_temps: F<"save-temps">;
def thinlto_cache_dir: J<"thinlto-cache-dir=">,
HelpText<"Path to ThinLTO cached object file directory">;
@@ -375,18 +385,15 @@ def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
// --version output.
def plugin: S<"plugin">;
def plugin_eq: J<"plugin=">;
-def plugin_opt: S<"plugin-opt">;
-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 cref: F<"cref">;
def detect_odr_violations: F<"detect-odr-violations">;
def g: Flag<["-"], "g">;
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">,
- Alias<no_add_needed>;
+def no_copy_dt_needed_entries: F<"no-copy-dt-needed-entries">;
def no_keep_memory: F<"no-keep-memory">;
def no_mmap_output_file: F<"no-mmap-output-file">;
def no_warn_common: F<"no-warn-common">;
@@ -396,12 +403,9 @@ 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_once: F<"warn-once">;
def warn_shared_textrel: F<"warn-shared-textrel">;
def EB : F<"EB">;
def EL : F<"EL">;
def G: JoinedOrSeparate<["-"], "G">;
def Qy : F<"Qy">;
-
-// Aliases for ignored options
-def alias_version_script_version_script: J<"version-script=">,
- Alias<version_script>;
diff --git a/ELF/OutputSections.cpp b/ELF/OutputSections.cpp
index 6f04a04be..800ab2eb3 100644
--- a/ELF/OutputSections.cpp
+++ b/ELF/OutputSections.cpp
@@ -15,8 +15,9 @@
#include "SymbolTable.h"
#include "SyntheticSections.h"
#include "Target.h"
-#include "Threads.h"
+#include "lld/Common/Threads.h"
#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/Support/Compression.h"
#include "llvm/Support/MD5.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/SHA1.h"
@@ -42,7 +43,6 @@ OutputSection *Out::InitArray;
OutputSection *Out::FiniArray;
std::vector<OutputSection *> elf::OutputSections;
-std::vector<OutputSectionCommand *> elf::OutputSectionCommands;
uint32_t OutputSection::getPhdrFlags() const {
uint32_t Ret = PF_R;
@@ -68,134 +68,95 @@ void OutputSection::writeHeaderTo(typename ELFT::Shdr *Shdr) {
}
OutputSection::OutputSection(StringRef Name, uint32_t Type, uint64_t Flags)
- : SectionBase(Output, Name, Flags, /*Entsize*/ 0, /*Alignment*/ 1, Type,
+ : BaseCommand(OutputSectionKind),
+ SectionBase(Output, Name, Flags, /*Entsize*/ 0, /*Alignment*/ 1, Type,
/*Info*/ 0,
/*Link*/ 0),
- SectionIndex(INT_MAX) {}
+ SectionIndex(INT_MAX) {
+ Live = false;
+}
-static uint64_t updateOffset(uint64_t Off, InputSection *S) {
- Off = alignTo(Off, S->Alignment);
- S->OutSecOff = Off;
- return Off + S->getSize();
+// We allow sections of types listed below to merged into a
+// single progbits section. This is typically done by linker
+// scripts. Merging nobits and progbits will force disk space
+// to be allocated for nobits sections. Other ones don't require
+// any special treatment on top of progbits, so there doesn't
+// seem to be a harm in merging them.
+static bool canMergeToProgbits(unsigned Type) {
+ return Type == SHT_NOBITS || Type == SHT_PROGBITS || Type == SHT_INIT_ARRAY ||
+ Type == SHT_PREINIT_ARRAY || Type == SHT_FINI_ARRAY ||
+ Type == SHT_NOTE;
}
-void OutputSection::addSection(InputSection *S) {
- assert(S->Live);
- Sections.push_back(S);
- S->Parent = this;
- this->updateAlignment(S->Alignment);
+void OutputSection::addSection(InputSection *IS) {
+ if (!IS->Live) {
+ reportDiscarded(IS);
+ return;
+ }
+
+ if (!Live) {
+ // If IS is the first section to be added to this section,
+ // initialize Type by IS->Type.
+ Live = true;
+ Type = IS->Type;
+ } else {
+ // Otherwise, check if new type or flags are compatible with existing ones.
+ if ((Flags & (SHF_ALLOC | SHF_TLS)) != (IS->Flags & (SHF_ALLOC | SHF_TLS)))
+ error("incompatible section flags for " + Name + "\n>>> " + toString(IS) +
+ ": 0x" + utohexstr(IS->Flags) + "\n>>> output section " + Name +
+ ": 0x" + utohexstr(Flags));
+
+ if (Type != IS->Type) {
+ if (!canMergeToProgbits(Type) || !canMergeToProgbits(IS->Type))
+ error("section type mismatch for " + IS->Name + "\n>>> " +
+ toString(IS) + ": " +
+ getELFSectionTypeName(Config->EMachine, IS->Type) +
+ "\n>>> output section " + Name + ": " +
+ getELFSectionTypeName(Config->EMachine, Type));
+ Type = SHT_PROGBITS;
+ }
+ }
+
+ IS->Parent = this;
+ Flags |= IS->Flags;
+ Alignment = std::max(Alignment, IS->Alignment);
// The actual offsets will be computed by assignAddresses. For now, use
// crude approximation so that it is at least easy for other code to know the
// section order. It is also used to calculate the output section size early
// for compressed debug sections.
- this->Size = updateOffset(Size, S);
+ IS->OutSecOff = alignTo(Size, IS->Alignment);
+ this->Size = IS->OutSecOff + IS->getSize();
// If this section contains a table of fixed-size entries, sh_entsize
// holds the element size. Consequently, if this contains two or more
// input sections, all of them must have the same sh_entsize. However,
// you can put different types of input sections into one output
- // sectin by using linker scripts. I don't know what to do here.
+ // section by using linker scripts. I don't know what to do here.
// Probably we sholuld handle that as an error. But for now we just
// pick the largest sh_entsize.
- this->Entsize = std::max(this->Entsize, S->Entsize);
-}
-
-// This function is called after we sort input sections
-// and scan relocations to setup sections' offsets.
-void OutputSection::assignOffsets() {
- OutputSectionCommand *Cmd = Script->getCmd(this);
- uint64_t Off = 0;
- for (BaseCommand *Base : Cmd->Commands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(Base))
- for (InputSection *S : ISD->Sections)
- Off = updateOffset(Off, S);
- this->Size = Off;
-}
-
-void OutputSection::sort(std::function<int(InputSectionBase *S)> Order) {
- typedef std::pair<unsigned, InputSection *> Pair;
- auto Comp = [](const Pair &A, const Pair &B) { return A.first < B.first; };
-
- std::vector<Pair> V;
- for (InputSection *S : Sections)
- 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.
-void OutputSection::sortInitFini() {
- // Sort sections by priority.
- sort([](InputSectionBase *S) { return getPriority(S->Name); });
-}
-
-// Returns true if S matches /Filename.?\.o$/.
-static bool isCrtBeginEnd(StringRef S, StringRef Filename) {
- if (!S.endswith(".o"))
- return false;
- S = S.drop_back(2);
- if (S.endswith(Filename))
- return true;
- return !S.empty() && S.drop_back().endswith(Filename);
-}
-
-static bool isCrtbegin(StringRef S) { return isCrtBeginEnd(S, "crtbegin"); }
-static bool isCrtend(StringRef S) { return isCrtBeginEnd(S, "crtend"); }
-
-// .ctors and .dtors are sorted by this priority from highest to lowest.
-//
-// 1. The section was contained in crtbegin (crtbegin contains
-// some sentinel value in its .ctors and .dtors so that the runtime
-// can find the beginning of the sections.)
-//
-// 2. The section has an optional priority value in the form of ".ctors.N"
-// or ".dtors.N" where N is a number. Unlike .{init,fini}_array,
-// they are compared as string rather than number.
-//
-// 3. The section is just ".ctors" or ".dtors".
-//
-// 4. The section was contained in crtend, which contains an end marker.
-//
-// In an ideal world, we don't need this function because .init_array and
-// .ctors are duplicate features (and .init_array is newer.) However, there
-// are too many real-world use cases of .ctors, so we had no choice to
-// support that with this rather ad-hoc semantics.
-static bool compCtors(const InputSection *A, const InputSection *B) {
- bool BeginA = isCrtbegin(A->File->getName());
- bool BeginB = isCrtbegin(B->File->getName());
- if (BeginA != BeginB)
- return BeginA;
- bool EndA = isCrtend(A->File->getName());
- bool EndB = isCrtend(B->File->getName());
- if (EndA != EndB)
- return EndB;
- 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);
- Y = Y.substr(6);
- if (X.empty() && Y.empty())
- return false;
- return X < Y;
+ this->Entsize = std::max(this->Entsize, IS->Entsize);
+
+ if (!IS->Assigned) {
+ IS->Assigned = true;
+ if (SectionCommands.empty() ||
+ !isa<InputSectionDescription>(SectionCommands.back()))
+ SectionCommands.push_back(make<InputSectionDescription>(""));
+ auto *ISD = cast<InputSectionDescription>(SectionCommands.back());
+ ISD->Sections.push_back(IS);
+ }
}
-// Sorts input sections by the special rules for .ctors and .dtors.
-// Unfortunately, the rules are different from the one for .{init,fini}_array.
-// Read the comment above.
-void OutputSection::sortCtorsDtors() {
- std::stable_sort(Sections.begin(), Sections.end(), compCtors);
-}
+static SectionKey createKey(InputSectionBase *IS, StringRef OutsecName) {
+ // When control reaches here, mergeable sections have already been
+ // merged except the -r case. If that's the case, we want to combine
+ // mergeable sections by sh_entsize and sh_flags.
+ if (Config->Relocatable && (IS->Flags & SHF_MERGE)) {
+ uint64_t Flags = IS->Flags & (SHF_MERGE | SHF_STRINGS);
+ uint32_t Alignment = std::max<uint32_t>(IS->Alignment, IS->Entsize);
+ return SectionKey{OutsecName, Flags, Alignment};
+ }
-static SectionKey createKey(InputSectionBase *C, StringRef OutsecName) {
// The ELF spec just says
// ----------------------------------------------------------------
// In the first phase, input sections that match in name, type and
@@ -238,57 +199,57 @@ static SectionKey createKey(InputSectionBase *C, StringRef OutsecName) {
//
// Given the above issues, we instead merge sections by name and error on
// incompatible types and flags.
-
- uint32_t Alignment = 0;
- uint64_t Flags = 0;
- if (Config->Relocatable && (C->Flags & SHF_MERGE)) {
- Alignment = std::max<uint64_t>(C->Alignment, C->Entsize);
- Flags = C->Flags & (SHF_MERGE | SHF_STRINGS);
- }
-
- return SectionKey{OutsecName, Flags, Alignment};
+ return SectionKey{OutsecName, 0, 0};
}
-OutputSectionFactory::OutputSectionFactory(
- std::vector<OutputSection *> &OutputSections)
- : OutputSections(OutputSections) {}
+OutputSectionFactory::OutputSectionFactory() {}
-static uint64_t getIncompatibleFlags(uint64_t Flags) {
- return Flags & (SHF_ALLOC | SHF_TLS);
-}
+void elf::sortByOrder(MutableArrayRef<InputSection *> In,
+ std::function<int(InputSectionBase *S)> Order) {
+ typedef std::pair<int, InputSection *> Pair;
+ auto Comp = [](const Pair &A, const Pair &B) { return A.first < B.first; };
-// We allow sections of types listed below to merged into a
-// single progbits section. This is typically done by linker
-// scripts. Merging nobits and progbits will force disk space
-// to be allocated for nobits sections. Other ones don't require
-// any special treatment on top of progbits, so there doesn't
-// seem to be a harm in merging them.
-static bool canMergeToProgbits(unsigned Type) {
- return Type == SHT_NOBITS || Type == SHT_PROGBITS || Type == SHT_INIT_ARRAY ||
- Type == SHT_PREINIT_ARRAY || Type == SHT_FINI_ARRAY ||
- Type == SHT_NOTE;
+ std::vector<Pair> V;
+ for (InputSection *S : In)
+ V.push_back({Order(S), S});
+ std::stable_sort(V.begin(), V.end(), Comp);
+
+ for (size_t I = 0; I < V.size(); ++I)
+ In[I] = V[I].second;
}
void elf::reportDiscarded(InputSectionBase *IS) {
if (!Config->PrintGcSections)
return;
message("removing unused section from '" + IS->Name + "' in file '" +
- IS->File->getName());
+ IS->File->getName() + "'");
}
-void OutputSectionFactory::addInputSec(InputSectionBase *IS,
- StringRef OutsecName) {
- // Sections with the SHT_GROUP attribute reach here only when the - r option
- // is given. Such sections define "section groups", and InputFiles.cpp has
- // dedup'ed section groups by their signatures. For the -r, we want to pass
- // through all SHT_GROUP sections without merging them because merging them
- // creates broken section contents.
- if (IS->Type == SHT_GROUP) {
- OutputSection *Out = nullptr;
- addInputSec(IS, OutsecName, Out);
- return;
+static OutputSection *createSection(InputSectionBase *IS, StringRef OutsecName) {
+ OutputSection *Sec = Script->createOutputSection(OutsecName, "<internal>");
+ Sec->Type = IS->Type;
+ Sec->Flags = IS->Flags;
+ Sec->addSection(cast<InputSection>(IS));
+ return Sec;
+}
+
+OutputSection *OutputSectionFactory::addInputSec(InputSectionBase *IS,
+ StringRef OutsecName) {
+ if (!IS->Live) {
+ reportDiscarded(IS);
+ return nullptr;
}
+ // Sections with SHT_GROUP or SHF_GROUP attributes reach here only when the -r
+ // option is given. A section with SHT_GROUP defines a "section group", and
+ // its members have SHF_GROUP attribute. Usually these flags have already been
+ // stripped by InputFiles.cpp as section groups are processed and uniquified.
+ // However, for the -r option, we want to pass through all section groups
+ // as-is because adding/removing members or merging them with other groups
+ // change their semantics.
+ if (IS->Type == SHT_GROUP || (IS->Flags & SHF_GROUP))
+ return createSection(IS, OutsecName);
+
// Imagine .zed : { *(.foo) *(.bar) } script. Both foo and bar may have
// relocation sections .rela.foo and .rela.bar for example. Most tools do
// not allow multiple REL[A] sections for output section. Hence we
@@ -299,46 +260,25 @@ void OutputSectionFactory::addInputSec(InputSectionBase *IS,
(IS->Type == SHT_REL || IS->Type == SHT_RELA)) {
auto *Sec = cast<InputSection>(IS);
OutputSection *Out = Sec->getRelocatedSection()->getOutputSection();
- addInputSec(IS, OutsecName, Out->RelocationSection);
- return;
- }
- SectionKey Key = createKey(IS, OutsecName);
- OutputSection *&Sec = Map[Key];
- return addInputSec(IS, OutsecName, Sec);
-}
+ if (Out->RelocationSection) {
+ Out->RelocationSection->addSection(Sec);
+ return nullptr;
+ }
-void OutputSectionFactory::addInputSec(InputSectionBase *IS,
- StringRef OutsecName,
- OutputSection *&Sec) {
- if (!IS->Live) {
- reportDiscarded(IS);
- return;
+ Out->RelocationSection = createSection(IS, OutsecName);
+ return Out->RelocationSection;
}
+ SectionKey Key = createKey(IS, OutsecName);
+ OutputSection *&Sec = Map[Key];
if (Sec) {
- if (getIncompatibleFlags(Sec->Flags) != getIncompatibleFlags(IS->Flags))
- error("incompatible section flags for " + Sec->Name +
- "\n>>> " + toString(IS) + ": 0x" + utohexstr(IS->Flags) +
- "\n>>> output section " + Sec->Name + ": 0x" +
- utohexstr(Sec->Flags));
- if (Sec->Type != IS->Type) {
- if (canMergeToProgbits(Sec->Type) && canMergeToProgbits(IS->Type))
- Sec->Type = SHT_PROGBITS;
- else
- error("section type mismatch for " + IS->Name +
- "\n>>> " + toString(IS) + ": " +
- getELFSectionTypeName(Config->EMachine, IS->Type) +
- "\n>>> output section " + Sec->Name + ": " +
- getELFSectionTypeName(Config->EMachine, Sec->Type));
- }
- Sec->Flags |= IS->Flags;
- } else {
- Sec = make<OutputSection>(OutsecName, IS->Type, IS->Flags);
- OutputSections.push_back(Sec);
+ Sec->addSection(cast<InputSection>(IS));
+ return nullptr;
}
- Sec->addSection(cast<InputSection>(IS));
+ Sec = createSection(IS, OutsecName);
+ return Sec;
}
OutputSectionFactory::~OutputSectionFactory() {}
@@ -367,7 +307,300 @@ uint64_t elf::getHeaderSize() {
return Out::ElfHeader->Size + Out::ProgramHeaders->Size;
}
+bool OutputSection::classof(const BaseCommand *C) {
+ return C->Kind == OutputSectionKind;
+}
+
+void OutputSection::sort(std::function<int(InputSectionBase *S)> Order) {
+ assert(SectionCommands.size() == 1);
+ sortByOrder(cast<InputSectionDescription>(SectionCommands[0])->Sections,
+ Order);
+}
+
+// Fill [Buf, Buf + Size) with Filler.
+// This is used for linker script "=fillexp" command.
+static void fill(uint8_t *Buf, size_t Size, uint32_t Filler) {
+ size_t I = 0;
+ for (; I + 4 < Size; I += 4)
+ memcpy(Buf + I, &Filler, 4);
+ memcpy(Buf + I, &Filler, Size - I);
+}
+
+// Compress section contents if this section contains debug info.
+template <class ELFT> void OutputSection::maybeCompress() {
+ typedef typename ELFT::Chdr Elf_Chdr;
+
+ // Compress only DWARF debug sections.
+ if (!Config->CompressDebugSections || (Flags & SHF_ALLOC) ||
+ !Name.startswith(".debug_"))
+ return;
+
+ // Create a section header.
+ ZDebugHeader.resize(sizeof(Elf_Chdr));
+ auto *Hdr = reinterpret_cast<Elf_Chdr *>(ZDebugHeader.data());
+ Hdr->ch_type = ELFCOMPRESS_ZLIB;
+ Hdr->ch_size = Size;
+ Hdr->ch_addralign = Alignment;
+
+ // Write section contents to a temporary buffer and compress it.
+ std::vector<uint8_t> Buf(Size);
+ writeTo<ELFT>(Buf.data());
+ if (Error E = zlib::compress(toStringRef(Buf), CompressedData))
+ fatal("compress failed: " + llvm::toString(std::move(E)));
+
+ // Update section headers.
+ Size = sizeof(Elf_Chdr) + CompressedData.size();
+ Flags |= SHF_COMPRESSED;
+}
+
+static void writeInt(uint8_t *Buf, uint64_t Data, uint64_t Size) {
+ if (Size == 1)
+ *Buf = Data;
+ else if (Size == 2)
+ write16(Buf, Data, Config->Endianness);
+ else if (Size == 4)
+ write32(Buf, Data, Config->Endianness);
+ else if (Size == 8)
+ write64(Buf, Data, Config->Endianness);
+ else
+ llvm_unreachable("unsupported Size argument");
+}
+
+template <class ELFT> void OutputSection::writeTo(uint8_t *Buf) {
+ if (Type == SHT_NOBITS)
+ return;
+
+ Loc = Buf;
+
+ // If -compress-debug-section is specified and if this is a debug seciton,
+ // we've already compressed section contents. If that's the case,
+ // just write it down.
+ if (!CompressedData.empty()) {
+ memcpy(Buf, ZDebugHeader.data(), ZDebugHeader.size());
+ memcpy(Buf + ZDebugHeader.size(), CompressedData.data(),
+ CompressedData.size());
+ return;
+ }
+
+ // Write leading padding.
+ std::vector<InputSection *> Sections;
+ for (BaseCommand *Cmd : SectionCommands)
+ if (auto *ISD = dyn_cast<InputSectionDescription>(Cmd))
+ for (InputSection *IS : ISD->Sections)
+ if (IS->Live)
+ Sections.push_back(IS);
+ uint32_t Filler = getFiller();
+ if (Filler)
+ fill(Buf, Sections.empty() ? Size : Sections[0]->OutSecOff, Filler);
+
+ parallelForEachN(0, Sections.size(), [&](size_t I) {
+ InputSection *IS = Sections[I];
+ IS->writeTo<ELFT>(Buf);
+
+ // Fill gaps between sections.
+ if (Filler) {
+ uint8_t *Start = Buf + IS->OutSecOff + IS->getSize();
+ uint8_t *End;
+ if (I + 1 == Sections.size())
+ End = Buf + Size;
+ else
+ End = Buf + Sections[I + 1]->OutSecOff;
+ fill(Start, End - Start, Filler);
+ }
+ });
+
+ // Linker scripts may have BYTE()-family commands with which you
+ // can write arbitrary bytes to the output. Process them if any.
+ for (BaseCommand *Base : SectionCommands)
+ if (auto *Data = dyn_cast<ByteCommand>(Base))
+ writeInt(Buf + Data->Offset, Data->Expression().getValue(), Data->Size);
+}
+
+static bool compareByFilePosition(InputSection *A, InputSection *B) {
+ // Synthetic doesn't have link order dependecy, stable_sort will keep it last
+ if (A->kind() == InputSectionBase::Synthetic ||
+ B->kind() == InputSectionBase::Synthetic)
+ return false;
+ InputSection *LA = A->getLinkOrderDep();
+ InputSection *LB = B->getLinkOrderDep();
+ OutputSection *AOut = LA->getParent();
+ OutputSection *BOut = LB->getParent();
+ if (AOut != BOut)
+ return AOut->SectionIndex < BOut->SectionIndex;
+ return LA->OutSecOff < LB->OutSecOff;
+}
+
+template <class ELFT>
+static void finalizeShtGroup(OutputSection *OS,
+ ArrayRef<InputSection *> Sections) {
+ assert(Config->Relocatable && Sections.size() == 1);
+
+ // sh_link field for SHT_GROUP sections should contain the section index of
+ // the symbol table.
+ OS->Link = InX::SymTab->getParent()->SectionIndex;
+
+ // sh_info then contain index of an entry in symbol table section which
+ // provides signature of the section group.
+ ObjFile<ELFT> *Obj = Sections[0]->getFile<ELFT>();
+ ArrayRef<SymbolBody *> Symbols = Obj->getSymbols();
+ OS->Info = InX::SymTab->getSymbolIndex(Symbols[Sections[0]->Info]);
+}
+
+template <class ELFT> void OutputSection::finalize() {
+ // Link order may be distributed across several InputSectionDescriptions
+ // but sort must consider them all at once.
+ std::vector<InputSection **> ScriptSections;
+ std::vector<InputSection *> Sections;
+ for (BaseCommand *Base : SectionCommands) {
+ if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) {
+ for (InputSection *&IS : ISD->Sections) {
+ ScriptSections.push_back(&IS);
+ Sections.push_back(IS);
+ }
+ }
+ }
+
+ if (Flags & SHF_LINK_ORDER) {
+ std::stable_sort(Sections.begin(), Sections.end(), compareByFilePosition);
+ for (int I = 0, N = Sections.size(); I < N; ++I)
+ *ScriptSections[I] = Sections[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 = Sections.front()->getLinkOrderDep())
+ Link = D->getParent()->SectionIndex;
+ }
+
+ if (Type == SHT_GROUP) {
+ finalizeShtGroup<ELFT>(this, Sections);
+ return;
+ }
+
+ if (!Config->CopyRelocs || (Type != SHT_RELA && Type != SHT_REL))
+ return;
+
+ InputSection *First = Sections[0];
+ if (isa<SyntheticSection>(First))
+ return;
+
+ Link = InX::SymTab->getParent()->SectionIndex;
+ // sh_info for SHT_REL[A] sections should contain the section header index of
+ // the section to which the relocation applies.
+ InputSectionBase *S = First->getRelocatedSection();
+ Info = S->getOutputSection()->SectionIndex;
+ Flags |= SHF_INFO_LINK;
+}
+
+// Returns true if S matches /Filename.?\.o$/.
+static bool isCrtBeginEnd(StringRef S, StringRef Filename) {
+ if (!S.endswith(".o"))
+ return false;
+ S = S.drop_back(2);
+ if (S.endswith(Filename))
+ return true;
+ return !S.empty() && S.drop_back().endswith(Filename);
+}
+
+static bool isCrtbegin(StringRef S) { return isCrtBeginEnd(S, "crtbegin"); }
+static bool isCrtend(StringRef S) { return isCrtBeginEnd(S, "crtend"); }
+
+// .ctors and .dtors are sorted by this priority from highest to lowest.
+//
+// 1. The section was contained in crtbegin (crtbegin contains
+// some sentinel value in its .ctors and .dtors so that the runtime
+// can find the beginning of the sections.)
+//
+// 2. The section has an optional priority value in the form of ".ctors.N"
+// or ".dtors.N" where N is a number. Unlike .{init,fini}_array,
+// they are compared as string rather than number.
+//
+// 3. The section is just ".ctors" or ".dtors".
+//
+// 4. The section was contained in crtend, which contains an end marker.
+//
+// In an ideal world, we don't need this function because .init_array and
+// .ctors are duplicate features (and .init_array is newer.) However, there
+// are too many real-world use cases of .ctors, so we had no choice to
+// support that with this rather ad-hoc semantics.
+static bool compCtors(const InputSection *A, const InputSection *B) {
+ bool BeginA = isCrtbegin(A->File->getName());
+ bool BeginB = isCrtbegin(B->File->getName());
+ if (BeginA != BeginB)
+ return BeginA;
+ bool EndA = isCrtend(A->File->getName());
+ bool EndB = isCrtend(B->File->getName());
+ if (EndA != EndB)
+ return EndB;
+ 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);
+ Y = Y.substr(6);
+ if (X.empty() && Y.empty())
+ return false;
+ return X < Y;
+}
+
+// Sorts input sections by the special rules for .ctors and .dtors.
+// Unfortunately, the rules are different from the one for .{init,fini}_array.
+// Read the comment above.
+void OutputSection::sortCtorsDtors() {
+ assert(SectionCommands.size() == 1);
+ auto *ISD = cast<InputSectionDescription>(SectionCommands[0]);
+ std::stable_sort(ISD->Sections.begin(), ISD->Sections.end(), compCtors);
+}
+
+// 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 (!to_integer(S.substr(Pos + 1), V, 10))
+ return 65536;
+ return V;
+}
+
+// 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.
+void OutputSection::sortInitFini() {
+ // Sort sections by priority.
+ sort([](InputSectionBase *S) { return getPriority(S->Name); });
+}
+
+uint32_t OutputSection::getFiller() {
+ if (Filler)
+ return *Filler;
+ if (Flags & SHF_EXECINSTR)
+ return Target->TrapInstr;
+ return 0;
+}
+
template void OutputSection::writeHeaderTo<ELF32LE>(ELF32LE::Shdr *Shdr);
template void OutputSection::writeHeaderTo<ELF32BE>(ELF32BE::Shdr *Shdr);
template void OutputSection::writeHeaderTo<ELF64LE>(ELF64LE::Shdr *Shdr);
template void OutputSection::writeHeaderTo<ELF64BE>(ELF64BE::Shdr *Shdr);
+
+template void OutputSection::writeTo<ELF32LE>(uint8_t *Buf);
+template void OutputSection::writeTo<ELF32BE>(uint8_t *Buf);
+template void OutputSection::writeTo<ELF64LE>(uint8_t *Buf);
+template void OutputSection::writeTo<ELF64BE>(uint8_t *Buf);
+
+template void OutputSection::maybeCompress<ELF32LE>();
+template void OutputSection::maybeCompress<ELF32BE>();
+template void OutputSection::maybeCompress<ELF64LE>();
+template void OutputSection::maybeCompress<ELF64BE>();
+
+template void OutputSection::finalize<ELF32LE>();
+template void OutputSection::finalize<ELF32BE>();
+template void OutputSection::finalize<ELF64LE>();
+template void OutputSection::finalize<ELF64BE>();
diff --git a/ELF/OutputSections.h b/ELF/OutputSections.h
index d5f77838d..cce30f632 100644
--- a/ELF/OutputSections.h
+++ b/ELF/OutputSections.h
@@ -12,9 +12,10 @@
#include "Config.h"
#include "InputSection.h"
+#include "LinkerScript.h"
#include "Relocations.h"
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Object/ELF.h"
@@ -29,7 +30,7 @@ class InputSection;
class InputSectionBase;
class MergeInputSection;
class OutputSection;
-template <class ELFT> class ObjectFile;
+template <class ELFT> class ObjFile;
template <class ELFT> class SharedFile;
class SharedSymbol;
class DefinedRegular;
@@ -38,7 +39,7 @@ class DefinedRegular;
// It is composed of multiple InputSections.
// The writer creates multiple OutputSections and assign them unique,
// non-overlapping file offsets and VAs.
-class OutputSection final : public SectionBase {
+class OutputSection final : public BaseCommand, public SectionBase {
public:
OutputSection(StringRef Name, uint32_t Type, uint64_t Flags);
@@ -46,6 +47,8 @@ public:
return S->kind() == SectionBase::Output;
}
+ static bool classof(const BaseCommand *C);
+
uint64_t getLMA() const { return Addr + LMAOffset; }
template <typename ELFT> void writeHeaderTo(typename ELFT::Shdr *SHdr);
@@ -54,18 +57,14 @@ public:
uint32_t getPhdrFlags() const;
- void updateAlignment(uint32_t Val) {
- if (Val > Alignment)
- Alignment = Val;
- }
-
- // 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.
- OutputSection *FirstInPtLoad = nullptr;
+ // Pointer to the PT_LOAD segment, which this section 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, where Off_first and VA_first is file offset and VA of first
+ // section in PT_LOAD.
+ PhdrEntry *PtLoad = nullptr;
// Pointer to a relocation section for this section. Usually nullptr because
// we consume relocations, but if --emit-relocs is specified (which is rare),
@@ -79,21 +78,43 @@ public:
uint64_t Addr = 0;
uint32_t ShName = 0;
- void addSection(InputSection *S);
+ void addSection(InputSection *IS);
+
+ // Location in the output buffer.
+ uint8_t *Loc = nullptr;
+
+ // The following members are normally only used in linker scripts.
+ MemoryRegion *MemRegion = nullptr;
+ Expr AddrExpr;
+ Expr AlignExpr;
+ Expr LMAExpr;
+ Expr SubalignExpr;
+ std::vector<BaseCommand *> SectionCommands;
+ std::vector<StringRef> Phdrs;
+ llvm::Optional<uint32_t> Filler;
+ ConstraintKind Constraint = ConstraintKind::NoConstraint;
+ std::string Location;
+ std::string MemoryRegionName;
+ bool Noload = false;
+
+ template <class ELFT> void finalize();
+ template <class ELFT> void writeTo(uint8_t *Buf);
+ template <class ELFT> void maybeCompress();
+
void sort(std::function<int(InputSectionBase *S)> Order);
void sortInitFini();
void sortCtorsDtors();
- void assignOffsets();
- std::vector<InputSection *> Sections;
+private:
// Used for implementation of --compress-debug-sections option.
std::vector<uint8_t> ZDebugHeader;
llvm::SmallVector<char, 1> CompressedData;
- // Location in the output buffer.
- uint8_t *Loc = nullptr;
+ uint32_t getFiller();
};
+int getPriority(StringRef S);
+
// All output sections that are handled by the linker specially are
// globally accessible. Writer initializes them, so don't use them
// until Writer is initialized.
@@ -115,8 +136,9 @@ struct SectionKey {
uint64_t Flags;
uint32_t Alignment;
};
-}
-}
+} // namespace elf
+} // namespace lld
+
namespace llvm {
template <> struct DenseMapInfo<lld::elf::SectionKey> {
static lld::elf::SectionKey getEmptyKey();
@@ -125,35 +147,32 @@ template <> struct DenseMapInfo<lld::elf::SectionKey> {
static bool isEqual(const lld::elf::SectionKey &LHS,
const lld::elf::SectionKey &RHS);
};
-}
+} // namespace llvm
+
namespace lld {
namespace elf {
-
// This class knows how to create an output section for a given
// input section. Output section type is determined by various
// factors, including input section's sh_flags, sh_type and
// linker scripts.
class OutputSectionFactory {
public:
- OutputSectionFactory(std::vector<OutputSection *> &OutputSections);
+ OutputSectionFactory();
~OutputSectionFactory();
- void addInputSec(InputSectionBase *IS, StringRef OutsecName);
- void addInputSec(InputSectionBase *IS, StringRef OutsecName,
- OutputSection *&Sec);
+ OutputSection *addInputSec(InputSectionBase *IS, StringRef OutsecName);
private:
llvm::SmallDenseMap<SectionKey, OutputSection *> Map;
- std::vector<OutputSection *> &OutputSections;
};
uint64_t getHeaderSize();
void reportDiscarded(InputSectionBase *IS);
+void sortByOrder(llvm::MutableArrayRef<InputSection *> In,
+ std::function<int(InputSectionBase *S)> Order);
extern std::vector<OutputSection *> OutputSections;
-extern std::vector<OutputSectionCommand *> OutputSectionCommands;
} // namespace elf
} // namespace lld
-
#endif
diff --git a/ELF/Relocations.cpp b/ELF/Relocations.cpp
index 1ac3bce76..b80e3e2f1 100644
--- a/ELF/Relocations.cpp
+++ b/ELF/Relocations.cpp
@@ -73,27 +73,32 @@ template <class ELFT>
static std::string getLocation(InputSectionBase &S, const SymbolBody &Sym,
uint64_t Off) {
std::string Msg =
- "\n>>> defined in " + toString(Sym.File) + "\n>>> referenced by ";
+ "\n>>> defined in " + toString(Sym.getFile()) + "\n>>> referenced by ";
std::string Src = S.getSrcMsg<ELFT>(Off);
if (!Src.empty())
Msg += Src + "\n>>> ";
return Msg + S.getObjMsg<ELFT>(Off);
}
-static bool isPreemptible(const SymbolBody &Body, uint32_t Type) {
- // In case of MIPS GP-relative relocations always resolve to a definition
- // in a regular input file, ignoring the one-definition rule. So we,
- // for example, should not attempt to create a dynamic relocation even
- // if the target symbol is preemptible. There are two two MIPS GP-relative
- // relocations R_MIPS_GPREL16 and R_MIPS_GPREL32. But only R_MIPS_GPREL16
- // can be against a preemptible symbol.
- // To get MIPS relocation type we apply 0xff mask. In case of O32 ABI all
- // relocation types occupy eight bit. In case of N64 ABI we extract first
- // relocation from 3-in-1 packet because only the first relocation can
- // be against a real symbol.
- if (Config->EMachine == EM_MIPS && (Type & 0xff) == R_MIPS_GPREL16)
+// This is a MIPS-specific rule.
+//
+// In case of MIPS GP-relative relocations always resolve to a definition
+// in a regular input file, ignoring the one-definition rule. So we,
+// for example, should not attempt to create a dynamic relocation even
+// if the target symbol is preemptible. There are two two MIPS GP-relative
+// relocations R_MIPS_GPREL16 and R_MIPS_GPREL32. But only R_MIPS_GPREL16
+// can be against a preemptible symbol.
+//
+// To get MIPS relocation type we apply 0xff mask. In case of O32 ABI all
+// relocation types occupy eight bit. In case of N64 ABI we extract first
+// relocation from 3-in-1 packet because only the first relocation can
+// be against a real symbol.
+static bool isMipsGprel(RelType Type) {
+ if (Config->EMachine != EM_MIPS)
return false;
- return Body.isPreemptible();
+ Type &= 0xff;
+ return Type == R_MIPS_GPREL16 || Type == R_MICROMIPS_GPREL16 ||
+ Type == R_MICROMIPS_GPREL7_S2;
}
// This function is similar to the `handleTlsRelocation`. MIPS does not
@@ -103,7 +108,7 @@ static bool isPreemptible(const SymbolBody &Body, uint32_t Type) {
// Mips has a custom MipsGotSection that handles the writing of GOT entries
// without dynamic relocations.
template <class ELFT>
-static unsigned handleMipsTlsRelocation(uint32_t Type, SymbolBody &Body,
+static unsigned handleMipsTlsRelocation(RelType Type, SymbolBody &Body,
InputSectionBase &C, uint64_t Offset,
int64_t Addend, RelExpr Expr) {
if (Expr == R_MIPS_TLSLD) {
@@ -116,11 +121,11 @@ static unsigned handleMipsTlsRelocation(uint32_t Type, SymbolBody &Body,
}
if (Expr == R_MIPS_TLSGD) {
- if (InX::MipsGot->addDynTlsEntry(Body) && Body.isPreemptible()) {
+ if (InX::MipsGot->addDynTlsEntry(Body) && Body.IsPreemptible) {
uint64_t Off = InX::MipsGot->getGlobalDynOffset(Body);
In<ELFT>::RelaDyn->addReloc(
{Target->TlsModuleIndexRel, InX::MipsGot, Off, false, &Body, 0});
- if (Body.isPreemptible())
+ if (Body.IsPreemptible)
In<ELFT>::RelaDyn->addReloc({Target->TlsOffsetRel, InX::MipsGot,
Off + Config->Wordsize, false, &Body, 0});
}
@@ -145,16 +150,16 @@ static unsigned handleMipsTlsRelocation(uint32_t Type, SymbolBody &Body,
// GOT[e0] Module Index (Used to find pointer to TLS block at run-time)
// GOT[e1] Offset of symbol in TLS block
template <class ELFT>
-static unsigned handleARMTlsRelocation(uint32_t Type, SymbolBody &Body,
+static unsigned handleARMTlsRelocation(RelType Type, SymbolBody &Body,
InputSectionBase &C, uint64_t Offset,
int64_t Addend, RelExpr Expr) {
// The Dynamic TLS Module Index Relocation for a symbol defined in an
- // executable is always 1. If the target Symbol is not preemtible then
+ // executable is always 1. If the target Symbol is not preemptible then
// we know the offset into the TLS block at static link time.
- bool NeedDynId = Body.isPreemptible() || Config->Shared;
- bool NeedDynOff = Body.isPreemptible();
+ bool NeedDynId = Body.IsPreemptible || Config->Shared;
+ bool NeedDynOff = Body.IsPreemptible;
- auto AddTlsReloc = [&](uint64_t Off, uint32_t Type, SymbolBody *Dest,
+ auto AddTlsReloc = [&](uint64_t Off, RelType Type, SymbolBody *Dest,
bool Dyn) {
if (Dyn)
In<ELFT>::RelaDyn->addReloc({Type, InX::Got, Off, false, Dest, 0});
@@ -192,7 +197,7 @@ static unsigned handleARMTlsRelocation(uint32_t Type, SymbolBody &Body,
// Returns the number of relocations processed.
template <class ELFT>
static unsigned
-handleTlsRelocation(uint32_t Type, SymbolBody &Body, InputSectionBase &C,
+handleTlsRelocation(RelType Type, SymbolBody &Body, InputSectionBase &C,
typename ELFT::uint Offset, int64_t Addend, RelExpr Expr) {
if (!(C.Flags & SHF_ALLOC))
return 0;
@@ -205,13 +210,12 @@ handleTlsRelocation(uint32_t Type, SymbolBody &Body, InputSectionBase &C,
if (Config->EMachine == EM_MIPS)
return handleMipsTlsRelocation<ELFT>(Type, Body, C, Offset, Addend, Expr);
- bool IsPreemptible = isPreemptible(Body, Type);
if (isRelExprOneOf<R_TLSDESC, R_TLSDESC_PAGE, R_TLSDESC_CALL>(Expr) &&
Config->Shared) {
if (InX::Got->addDynTlsEntry(Body)) {
uint64_t Off = InX::Got->getGlobalDynOffset(Body);
In<ELFT>::RelaDyn->addReloc(
- {Target->TlsDescRel, InX::Got, Off, !IsPreemptible, &Body, 0});
+ {Target->TlsDescRel, InX::Got, Off, !Body.IsPreemptible, &Body, 0});
}
if (Expr != R_TLSDESC_CALL)
C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
@@ -251,7 +255,7 @@ handleTlsRelocation(uint32_t Type, SymbolBody &Body, InputSectionBase &C,
// If the symbol is preemptible we need the dynamic linker to write
// the offset too.
uint64_t OffsetOff = Off + Config->Wordsize;
- if (IsPreemptible)
+ if (Body.IsPreemptible)
In<ELFT>::RelaDyn->addReloc(
{Target->TlsOffsetRel, InX::Got, OffsetOff, false, &Body, 0});
else
@@ -264,7 +268,7 @@ handleTlsRelocation(uint32_t Type, SymbolBody &Body, InputSectionBase &C,
// Global-Dynamic relocs can be relaxed to Initial-Exec or Local-Exec
// depending on the symbol being locally defined or not.
- if (IsPreemptible) {
+ if (Body.IsPreemptible) {
C.Relocations.push_back(
{Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_IE), Type,
Offset, Addend, &Body});
@@ -276,7 +280,7 @@ handleTlsRelocation(uint32_t Type, SymbolBody &Body, InputSectionBase &C,
} else {
C.Relocations.push_back(
{Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_LE), Type,
- Offset, Addend, &Body});
+ Offset, Addend, &Body});
}
return Target->TlsGdRelaxSkip;
}
@@ -284,7 +288,7 @@ handleTlsRelocation(uint32_t Type, SymbolBody &Body, InputSectionBase &C,
// Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally
// defined.
if (isRelExprOneOf<R_GOT, R_GOT_FROM_END, R_GOT_PC, R_GOT_PAGE_PC>(Expr) &&
- !Config->Shared && !IsPreemptible) {
+ !Config->Shared && !Body.IsPreemptible) {
C.Relocations.push_back(
{R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Body});
return 1;
@@ -295,12 +299,22 @@ handleTlsRelocation(uint32_t Type, SymbolBody &Body, InputSectionBase &C,
return 0;
}
-static uint32_t getMipsPairType(uint32_t Type, const SymbolBody &Sym) {
+static RelType getMipsPairType(RelType Type, bool IsLocal) {
switch (Type) {
case R_MIPS_HI16:
return R_MIPS_LO16;
case R_MIPS_GOT16:
- return Sym.isLocal() ? R_MIPS_LO16 : R_MIPS_NONE;
+ // In case of global symbol, the R_MIPS_GOT16 relocation does not
+ // have a pair. Each global symbol has a unique entry in the GOT
+ // and a corresponding instruction with help of the R_MIPS_GOT16
+ // relocation loads an address of the symbol. In case of local
+ // symbol, the R_MIPS_GOT16 relocation creates a GOT entry to hold
+ // the high 16 bits of the symbol's value. A paired R_MIPS_LO16
+ // relocations handle low 16 bits of the address. That allows
+ // to allocate only one GOT entry for every 64 KBytes of local data.
+ return IsLocal ? R_MIPS_LO16 : R_MIPS_NONE;
+ case R_MICROMIPS_GOT16:
+ return IsLocal ? R_MICROMIPS_LO16 : R_MIPS_NONE;
case R_MIPS_PCHI16:
return R_MIPS_PCLO16;
case R_MICROMIPS_HI16:
@@ -313,8 +327,8 @@ static uint32_t getMipsPairType(uint32_t Type, const SymbolBody &Sym) {
// True if non-preemptable symbol always has the same value regardless of where
// the DSO is loaded.
static bool isAbsolute(const SymbolBody &Body) {
- if (Body.isUndefined())
- return !Body.isLocal() && Body.symbol()->isWeak();
+ if (Body.isUndefWeak())
+ return true;
if (const auto *DR = dyn_cast<DefinedRegular>(&Body))
return DR->Section == nullptr; // Absolute symbol.
return false;
@@ -355,13 +369,13 @@ static bool isRelExpr(RelExpr Expr) {
// If this function returns false, that means we need to emit a
// dynamic relocation so that the relocation will be fixed at load-time.
template <class ELFT>
-static bool isStaticLinkTimeConstant(RelExpr E, uint32_t Type,
+static bool isStaticLinkTimeConstant(RelExpr E, RelType Type,
const SymbolBody &Body,
InputSectionBase &S, uint64_t RelOff) {
// These expressions always compute a constant
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_GOT_GP_PC,
- R_MIPS_TLSGD, R_GOT_PAGE_PC, R_GOT_PC,
+ R_MIPS_TLSGD, R_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC,
R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_PC, R_TLSGD,
R_PPC_PLT_OPD, R_TLSDESC_CALL, R_TLSDESC_PAGE, R_HINT>(E))
return true;
@@ -371,7 +385,7 @@ static bool isStaticLinkTimeConstant(RelExpr E, uint32_t Type,
if (E == R_GOT || E == R_PLT || E == R_TLSDESC)
return Target->usesOnlyLowPageBits(Type) || !Config->Pic;
- if (isPreemptible(Body, Type))
+ if (Body.IsPreemptible)
return false;
if (!Config->Pic)
return true;
@@ -396,7 +410,7 @@ static bool isStaticLinkTimeConstant(RelExpr E, uint32_t Type,
// between start of a function and '_gp' value and defined as absolute just
// to simplify the code.
assert(AbsVal && RelE);
- if (Body.isUndefined() && !Body.isLocal() && Body.symbol()->isWeak())
+ if (Body.isUndefWeak())
return true;
error("relocation " + toString(Type) + " cannot refer to absolute symbol: " +
@@ -434,7 +448,7 @@ template <class ELFT> static bool isReadOnly(SharedSymbol *SS) {
uint64_t Value = SS->getValue<ELFT>();
// Determine if the symbol is read-only by scanning the DSO's program headers.
- auto *File = cast<SharedFile<ELFT>>(SS->File);
+ const SharedFile<ELFT> *File = SS->getFile<ELFT>();
for (const Elf_Phdr &Phdr : check(File->getObj().program_headers()))
if ((Phdr.p_type == ELF::PT_LOAD || Phdr.p_type == ELF::PT_GNU_RELRO) &&
!(Phdr.p_flags & ELF::PF_W) && Value >= Phdr.p_vaddr &&
@@ -452,16 +466,16 @@ template <class ELFT>
static std::vector<SharedSymbol *> getSymbolsAt(SharedSymbol *SS) {
typedef typename ELFT::Sym Elf_Sym;
- auto *File = cast<SharedFile<ELFT>>(SS->File);
+ SharedFile<ELFT> *File = SS->getFile<ELFT>();
uint64_t Shndx = SS->getShndx<ELFT>();
uint64_t Value = SS->getValue<ELFT>();
std::vector<SharedSymbol *> Ret;
- for (const Elf_Sym &S : File->getGlobalSymbols()) {
+ for (const Elf_Sym &S : File->getGlobalELFSyms()) {
if (S.st_shndx != Shndx || S.st_value != Value)
continue;
StringRef Name = check(S.getName(File->getStringTable()));
- SymbolBody *Sym = Symtab<ELFT>::X->find(Name);
+ SymbolBody *Sym = Symtab->find(Name);
if (auto *Alias = dyn_cast_or_null<SharedSymbol>(Sym))
Ret.push_back(Alias);
}
@@ -519,46 +533,67 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol *SS) {
// See if this symbol is in a read-only segment. If so, preserve the symbol's
// memory protection by reserving space in the .bss.rel.ro section.
bool IsReadOnly = isReadOnly<ELFT>(SS);
- BssSection *Sec = IsReadOnly ? InX::BssRelRo : InX::Bss;
- uint64_t Off = Sec->reserveSpace(SymSize, SS->getAlignment<ELFT>());
+ BssSection *Sec = make<BssSection>(IsReadOnly ? ".bss.rel.ro" : ".bss",
+ SymSize, SS->getAlignment<ELFT>());
+ if (IsReadOnly)
+ InX::BssRelRo->getParent()->addSection(Sec);
+ else
+ InX::Bss->getParent()->addSection(Sec);
// 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 (SharedSymbol *Sym : getSymbolsAt<ELFT>(SS)) {
- Sym->NeedsCopy = true;
Sym->CopyRelSec = Sec;
- Sym->CopyRelSecOff = Off;
+ Sym->IsPreemptible = false;
Sym->symbol()->IsUsedInRegularObj = true;
}
- In<ELFT>::RelaDyn->addReloc({Target->CopyRel, Sec, Off, false, SS, 0});
+ In<ELFT>::RelaDyn->addReloc({Target->CopyRel, Sec, 0, false, SS, 0});
+}
+
+static void errorOrWarn(const Twine &Msg) {
+ if (!Config->NoinhibitExec)
+ error(Msg);
+ else
+ warn(Msg);
}
template <class ELFT>
-static RelExpr adjustExpr(SymbolBody &Body, RelExpr Expr, uint32_t Type,
- const uint8_t *Data, InputSectionBase &S,
- typename ELFT::uint RelOff) {
- if (Body.isGnuIFunc()) {
- Expr = toPlt(Expr);
- } else if (!isPreemptible(Body, Type)) {
- if (needsPlt(Expr))
- Expr = fromPlt(Expr);
- if (Expr == R_GOT_PC && !isAbsoluteValue(Body))
- Expr = Target->adjustRelaxExpr(Type, Data, Expr);
- }
+static RelExpr adjustExpr(SymbolBody &Body, RelExpr Expr, RelType Type,
+ InputSectionBase &S, uint64_t RelOff) {
+ // We can create any dynamic relocation if a section is simply writable.
+ if (S.Flags & SHF_WRITE)
+ return Expr;
+
+ // Or, if we are allowed to create dynamic relocations against
+ // read-only sections (i.e. unless "-z notext" is given),
+ // we can create a dynamic relocation as we want, too.
+ if (!Config->ZText)
+ return Expr;
+
+ // If a relocation can be applied at link-time, we don't need to
+ // create a dynamic relocation in the first place.
+ if (isStaticLinkTimeConstant<ELFT>(Expr, Type, Body, S, RelOff))
+ return Expr;
+
+ // If we got here we know that this relocation would require the dynamic
+ // linker to write a value to read only memory.
- bool IsWrite = !Config->ZText || (S.Flags & SHF_WRITE);
- if (IsWrite || isStaticLinkTimeConstant<ELFT>(Expr, Type, Body, S, RelOff))
+ // If the relocation is to a weak undef, give up on it and produce a
+ // non preemptible 0.
+ if (Body.isUndefWeak()) {
+ Body.IsPreemptible = false;
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
+ // 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 " + toString(Type) + " against " +
- (Body.getName().empty() ? "local symbol in readonly segment"
+ (Body.getName().empty() ? "local symbol"
: "symbol: " + toString(Body)) +
+ " in readonly segment; recompile object files with -fPIC" +
getLocation<ELFT>(S, Body, RelOff));
return Expr;
}
@@ -572,7 +607,7 @@ static RelExpr adjustExpr(SymbolBody &Body, RelExpr Expr, uint32_t Type,
if (Body.isObject()) {
// Produce a copy relocation.
auto *B = cast<SharedSymbol>(&Body);
- if (!B->NeedsCopy) {
+ if (!B->CopyRelSec) {
if (Config->ZNocopyreloc)
error("unresolvable relocation " + toString(Type) +
" against symbol '" + toString(*B) +
@@ -606,38 +641,24 @@ static RelExpr adjustExpr(SymbolBody &Body, RelExpr Expr, uint32_t Type,
// plt. That is identified by special relocation types (R_X86_64_JUMP_SLOT,
// R_386_JMP_SLOT, etc).
Body.NeedsPltAddr = true;
+ Body.IsPreemptible = false;
return toPlt(Expr);
}
- error("symbol '" + toString(Body) + "' defined in " + toString(Body.File) +
- " has no type");
+ errorOrWarn("symbol '" + toString(Body) + "' defined in " +
+ toString(Body.getFile()) + " has no type");
return Expr;
}
-// Returns an addend of a given relocation. If it is RELA, an addend
-// is in a relocation itself. If it is REL, we need to read it from an
-// input section.
-template <class ELFT, class RelTy>
-static int64_t computeAddend(const RelTy &Rel, const uint8_t *Buf) {
- uint32_t Type = Rel.getType(Config->IsMips64EL);
- int64_t A = RelTy::IsRela
- ? getAddend<ELFT>(Rel)
- : Target->getImplicitAddend(Buf + Rel.r_offset, Type);
-
- if (Config->EMachine == EM_PPC64 && Config->Pic && Type == R_PPC64_TOC)
- A += getPPC64TocBase();
- return A;
-}
-
// MIPS has an odd notion of "paired" relocations to calculate addends.
// For example, if a relocation is of R_MIPS_HI16, there must be a
// R_MIPS_LO16 relocation after that, and an addend is calculated using
// the two relocations.
template <class ELFT, class RelTy>
-static int64_t computeMipsAddend(const RelTy &Rel, InputSectionBase &Sec,
- RelExpr Expr, SymbolBody &Body,
- const RelTy *End) {
- if (Expr == R_MIPS_GOTREL && Body.isLocal())
+static int64_t computeMipsAddend(const RelTy &Rel, const RelTy *End,
+ InputSectionBase &Sec, RelExpr Expr,
+ bool IsLocal) {
+ if (Expr == R_MIPS_GOTREL && IsLocal)
return Sec.getFile<ELFT>()->MipsGp0;
// The ABI says that the paired relocation is used only for REL.
@@ -645,8 +666,8 @@ static int64_t computeMipsAddend(const RelTy &Rel, InputSectionBase &Sec,
if (RelTy::IsRela)
return 0;
- uint32_t Type = Rel.getType(Config->IsMips64EL);
- uint32_t PairTy = getMipsPairType(Type, Body);
+ RelType Type = Rel.getType(Config->IsMips64EL);
+ uint32_t PairTy = getMipsPairType(Type, IsLocal);
if (PairTy == R_MIPS_NONE)
return 0;
@@ -655,64 +676,88 @@ static int64_t computeMipsAddend(const RelTy &Rel, InputSectionBase &Sec,
// To make things worse, paired relocations might not be contiguous in
// the relocation table, so we need to do linear search. *sigh*
- for (const RelTy *RI = &Rel; RI != End; ++RI) {
- if (RI->getType(Config->IsMips64EL) != PairTy)
- continue;
- if (RI->getSymbol(Config->IsMips64EL) != SymIndex)
- continue;
-
- endianness E = Config->Endianness;
- int32_t Hi = (read32(Buf + Rel.r_offset, E) & 0xffff) << 16;
- int32_t Lo = SignExtend32<16>(read32(Buf + RI->r_offset, E));
- return Hi + Lo;
- }
+ for (const RelTy *RI = &Rel; RI != End; ++RI)
+ if (RI->getType(Config->IsMips64EL) == PairTy &&
+ RI->getSymbol(Config->IsMips64EL) == SymIndex)
+ return Target->getImplicitAddend(Buf + RI->r_offset, PairTy);
warn("can't find matching " + toString(PairTy) + " relocation for " +
toString(Type));
return 0;
}
+// Returns an addend of a given relocation. If it is RELA, an addend
+// is in a relocation itself. If it is REL, we need to read it from an
+// input section.
+template <class ELFT, class RelTy>
+static int64_t computeAddend(const RelTy &Rel, const RelTy *End,
+ InputSectionBase &Sec, RelExpr Expr,
+ bool IsLocal) {
+ int64_t Addend;
+ RelType Type = Rel.getType(Config->IsMips64EL);
+
+ if (RelTy::IsRela) {
+ Addend = getAddend<ELFT>(Rel);
+ } else {
+ const uint8_t *Buf = Sec.Data.data();
+ Addend = Target->getImplicitAddend(Buf + Rel.r_offset, Type);
+ }
+
+ if (Config->EMachine == EM_PPC64 && Config->Pic && Type == R_PPC64_TOC)
+ Addend += getPPC64TocBase();
+ if (Config->EMachine == EM_MIPS)
+ Addend += computeMipsAddend<ELFT>(Rel, End, Sec, Expr, IsLocal);
+
+ return Addend;
+}
+
+// Report an undefined symbol if necessary.
+// Returns true if this function printed out an error message.
template <class ELFT>
-static void reportUndefined(SymbolBody &Sym, InputSectionBase &S,
- uint64_t Offset) {
+static bool maybeReportUndefined(SymbolBody &Sym, InputSectionBase &Sec,
+ uint64_t Offset) {
if (Config->UnresolvedSymbols == UnresolvedPolicy::IgnoreAll)
- return;
+ return false;
+
+ if (Sym.isLocal() || !Sym.isUndefined() || Sym.symbol()->isWeak())
+ return false;
bool CanBeExternal = Sym.symbol()->computeBinding() != STB_LOCAL &&
Sym.getVisibility() == STV_DEFAULT;
if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore && CanBeExternal)
- return;
+ return false;
std::string Msg =
"undefined symbol: " + toString(Sym) + "\n>>> referenced by ";
- std::string Src = S.getSrcMsg<ELFT>(Offset);
+ std::string Src = Sec.getSrcMsg<ELFT>(Offset);
if (!Src.empty())
Msg += Src + "\n>>> ";
- Msg += S.getObjMsg<ELFT>(Offset);
+ Msg += Sec.getObjMsg<ELFT>(Offset);
- if (Config->UnresolvedSymbols == UnresolvedPolicy::WarnAll ||
- (Config->UnresolvedSymbols == UnresolvedPolicy::Warn && CanBeExternal)) {
+ if ((Config->UnresolvedSymbols == UnresolvedPolicy::Warn && CanBeExternal) ||
+ Config->NoinhibitExec) {
warn(Msg);
- } else {
- error(Msg);
+ return false;
}
+
+ error(Msg);
+ return true;
}
-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->IsMips64EL) << (8 * Processed);
- }
- return std::make_pair(Type, Processed);
+// 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.
+template <class RelTy> static RelType getMipsN32RelType(RelTy *&Rel, RelTy *End) {
+ RelType Type = Rel->getType(Config->IsMips64EL);
+ uint64_t Offset = Rel->r_offset;
+
+ int N = 0;
+ while (Rel + 1 != End && (Rel + 1)->r_offset == Offset)
+ Type |= (++Rel)->getType(Config->IsMips64EL) << (8 * ++N);
+ return Type;
}
// .eh_frame sections are mergeable input sections, so their input
@@ -730,43 +775,40 @@ namespace {
class OffsetGetter {
public:
explicit OffsetGetter(InputSectionBase &Sec) {
- if (auto *Eh = dyn_cast<EhInputSection>(&Sec)) {
- P = Eh->Pieces;
- Size = Eh->Pieces.size();
- }
+ if (auto *Eh = dyn_cast<EhInputSection>(&Sec))
+ Pieces = Eh->Pieces;
}
// Translates offsets in input sections to offsets in output sections.
- // Given offset must increase monotonically. We assume that P is
+ // Given offset must increase monotonically. We assume that Piece is
// sorted by InputOff.
uint64_t get(uint64_t Off) {
- if (P.empty())
+ if (Pieces.empty())
return Off;
- while (I != Size && P[I].InputOff + P[I].size() <= Off)
+ while (I != Pieces.size() && Pieces[I].InputOff + Pieces[I].Size <= Off)
++I;
- if (I == Size)
+ if (I == Pieces.size())
return Off;
- // P must be contiguous, so there must be no holes in between.
- assert(P[I].InputOff <= Off && "Relocation not in any piece");
+ // Pieces must be contiguous, so there must be no holes in between.
+ assert(Pieces[I].InputOff <= Off && "Relocation not in any piece");
// Offset -1 means that the piece is dead (i.e. garbage collected).
- if (P[I].OutputOff == -1)
+ if (Pieces[I].OutputOff == -1)
return -1;
- return P[I].OutputOff + Off - P[I].InputOff;
+ return Pieces[I].OutputOff + Off - Pieces[I].InputOff;
}
private:
- ArrayRef<EhSectionPiece> P;
+ ArrayRef<EhSectionPiece> Pieces;
size_t I = 0;
- size_t Size;
};
} // namespace
template <class ELFT, class GotPltSection>
static void addPltEntry(PltSection *Plt, GotPltSection *GotPlt,
- RelocationSection<ELFT> *Rel, uint32_t Type,
+ RelocationSection<ELFT> *Rel, RelType Type,
SymbolBody &Sym, bool UseSymVA) {
Plt->addEntry<ELFT>(Sym);
GotPlt->addEntry(Sym);
@@ -777,26 +819,41 @@ template <class ELFT>
static void addGotEntry(SymbolBody &Sym, bool Preemptible) {
InX::Got->addEntry(Sym);
+ RelExpr Expr = Sym.isTls() ? R_TLS : R_ABS;
uint64_t Off = Sym.getGotOffset();
- uint32_t DynType;
- RelExpr Expr = R_ABS;
-
- if (Sym.isTls()) {
- DynType = Target->TlsGotRel;
- Expr = R_TLS;
- } else if (!Preemptible && Config->Pic && !isAbsolute(Sym)) {
- DynType = Target->RelativeRel;
- } else {
- DynType = Target->GotRel;
- }
- bool Constant = !Preemptible && !(Config->Pic && !isAbsolute(Sym));
- if (!Constant)
- In<ELFT>::RelaDyn->addReloc(
- {DynType, InX::Got, Off, !Preemptible, &Sym, 0});
+ // If a GOT slot value can be calculated at link-time, which is now,
+ // we can just fill that out.
+ //
+ // (We don't actually write a value to a GOT slot right now, but we
+ // add a static relocation to a Relocations vector so that
+ // InputSection::relocate will do the work for us. We may be able
+ // to just write a value now, but it is a TODO.)
+ bool IsLinkTimeConstant = !Preemptible && (!Config->Pic || isAbsolute(Sym));
+ if (IsLinkTimeConstant) {
+ InX::Got->Relocations.push_back({Expr, Target->GotRel, Off, 0, &Sym});
+ return;
+ }
- if (Constant || (!Config->IsRela && !Preemptible))
- InX::Got->Relocations.push_back({Expr, DynType, Off, 0, &Sym});
+ // Otherwise, we emit a dynamic relocation to .rel[a].dyn so that
+ // the GOT slot will be fixed at load-time.
+ RelType Type;
+ if (Sym.isTls())
+ Type = Target->TlsGotRel;
+ else if (!Preemptible && Config->Pic && !isAbsolute(Sym))
+ Type = Target->RelativeRel;
+ else
+ Type = Target->GotRel;
+ In<ELFT>::RelaDyn->addReloc({Type, InX::Got, Off, !Preemptible, &Sym, 0});
+
+ // REL type relocations don't have addend fields unlike RELAs, and
+ // their addends are stored to the section to which they are applied.
+ // So, store addends if we need to.
+ //
+ // This is ugly -- the difference between REL and RELA should be
+ // handled in a better way. It's a TODO.
+ if (!Config->IsRela)
+ InX::Got->Relocations.push_back({R_ABS, Target->GotRel, Off, 0, &Sym});
}
// The reason we have to do this early scan is as follows
@@ -819,26 +876,20 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
for (auto I = Rels.begin(), End = Rels.end(); I != End; ++I) {
const RelTy &Rel = *I;
SymbolBody &Body = Sec.getFile<ELFT>()->getRelocTargetSym(Rel);
- uint32_t Type = Rel.getType(Config->IsMips64EL);
+ RelType Type = Rel.getType(Config->IsMips64EL);
- if (Config->MipsN32Abi) {
- uint32_t Processed;
- std::tie(Type, Processed) =
- mergeMipsN32RelTypes(Type, Rel.r_offset, I + 1, End);
- I += Processed;
- }
+ // Deal with MIPS oddity.
+ if (Config->MipsN32Abi)
+ Type = getMipsN32RelType(I, End);
- // Compute the offset of this section in the output section.
+ // Get an offset in an output section this relocation is applied to.
uint64_t Offset = GetOffset.get(Rel.r_offset);
if (Offset == uint64_t(-1))
continue;
- // Report undefined symbols. The fact that we report undefined
- // symbols here means that we report undefined symbols only when
- // they have relocations pointing to them. We don't care about
- // undefined symbols that are in dead-stripped sections.
- if (!Body.isLocal() && Body.isUndefined() && !Body.symbol()->isWeak())
- reportUndefined<ELFT>(Body, Sec, Rel.r_offset);
+ // Skip if the target symbol is an erroneous undefined symbol.
+ if (maybeReportUndefined<ELFT>(Body, Sec, Rel.r_offset))
+ continue;
RelExpr Expr =
Target->getRelExpr(Type, Body, Sec.Data.begin() + Rel.r_offset);
@@ -847,9 +898,35 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
if (isRelExprOneOf<R_HINT, R_NONE>(Expr))
continue;
- bool Preemptible = isPreemptible(Body, Type);
- Expr = adjustExpr<ELFT>(Body, Expr, Type, Sec.Data.data() + Rel.r_offset,
- Sec, Rel.r_offset);
+ // Handle yet another MIPS-ness.
+ if (isMipsGprel(Type)) {
+ int64_t Addend = computeAddend<ELFT>(Rel, End, Sec, Expr, Body.isLocal());
+ Sec.Relocations.push_back({R_MIPS_GOTREL, Type, Offset, Addend, &Body});
+ continue;
+ }
+
+ bool Preemptible = Body.IsPreemptible;
+
+ // Strenghten or relax a PLT access.
+ //
+ // GNU ifunc symbols must be accessed via PLT because their addresses
+ // are determined by runtime.
+ //
+ // On the other hand, if we know that a PLT entry will be resolved within
+ // the same ELF module, we can skip PLT access and directly jump to the
+ // destination function. For example, if we are linking a main exectuable,
+ // all dynamic symbols that can be resolved within the executable will
+ // actually be resolved that way at runtime, because the main exectuable
+ // is always at the beginning of a search list. We can leverage that fact.
+ if (Body.isGnuIFunc())
+ Expr = toPlt(Expr);
+ else if (!Preemptible && Expr == R_GOT_PC && !isAbsoluteValue(Body))
+ Expr =
+ Target->adjustRelaxExpr(Type, Sec.Data.data() + Rel.r_offset, Expr);
+ else if (!Preemptible)
+ Expr = fromPlt(Expr);
+
+ Expr = adjustExpr<ELFT>(Body, Expr, Type, Sec, Rel.r_offset);
if (ErrorCount)
continue;
@@ -860,9 +937,7 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
InX::Got->HasGotOffRel = true;
// Read an addend.
- int64_t Addend = computeAddend<ELFT>(Rel, Sec.Data.data());
- if (Config->EMachine == EM_MIPS)
- Addend += computeMipsAddend<ELFT>(Rel, Sec, Expr, Body, End);
+ int64_t Addend = computeAddend<ELFT>(Rel, End, Sec, Expr, Body.isLocal());
// Process some TLS relocations, including relaxing TLS relocations.
// Note that this function does not handle all TLS relocations.
@@ -893,7 +968,7 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
// for detailed description:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
InX::MipsGot->addEntry(Body, Addend, Expr);
- if (Body.isTls() && Body.isPreemptible())
+ if (Body.isTls() && Body.IsPreemptible)
In<ELFT>::RelaDyn->addReloc({Target->TlsGotRel, InX::MipsGot,
Body.getGotOffset(), false, &Body, 0});
} else if (!Body.isInGot()) {
@@ -901,13 +976,14 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
}
}
- if (!needsPlt(Expr) && !needsGot(Expr) && isPreemptible(Body, Type)) {
+ if (!needsPlt(Expr) && !needsGot(Expr) && Body.IsPreemptible) {
// We don't know anything about the finaly symbol. Just ask the dynamic
// linker to handle the relocation for us.
if (!Target->isPicRel(Type))
- error("relocation " + toString(Type) +
- " cannot be used against shared object; recompile with -fPIC" +
- getLocation<ELFT>(Sec, Body, Offset));
+ errorOrWarn(
+ "relocation " + toString(Type) +
+ " cannot be used against shared object; recompile with -fPIC" +
+ getLocation<ELFT>(Sec, Body, Offset));
In<ELFT>::RelaDyn->addReloc(
{Target->getDynRel(Type), &Sec, Offset, false, &Body, Addend});
@@ -940,20 +1016,28 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
if (Expr == R_SIZE)
Addend += Body.getSize<ELFT>();
+ // 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 (IsConstant) {
+ Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
+ continue;
+ }
+
// 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
// dynamic linker. We can however do better than just copying the incoming
// relocation. We can process some of it and and just ask the dynamic
// linker to add the load address.
- if (!IsConstant)
+ if (Config->IsRela) {
In<ELFT>::RelaDyn->addReloc(
{Target->RelativeRel, &Sec, 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 (IsConstant || !RelTy::IsRela)
+ } else {
+ // In REL, addends are stored to the target section.
+ In<ELFT>::RelaDyn->addReloc(
+ {Target->RelativeRel, &Sec, Offset, true, &Body, 0});
Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
+ }
}
}
@@ -1000,31 +1084,37 @@ void ThunkCreator::mergeThunks() {
}
}
+static uint32_t findEndOfFirstNonExec(OutputSection &Cmd) {
+ for (BaseCommand *Base : Cmd.SectionCommands)
+ if (auto *ISD = dyn_cast<InputSectionDescription>(Base))
+ for (auto *IS : ISD->Sections)
+ if ((IS->Flags & SHF_EXECINSTR) == 0)
+ return IS->OutSecOff + IS->getSize();
+ return 0;
+}
+
ThunkSection *ThunkCreator::getOSThunkSec(OutputSection *OS,
std::vector<InputSection *> *ISR) {
if (CurTS == nullptr) {
- uint32_t Off = 0;
- for (auto *IS : OS->Sections) {
- Off = IS->OutSecOff + IS->getSize();
- if ((IS->Flags & SHF_EXECINSTR) == 0)
- break;
- }
+ uint32_t Off = findEndOfFirstNonExec(*OS);
CurTS = addThunkSection(OS, ISR, Off);
}
return CurTS;
}
-ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS, OutputSection *OS) {
+// Add a Thunk that needs to be placed in a ThunkSection that immediately
+// precedes its Target.
+ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS) {
ThunkSection *TS = ThunkedSections.lookup(IS);
if (TS)
return TS;
- auto *TOS = IS->getParent();
- // Find InputSectionRange within TOS that IS is in
- OutputSectionCommand *C = Script->getCmd(TOS);
+ // Find InputSectionRange within Target Output Section (TOS) that the
+ // InputSection (IS) that we need to precede is in.
+ OutputSection *TOS = IS->getParent();
std::vector<InputSection *> *Range = nullptr;
- for (BaseCommand *BC : C->Commands)
- if (auto *ISD = dyn_cast<InputSectionDescription> (BC)) {
+ for (BaseCommand *BC : TOS->SectionCommands)
+ if (auto *ISD = dyn_cast<InputSectionDescription>(BC)) {
InputSection *first = ISD->Sections.front();
InputSection *last = ISD->Sections.back();
if (IS->OutSecOff >= first->OutSecOff &&
@@ -1046,33 +1136,37 @@ ThunkSection *ThunkCreator::addThunkSection(OutputSection *OS,
return TS;
}
-
std::pair<Thunk *, bool> ThunkCreator::getThunk(SymbolBody &Body,
- uint32_t Type) {
- auto res = ThunkedSymbols.insert({&Body, nullptr});
- if (res.second)
- res.first->second = addThunk(Type, Body);
- return std::make_pair(res.first->second, res.second);
+ RelType Type) {
+ auto Res = ThunkedSymbols.insert({&Body, std::vector<Thunk *>()});
+ if (!Res.second) {
+ // Check existing Thunks for Body to see if they can be reused
+ for (Thunk *ET : Res.first->second)
+ if (ET->isCompatibleWith(Type))
+ return std::make_pair(ET, false);
+ }
+ // No existing compatible Thunk in range, create a new one
+ Thunk *T = addThunk(Type, Body);
+ Res.first->second.push_back(T);
+ return std::make_pair(T, true);
}
// Call Fn on every executable InputSection accessed via the linker script
// InputSectionDescription::Sections.
void ThunkCreator::forEachExecInputSection(
- ArrayRef<OutputSectionCommand *> OutputSections,
+ ArrayRef<OutputSection *> OutputSections,
std::function<void(OutputSection *, std::vector<InputSection *> *,
InputSection *)>
Fn) {
- for (OutputSectionCommand *Cmd : OutputSections) {
- OutputSection *OS = Cmd->Sec;
+ for (OutputSection *OS : OutputSections) {
if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR))
continue;
- if (OutputSectionCommand *C = Script->getCmd(OS))
- for (BaseCommand *BC : C->Commands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(BC)) {
- CurTS = nullptr;
- for (InputSection* IS : ISD->Sections)
- Fn(OS, &ISD->Sections, IS);
- }
+ for (BaseCommand *BC : OS->SectionCommands)
+ if (auto *ISD = dyn_cast<InputSectionDescription>(BC)) {
+ CurTS = nullptr;
+ for (InputSection *IS : ISD->Sections)
+ Fn(OS, &ISD->Sections, IS);
+ }
}
}
@@ -1086,8 +1180,7 @@ void ThunkCreator::forEachExecInputSection(
//
// FIXME: All Thunks are assumed to be in range of the relocation. Range
// extension Thunks are not yet supported.
-bool ThunkCreator::createThunks(
- ArrayRef<OutputSectionCommand *> OutputSections) {
+bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) {
if (Pass > 0)
ThunkSections.clear();
@@ -1097,32 +1190,32 @@ bool ThunkCreator::createThunks(
// We separate the creation of ThunkSections from the insertion of the
// ThunkSections back into the OutputSection as ThunkSections are not always
// inserted into the same OutputSection as the caller.
- forEachExecInputSection(
- OutputSections, [&](OutputSection *OS, std::vector<InputSection*> *ISR,
- InputSection *IS) {
- for (Relocation &Rel : IS->Relocations) {
- SymbolBody &Body = *Rel.Sym;
- if (Thunks.find(&Body) != Thunks.end() ||
- !Target->needsThunk(Rel.Expr, Rel.Type, IS->File, Body))
- continue;
- Thunk *T;
- bool IsNew;
- std::tie(T, IsNew) = getThunk(Body, Rel.Type);
- if (IsNew) {
- // Find or create a ThunkSection for the new Thunk
- ThunkSection *TS;
- if (auto *TIS = T->getTargetInputSection())
- TS = getISThunkSec(TIS, OS);
- else
- TS = getOSThunkSec(OS, ISR);
- TS->addThunk(T);
- Thunks[T->ThunkSym] = T;
- }
- // Redirect relocation to Thunk, we never go via the PLT to a Thunk
- Rel.Sym = T->ThunkSym;
- Rel.Expr = fromPlt(Rel.Expr);
- }
- });
+ forEachExecInputSection(OutputSections, [&](OutputSection *OS,
+ std::vector<InputSection *> *ISR,
+ InputSection *IS) {
+ for (Relocation &Rel : IS->Relocations) {
+ SymbolBody &Body = *Rel.Sym;
+ if (Thunks.find(&Body) != Thunks.end() ||
+ !Target->needsThunk(Rel.Expr, Rel.Type, IS->File, Body))
+ continue;
+ Thunk *T;
+ bool IsNew;
+ std::tie(T, IsNew) = getThunk(Body, Rel.Type);
+ if (IsNew) {
+ // Find or create a ThunkSection for the new Thunk
+ ThunkSection *TS;
+ if (auto *TIS = T->getTargetInputSection())
+ TS = getISThunkSec(TIS);
+ else
+ TS = getOSThunkSec(OS, ISR);
+ TS->addThunk(T);
+ Thunks[T->ThunkSym] = T;
+ }
+ // Redirect relocation to Thunk, we never go via the PLT to a Thunk
+ Rel.Sym = T->ThunkSym;
+ Rel.Expr = fromPlt(Rel.Expr);
+ }
+ });
// Merge all created synthetic ThunkSections back into OutputSection
mergeThunks();
++Pass;
diff --git a/ELF/Relocations.h b/ELF/Relocations.h
index 445308b27..5548a5583 100644
--- a/ELF/Relocations.h
+++ b/ELF/Relocations.h
@@ -10,7 +10,7 @@
#ifndef LLD_ELF_RELOCATIONS_H
#define LLD_ELF_RELOCATIONS_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include <map>
#include <vector>
@@ -21,12 +21,16 @@ class SymbolBody;
class InputSection;
class InputSectionBase;
class OutputSection;
-struct OutputSectionCommand;
+class OutputSection;
+
+// Represents a relocation type, such as R_X86_64_PC32 or R_ARM_THM_CALL.
+typedef uint32_t RelType;
// 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_INVALID,
R_ABS,
R_ARM_SBREL,
R_GOT,
@@ -103,14 +107,15 @@ struct RelExprMaskBuilder<Head, Tail...> {
// 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!");
+ 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;
+ RelType Type;
uint64_t Offset;
int64_t Addend;
SymbolBody *Sym;
@@ -124,7 +129,7 @@ class Thunk;
class ThunkCreator {
public:
// Return true if Thunks have been added to OutputSections
- bool createThunks(ArrayRef<OutputSectionCommand *> OutputSections);
+ bool createThunks(ArrayRef<OutputSection *> OutputSections);
// The number of completed passes of createThunks this permits us
// to do one time initialization on Pass 0 and put a limit on the
@@ -135,23 +140,26 @@ private:
void mergeThunks();
ThunkSection *getOSThunkSec(OutputSection *OS,
std::vector<InputSection *> *ISR);
- ThunkSection *getISThunkSec(InputSection *IS, OutputSection *OS);
+ ThunkSection *getISThunkSec(InputSection *IS);
void forEachExecInputSection(
- ArrayRef<OutputSectionCommand *> OutputSections,
+ ArrayRef<OutputSection *> OutputSections,
std::function<void(OutputSection *, std::vector<InputSection *> *,
InputSection *)>
Fn);
- std::pair<Thunk *, bool> getThunk(SymbolBody &Body, uint32_t Type);
+ std::pair<Thunk *, bool> getThunk(SymbolBody &Body, RelType Type);
ThunkSection *addThunkSection(OutputSection *OS,
std::vector<InputSection *> *, uint64_t Off);
- // Track Symbols that already have a Thunk
- llvm::DenseMap<SymbolBody *, Thunk *> ThunkedSymbols;
+ // Record all the available Thunks for a Symbol
+ llvm::DenseMap<SymbolBody *, std::vector<Thunk *>> ThunkedSymbols;
// Find a Thunk from the Thunks symbol definition, we can use this to find
// the Thunk from a relocation to the Thunks symbol definition.
llvm::DenseMap<SymbolBody *, Thunk *> Thunks;
- // Track InputSections that have a ThunkSection placed in front
+ // Track InputSections that have an inline ThunkSection placed in front
+ // an inline ThunkSection may have control fall through to the section below
+ // so we need to make sure that there is only one of them.
+ // The Mips LA25 Thunk is an example of an inline ThunkSection.
llvm::DenseMap<InputSection *, ThunkSection *> ThunkedSections;
// All the ThunkSections that we have created, organised by OutputSection
@@ -175,7 +183,7 @@ template <class ELFT>
static inline int64_t getAddend(const typename ELFT::Rela &Rel) {
return Rel.r_addend;
}
-}
-}
+} // namespace elf
+} // namespace lld
#endif
diff --git a/ELF/ScriptLexer.cpp b/ELF/ScriptLexer.cpp
index 86720de35..c5cfa5d2b 100644
--- a/ELF/ScriptLexer.cpp
+++ b/ELF/ScriptLexer.cpp
@@ -75,19 +75,14 @@ ScriptLexer::ScriptLexer(MemoryBufferRef MB) { tokenize(MB); }
// We don't want to record cascading errors. Keep only the first one.
void ScriptLexer::setError(const Twine &Msg) {
- if (Error)
+ if (ErrorCount)
return;
- Error = true;
- if (!Pos) {
- error(getCurrentLocation() + ": " + Msg);
- return;
- }
-
- std::string S = getCurrentLocation() + ": ";
- error(S + Msg);
- error(S + getLine());
- error(S + std::string(getColumnNumber(), ' ') + "^");
+ std::string S = (getCurrentLocation() + ": " + Msg).str();
+ if (Pos)
+ S += "\n>>> " + getLine().str() + "\n>>> " +
+ std::string(getColumnNumber(), ' ') + "^";
+ error(S);
}
// Split S into linker script tokens.
@@ -164,18 +159,18 @@ StringRef ScriptLexer::skipSpace(StringRef S) {
}
// An erroneous token is handled as if it were the last token before EOF.
-bool ScriptLexer::atEOF() { return Error || Tokens.size() == Pos; }
+bool ScriptLexer::atEOF() { return ErrorCount || Tokens.size() == Pos; }
// Split a given string as an expression.
// This function returns "3", "*" and "5" for "3*5" for example.
static std::vector<StringRef> tokenizeExpr(StringRef S) {
- StringRef Ops = "+-*/:"; // List of operators
+ StringRef Ops = "+-*/:!~"; // List of operators
// Quoted strings are literal strings, so we don't want to split it.
if (S.startswith("\""))
return {S};
- // Split S with +-*/ as separators.
+ // Split S with operators as separators.
std::vector<StringRef> Ret;
while (!S.empty()) {
size_t E = S.find_first_of(Ops);
@@ -190,9 +185,14 @@ static std::vector<StringRef> tokenizeExpr(StringRef S) {
if (E != 0)
Ret.push_back(S.substr(0, E));
- // Get the operator as a token.
- Ret.push_back(S.substr(E, 1));
- S = S.substr(E + 1);
+ // Get the operator as a token. Keep != as one token.
+ if (S.substr(E).startswith("!=")) {
+ Ret.push_back(S.substr(E, 2));
+ S = S.substr(E + 2);
+ } else {
+ Ret.push_back(S.substr(E, 1));
+ S = S.substr(E + 1);
+ }
}
return Ret;
}
@@ -207,7 +207,7 @@ static std::vector<StringRef> tokenizeExpr(StringRef S) {
//
// This function may split the current token into multiple tokens.
void ScriptLexer::maybeSplitExpr() {
- if (!InExpr || Error || atEOF())
+ if (!InExpr || ErrorCount || atEOF())
return;
std::vector<StringRef> V = tokenizeExpr(Tokens[Pos]);
@@ -220,7 +220,7 @@ void ScriptLexer::maybeSplitExpr() {
StringRef ScriptLexer::next() {
maybeSplitExpr();
- if (Error)
+ if (ErrorCount)
return "";
if (atEOF()) {
setError("unexpected EOF");
@@ -231,7 +231,7 @@ StringRef ScriptLexer::next() {
StringRef ScriptLexer::peek() {
StringRef Tok = next();
- if (Error)
+ if (ErrorCount)
return "";
Pos = Pos - 1;
return Tok;
@@ -260,7 +260,7 @@ bool ScriptLexer::consumeLabel(StringRef Tok) {
void ScriptLexer::skip() { (void)next(); }
void ScriptLexer::expect(StringRef Expect) {
- if (Error)
+ if (ErrorCount)
return;
StringRef Tok = next();
if (Tok != Expect)
diff --git a/ELF/ScriptLexer.h b/ELF/ScriptLexer.h
index 64d6d9204..e7c8b28e4 100644
--- a/ELF/ScriptLexer.h
+++ b/ELF/ScriptLexer.h
@@ -10,7 +10,7 @@
#ifndef LLD_ELF_SCRIPT_LEXER_H
#define LLD_ELF_SCRIPT_LEXER_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/MemoryBuffer.h"
#include <utility>
@@ -39,7 +39,6 @@ public:
std::vector<StringRef> Tokens;
bool InExpr = false;
size_t Pos = 0;
- bool Error = false;
private:
void maybeSplitExpr();
diff --git a/ELF/ScriptParser.cpp b/ELF/ScriptParser.cpp
index 4a44944fe..5582ce672 100644
--- a/ELF/ScriptParser.cpp
+++ b/ELF/ScriptParser.cpp
@@ -24,6 +24,7 @@
#include "Target.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Support/Casting.h"
@@ -55,7 +56,6 @@ public:
private:
void addFile(StringRef Path);
- OutputSection *checkSection(OutputSectionCommand *Cmd, StringRef Loccation);
void readAsNeeded();
void readEntry();
@@ -67,17 +67,18 @@ private:
void readOutputArch();
void readOutputFormat();
void readPhdrs();
+ void readRegionAlias();
void readSearchDir();
void readSections();
void readVersion();
void readVersionScriptCommand();
SymbolAssignment *readAssignment(StringRef Name);
- BytesDataCommand *readBytesDataCommand(StringRef Tok);
+ ByteCommand *readByteCommand(StringRef Tok);
uint32_t readFill();
uint32_t parseFill(StringRef Tok);
- void readSectionAddressType(OutputSectionCommand *Cmd);
- OutputSectionCommand *readOutputSectionDescription(StringRef OutSec);
+ void readSectionAddressType(OutputSection *Cmd);
+ OutputSection *readOutputSectionDescription(StringRef OutSec);
std::vector<StringRef> readOutputSectionPhdrs();
InputSectionDescription *readInputSectionDescription(StringRef Tok);
StringMatcher readFilePatterns();
@@ -90,6 +91,8 @@ private:
void readSort();
AssertCommand *readAssert();
Expr readAssertExpr();
+ Expr readConstant();
+ Expr getPageSize();
uint64_t readMemoryAssignment(StringRef, StringRef, StringRef);
std::pair<uint32_t, uint32_t> readMemoryAttributes();
@@ -109,10 +112,20 @@ private:
std::pair<std::vector<SymbolVersion>, std::vector<SymbolVersion>>
readSymbols();
+ // True if a script being read is in a subdirectory specified by -sysroot.
bool IsUnderSysroot;
+
+ // A set to detect an INCLUDE() cycle.
+ StringSet<> Seen;
};
} // namespace
+static StringRef unquote(StringRef S) {
+ if (S.startswith("\""))
+ return S.substr(1, S.size() - 2);
+ return S;
+}
+
static bool isUnderSysroot(StringRef Path) {
if (Config->Sysroot == "")
return false;
@@ -125,7 +138,7 @@ static bool isUnderSysroot(StringRef Path) {
// Some operations only support one non absolute value. Move the
// absolute one to the right hand side for convenience.
static void moveAbsRight(ExprValue &A, ExprValue &B) {
- if (A.isAbsolute())
+ if (A.Sec == nullptr || (A.ForceAbsolute && !B.isAbsolute()))
std::swap(A, B);
if (!B.isAbsolute())
error(A.Loc + ": at least one side of the expression must be absolute");
@@ -133,11 +146,11 @@ static void moveAbsRight(ExprValue &A, ExprValue &B) {
static ExprValue add(ExprValue A, ExprValue B) {
moveAbsRight(A, B);
- return {A.Sec, A.ForceAbsolute, A.Val + B.getValue(), A.Loc};
+ return {A.Sec, A.ForceAbsolute, A.getSectionOffset() + B.getValue(), A.Loc};
}
static ExprValue sub(ExprValue A, ExprValue B) {
- return {A.Sec, A.Val - B.getValue(), A.Loc};
+ return {A.Sec, false, A.getSectionOffset() - B.getValue(), A.Loc};
}
static ExprValue mul(ExprValue A, ExprValue B) {
@@ -164,10 +177,24 @@ static ExprValue bitOr(ExprValue A, ExprValue B) {
}
void ScriptParser::readDynamicList() {
+ Config->HasDynamicList = true;
expect("{");
- readAnonymousDeclaration();
- if (!atEOF())
+ std::vector<SymbolVersion> Locals;
+ std::vector<SymbolVersion> Globals;
+ std::tie(Locals, Globals) = readSymbols();
+ expect(";");
+
+ if (!atEOF()) {
setError("EOF expected, but got " + next());
+ return;
+ }
+ if (!Locals.empty()) {
+ setError("\"local:\" scope not supported in --dynamic-list");
+ return;
+ }
+
+ for (SymbolVersion V : Globals)
+ Config->DynamicList.push_back(V);
}
void ScriptParser::readVersionScript() {
@@ -182,7 +209,7 @@ void ScriptParser::readVersionScriptCommand() {
return;
}
- while (!atEOF() && !Error && peek() != "}") {
+ while (!atEOF() && !ErrorCount && peek() != "}") {
StringRef VerStr = next();
if (VerStr == "{") {
setError("anonymous version definition is used in "
@@ -207,7 +234,7 @@ void ScriptParser::readLinkerScript() {
continue;
if (Tok == "ASSERT") {
- Script->Opt.Commands.push_back(readAssert());
+ Script->SectionCommands.push_back(readAssert());
} else if (Tok == "ENTRY") {
readEntry();
} else if (Tok == "EXTERN") {
@@ -226,6 +253,8 @@ void ScriptParser::readLinkerScript() {
readOutputFormat();
} else if (Tok == "PHDRS") {
readPhdrs();
+ } else if (Tok == "REGION_ALIAS") {
+ readRegionAlias();
} else if (Tok == "SEARCH_DIR") {
readSearchDir();
} else if (Tok == "SECTIONS") {
@@ -233,7 +262,7 @@ void ScriptParser::readLinkerScript() {
} else if (Tok == "VERSION") {
readVersion();
} else if (SymbolAssignment *Cmd = readProvideOrAssignment(Tok)) {
- Script->Opt.Commands.push_back(Cmd);
+ Script->SectionCommands.push_back(Cmd);
} else {
setError("unknown directive: " + Tok);
}
@@ -250,7 +279,7 @@ void ScriptParser::addFile(StringRef S) {
}
}
- if (sys::path::is_absolute(S)) {
+ if (S.startswith("/")) {
Driver->addFile(S, /*WithLOption=*/false);
} else if (S.startswith("=")) {
if (Config->Sysroot.empty())
@@ -274,7 +303,7 @@ void ScriptParser::readAsNeeded() {
expect("(");
bool Orig = Config->AsNeeded;
Config->AsNeeded = true;
- while (!Error && !consume(")"))
+ while (!ErrorCount && !consume(")"))
addFile(unquote(next()));
Config->AsNeeded = Orig;
}
@@ -290,13 +319,13 @@ void ScriptParser::readEntry() {
void ScriptParser::readExtern() {
expect("(");
- while (!Error && !consume(")"))
+ while (!ErrorCount && !consume(")"))
Config->Undefined.push_back(next());
}
void ScriptParser::readGroup() {
expect("(");
- while (!Error && !consume(")")) {
+ while (!ErrorCount && !consume(")")) {
if (consume("AS_NEEDED"))
readAsNeeded();
else
@@ -307,6 +336,11 @@ void ScriptParser::readGroup() {
void ScriptParser::readInclude() {
StringRef Tok = unquote(next());
+ if (!Seen.insert(Tok).second) {
+ setError("there is a cycle in linker script INCLUDEs");
+ return;
+ }
+
// 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.
@@ -335,7 +369,7 @@ void ScriptParser::readOutput() {
void ScriptParser::readOutputArch() {
// OUTPUT_ARCH is ignored for now.
expect("(");
- while (!Error && !consume(")"))
+ while (!ErrorCount && !consume(")"))
skip();
}
@@ -354,28 +388,43 @@ void ScriptParser::readOutputFormat() {
void ScriptParser::readPhdrs() {
expect("{");
- while (!Error && !consume("}")) {
- Script->Opt.PhdrsCommands.push_back(
- {next(), PT_NULL, false, false, UINT_MAX, nullptr});
- PhdrsCommand &PhdrCmd = Script->Opt.PhdrsCommands.back();
- PhdrCmd.Type = readPhdrType();
+ while (!ErrorCount && !consume("}")) {
+ PhdrsCommand Cmd;
+ Cmd.Name = next();
+ Cmd.Type = readPhdrType();
- while (!Error && !consume(";")) {
+ while (!ErrorCount && !consume(";")) {
if (consume("FILEHDR"))
- PhdrCmd.HasFilehdr = true;
+ Cmd.HasFilehdr = true;
else if (consume("PHDRS"))
- PhdrCmd.HasPhdrs = true;
+ Cmd.HasPhdrs = true;
else if (consume("AT"))
- PhdrCmd.LMAExpr = readParenExpr();
+ Cmd.LMAExpr = readParenExpr();
else if (consume("FLAGS"))
- PhdrCmd.Flags = readParenExpr()().getValue();
+ Cmd.Flags = readParenExpr()().getValue();
else
setError("unexpected header attribute: " + next());
}
+
+ Script->PhdrsCommands.push_back(Cmd);
}
}
+void ScriptParser::readRegionAlias() {
+ expect("(");
+ StringRef Alias = unquote(next());
+ expect(",");
+ StringRef Name = next();
+ expect(")");
+
+ if (Script->MemoryRegions.count(Alias))
+ setError("redefinition of memory region '" + Alias + "'");
+ if (!Script->MemoryRegions.count(Name))
+ setError("memory region '" + Name + "' is not defined");
+ Script->MemoryRegions[Alias] = Script->MemoryRegions[Name];
+}
+
void ScriptParser::readSearchDir() {
expect("(");
StringRef Tok = next();
@@ -385,7 +434,7 @@ void ScriptParser::readSearchDir() {
}
void ScriptParser::readSections() {
- Script->Opt.HasSections = true;
+ Script->HasSectionsCommand = 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
@@ -393,7 +442,7 @@ void ScriptParser::readSections() {
Config->SingleRoRx = true;
expect("{");
- while (!Error && !consume("}")) {
+ while (!ErrorCount && !consume("}")) {
StringRef Tok = next();
BaseCommand *Cmd = readProvideOrAssignment(Tok);
if (!Cmd) {
@@ -402,7 +451,7 @@ void ScriptParser::readSections() {
else
Cmd = readOutputSectionDescription(Tok);
}
- Script->Opt.Commands.push_back(Cmd);
+ Script->SectionCommands.push_back(Cmd);
}
}
@@ -418,7 +467,7 @@ static int precedence(StringRef Op) {
StringMatcher ScriptParser::readFilePatterns() {
std::vector<StringRef> V;
- while (!Error && !consume(")"))
+ while (!ErrorCount && !consume(")"))
V.push_back(next());
return StringMatcher(V);
}
@@ -450,7 +499,7 @@ SortSectionPolicy ScriptParser::readSortKind() {
// any file but a.o, and section .baz in any file but b.o.
std::vector<SectionPattern> ScriptParser::readInputSectionsList() {
std::vector<SectionPattern> Ret;
- while (!Error && peek() != ")") {
+ while (!ErrorCount && peek() != ")") {
StringMatcher ExcludeFilePat;
if (consume("EXCLUDE_FILE")) {
expect("(");
@@ -458,7 +507,7 @@ std::vector<SectionPattern> ScriptParser::readInputSectionsList() {
}
std::vector<StringRef> V;
- while (!Error && peek() != ")" && peek() != "EXCLUDE_FILE")
+ while (!ErrorCount && peek() != ")" && peek() != "EXCLUDE_FILE")
V.push_back(next());
if (!V.empty())
@@ -485,7 +534,7 @@ ScriptParser::readInputSectionRules(StringRef FilePattern) {
auto *Cmd = make<InputSectionDescription>(FilePattern);
expect("(");
- while (!Error && !consume(")")) {
+ while (!ErrorCount && !consume(")")) {
SortSectionPolicy Outer = readSortKind();
SortSectionPolicy Inner = SortSectionPolicy::Default;
std::vector<SectionPattern> V;
@@ -523,7 +572,7 @@ ScriptParser::readInputSectionDescription(StringRef Tok) {
StringRef FilePattern = next();
InputSectionDescription *Cmd = readInputSectionRules(FilePattern);
expect(")");
- Script->Opt.KeptSections.push_back(Cmd);
+ Script->KeptSections.push_back(Cmd);
return Cmd;
}
return readInputSectionRules(Tok);
@@ -573,7 +622,7 @@ uint32_t ScriptParser::readFill() {
//
// https://sourceware.org/binutils/docs/ld/Output-Section-Address.html
// https://sourceware.org/binutils/docs/ld/Output-Section-Type.html
-void ScriptParser::readSectionAddressType(OutputSectionCommand *Cmd) {
+void ScriptParser::readSectionAddressType(OutputSection *Cmd) {
if (consume("(")) {
if (consume("NOLOAD")) {
expect(")");
@@ -593,10 +642,9 @@ void ScriptParser::readSectionAddressType(OutputSectionCommand *Cmd) {
}
}
-OutputSectionCommand *
-ScriptParser::readOutputSectionDescription(StringRef OutSec) {
- OutputSectionCommand *Cmd =
- Script->createOutputSectionCommand(OutSec, getCurrentLocation());
+OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
+ OutputSection *Cmd =
+ Script->createOutputSection(OutSec, getCurrentLocation());
if (peek() != ":")
readSectionAddressType(Cmd);
@@ -616,16 +664,16 @@ ScriptParser::readOutputSectionDescription(StringRef OutSec) {
Cmd->Constraint = ConstraintKind::ReadWrite;
expect("{");
- while (!Error && !consume("}")) {
+ while (!ErrorCount && !consume("}")) {
StringRef Tok = next();
if (Tok == ";") {
// Empty commands are allowed. Do nothing here.
} else if (SymbolAssignment *Assign = readProvideOrAssignment(Tok)) {
- Cmd->Commands.push_back(Assign);
- } else if (BytesDataCommand *Data = readBytesDataCommand(Tok)) {
- Cmd->Commands.push_back(Data);
+ Cmd->SectionCommands.push_back(Assign);
+ } else if (ByteCommand *Data = readByteCommand(Tok)) {
+ Cmd->SectionCommands.push_back(Data);
} else if (Tok == "ASSERT") {
- Cmd->Commands.push_back(readAssert());
+ Cmd->SectionCommands.push_back(readAssert());
expect(";");
} else if (Tok == "CONSTRUCTORS") {
// CONSTRUCTORS is a keyword to make the linker recognize C++ ctors/dtors
@@ -636,7 +684,7 @@ ScriptParser::readOutputSectionDescription(StringRef OutSec) {
} else if (Tok == "SORT") {
readSort();
} else if (peek() == "(") {
- Cmd->Commands.push_back(readInputSectionDescription(Tok));
+ Cmd->SectionCommands.push_back(readInputSectionDescription(Tok));
} else {
setError("unknown command " + Tok);
}
@@ -644,6 +692,8 @@ ScriptParser::readOutputSectionDescription(StringRef OutSec) {
if (consume(">"))
Cmd->MemoryRegionName = next();
+ else if (peek().startswith(">"))
+ Cmd->MemoryRegionName = next().drop_front();
Cmd->Phdrs = readOutputSectionPhdrs();
@@ -706,7 +756,7 @@ SymbolAssignment *ScriptParser::readAssignment(StringRef Name) {
Expr E = readExpr();
if (Op == "+=") {
std::string Loc = getCurrentLocation();
- E = [=] { return add(Script->getSymbolValue(Loc, Name), E()); };
+ E = [=] { return add(Script->getSymbolValue(Name, Loc), E()); };
}
return make<SymbolAssignment>(Name, E, getCurrentLocation());
}
@@ -758,7 +808,7 @@ static Expr combine(StringRef Op, Expr L, Expr R) {
// 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) {
+ while (!atEOF() && !ErrorCount) {
// Read an operator and an expression.
if (consume("?"))
return readTernary(Lhs);
@@ -784,13 +834,24 @@ Expr ScriptParser::readExpr1(Expr Lhs, int MinPrec) {
return Lhs;
}
-uint64_t static getConstant(StringRef S) {
+Expr ScriptParser::getPageSize() {
+ std::string Location = getCurrentLocation();
+ return [=]() -> uint64_t {
+ if (Target)
+ return Target->PageSize;
+ error(Location + ": unable to calculate page size");
+ return 4096; // Return a dummy value.
+ };
+}
+
+Expr ScriptParser::readConstant() {
+ StringRef S = readParenLiteral();
if (S == "COMMONPAGESIZE")
- return Target->PageSize;
+ return getPageSize();
if (S == "MAXPAGESIZE")
- return Config->MaxPageSize;
- error("unknown constant: " + S);
- return 0;
+ return [] { return Config->MaxPageSize; };
+ setError("unknown constant: " + S);
+ return {};
}
// Parses Tok as an integer. It recognizes hexadecimal (prefixed with
@@ -806,10 +867,16 @@ static Optional<uint64_t> parseInt(StringRef Tok) {
// Hexadecimal
uint64_t Val;
- if (Tok.startswith_lower("0x") && to_integer(Tok.substr(2), Val, 16))
+ if (Tok.startswith_lower("0x")) {
+ if (!to_integer(Tok.substr(2), Val, 16))
+ return None;
return Val;
- if (Tok.endswith_lower("H") && to_integer(Tok.drop_back(), Val, 16))
+ }
+ if (Tok.endswith_lower("H")) {
+ if (!to_integer(Tok.drop_back(), Val, 16))
+ return None;
return Val;
+ }
// Decimal
if (Tok.endswith_lower("K")) {
@@ -827,7 +894,7 @@ static Optional<uint64_t> parseInt(StringRef Tok) {
return Val;
}
-BytesDataCommand *ScriptParser::readBytesDataCommand(StringRef Tok) {
+ByteCommand *ScriptParser::readByteCommand(StringRef Tok) {
int Size = StringSwitch<int>(Tok)
.Case("BYTE", 1)
.Case("SHORT", 2)
@@ -836,8 +903,7 @@ BytesDataCommand *ScriptParser::readBytesDataCommand(StringRef Tok) {
.Default(-1);
if (Size == -1)
return nullptr;
-
- return make<BytesDataCommand>(readParenExpr(), Size);
+ return make<ByteCommand>(readParenExpr(), Size);
}
StringRef ScriptParser::readParenLiteral() {
@@ -847,14 +913,9 @@ StringRef ScriptParser::readParenLiteral() {
return Tok;
}
-OutputSection *ScriptParser::checkSection(OutputSectionCommand *Cmd,
- StringRef Location) {
+static void checkIfExists(OutputSection *Cmd, StringRef Location) {
if (Cmd->Location.empty() && Script->ErrorOnMissingSection)
error(Location + ": undefined section " + Cmd->Name);
- if (Cmd->Sec)
- return Cmd->Sec;
- static OutputSection Dummy("", 0, 0);
- return &Dummy;
}
Expr ScriptParser::readPrimary() {
@@ -865,6 +926,10 @@ Expr ScriptParser::readPrimary() {
Expr E = readPrimary();
return [=] { return ~E().getValue(); };
}
+ if (consume("!")) {
+ Expr E = readPrimary();
+ return [=] { return !E().getValue(); };
+ }
if (consume("-")) {
Expr E = readPrimary();
return [=] { return -E().getValue(); };
@@ -885,43 +950,49 @@ Expr ScriptParser::readPrimary() {
}
if (Tok == "ADDR") {
StringRef Name = readParenLiteral();
- OutputSectionCommand *Cmd = Script->getOrCreateOutputSectionCommand(Name);
+ OutputSection *Sec = Script->getOrCreateOutputSection(Name);
return [=]() -> ExprValue {
- return {checkSection(Cmd, Location), 0, Location};
+ checkIfExists(Sec, Location);
+ return {Sec, false, 0, Location};
};
}
if (Tok == "ALIGN") {
expect("(");
Expr E = readExpr();
if (consume(")"))
- return [=] { return alignTo(Script->getDot(), E().getValue()); };
+ return [=] {
+ return alignTo(Script->getDot(), std::max((uint64_t)1, E().getValue()));
+ };
expect(",");
Expr E2 = readExpr();
expect(")");
return [=] {
ExprValue V = E();
- V.Alignment = E2().getValue();
+ V.Alignment = std::max((uint64_t)1, E2().getValue());
return V;
};
}
if (Tok == "ALIGNOF") {
StringRef Name = readParenLiteral();
- OutputSectionCommand *Cmd = Script->getOrCreateOutputSectionCommand(Name);
- return [=] { return checkSection(Cmd, Location)->Alignment; };
+ OutputSection *Cmd = Script->getOrCreateOutputSection(Name);
+ return [=] {
+ checkIfExists(Cmd, Location);
+ return Cmd->Alignment;
+ };
}
if (Tok == "ASSERT")
return readAssertExpr();
- if (Tok == "CONSTANT") {
- StringRef Name = readParenLiteral();
- return [=] { return getConstant(Name); };
- }
+ if (Tok == "CONSTANT")
+ return readConstant();
if (Tok == "DATA_SEGMENT_ALIGN") {
expect("(");
Expr E = readExpr();
expect(",");
readExpr();
expect(")");
- return [=] { return alignTo(Script->getDot(), E().getValue()); };
+ return [=] {
+ return alignTo(Script->getDot(), std::max((uint64_t)1, E().getValue()));
+ };
}
if (Tok == "DATA_SEGMENT_END") {
expect("(");
@@ -938,28 +1009,32 @@ Expr ScriptParser::readPrimary() {
expect(",");
readExpr();
expect(")");
- return [] { return alignTo(Script->getDot(), Target->PageSize); };
+ Expr E = getPageSize();
+ return [=] { return alignTo(Script->getDot(), E().getValue()); };
}
if (Tok == "DEFINED") {
StringRef Name = readParenLiteral();
- return [=] { return Script->isDefined(Name) ? 1 : 0; };
+ return [=] { return Symtab->find(Name) ? 1 : 0; };
}
if (Tok == "LENGTH") {
StringRef Name = readParenLiteral();
- if (Script->Opt.MemoryRegions.count(Name) == 0)
+ if (Script->MemoryRegions.count(Name) == 0)
setError("memory region not defined: " + Name);
- return [=] { return Script->Opt.MemoryRegions[Name].Length; };
+ return [=] { return Script->MemoryRegions[Name]->Length; };
}
if (Tok == "LOADADDR") {
StringRef Name = readParenLiteral();
- OutputSectionCommand *Cmd = Script->getOrCreateOutputSectionCommand(Name);
- return [=] { return checkSection(Cmd, Location)->getLMA(); };
+ OutputSection *Cmd = Script->getOrCreateOutputSection(Name);
+ return [=] {
+ checkIfExists(Cmd, Location);
+ return Cmd->getLMA();
+ };
}
if (Tok == "ORIGIN") {
StringRef Name = readParenLiteral();
- if (Script->Opt.MemoryRegions.count(Name) == 0)
+ if (Script->MemoryRegions.count(Name) == 0)
setError("memory region not defined: " + Name);
- return [=] { return Script->Opt.MemoryRegions[Name].Origin; };
+ return [=] { return Script->MemoryRegions[Name]->Origin; };
}
if (Tok == "SEGMENT_START") {
expect("(");
@@ -971,18 +1046,18 @@ Expr ScriptParser::readPrimary() {
}
if (Tok == "SIZEOF") {
StringRef Name = readParenLiteral();
- OutputSectionCommand *Cmd = Script->getOrCreateOutputSectionCommand(Name);
+ OutputSection *Cmd = Script->getOrCreateOutputSection(Name);
// 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.
- return [=] { return Cmd->Sec ? Cmd->Sec->Size : 0; };
+ return [=] { return Cmd->Size; };
}
if (Tok == "SIZEOF_HEADERS")
return [=] { return elf::getHeaderSize(); };
// Tok is the dot.
if (Tok == ".")
- return [=] { return Script->getSymbolValue(Location, Tok); };
+ return [=] { return Script->getSymbolValue(Tok, Location); };
// Tok is a literal number.
if (Optional<uint64_t> Val = parseInt(Tok))
@@ -991,8 +1066,8 @@ Expr ScriptParser::readPrimary() {
// Tok is a symbol name.
if (!isValidCIdentifier(Tok))
setError("malformed number: " + Tok);
- Script->Opt.ReferencedSymbols.push_back(Tok);
- return [=] { return Script->getSymbolValue(Location, Tok); };
+ Script->ReferencedSymbols.push_back(Tok);
+ return [=] { return Script->getSymbolValue(Tok, Location); };
}
Expr ScriptParser::readTernary(Expr Cond) {
@@ -1011,7 +1086,7 @@ Expr ScriptParser::readParenExpr() {
std::vector<StringRef> ScriptParser::readOutputSectionPhdrs() {
std::vector<StringRef> Phdrs;
- while (!Error && peek().startswith(":")) {
+ while (!ErrorCount && peek().startswith(":")) {
StringRef Tok = next();
Phdrs.push_back((Tok.size() == 1) ? next() : Tok.substr(1));
}
@@ -1103,6 +1178,10 @@ void ScriptParser::readVersionDeclaration(StringRef VerStr) {
expect(";");
}
+static bool hasWildcard(StringRef S) {
+ return S.find_first_of("?*[") != StringRef::npos;
+}
+
// Reads a list of symbols, e.g. "{ global: foo; bar; local: *; };".
std::pair<std::vector<SymbolVersion>, std::vector<SymbolVersion>>
ScriptParser::readSymbols() {
@@ -1110,7 +1189,7 @@ ScriptParser::readSymbols() {
std::vector<SymbolVersion> Globals;
std::vector<SymbolVersion> *V = &Globals;
- while (!Error) {
+ while (!ErrorCount) {
if (consume("}"))
break;
if (consumeLabel("local")) {
@@ -1144,7 +1223,7 @@ std::vector<SymbolVersion> ScriptParser::readVersionExtern() {
expect("{");
std::vector<SymbolVersion> Ret;
- while (!Error && peek() != "}") {
+ while (!ErrorCount && peek() != "}") {
StringRef Tok = next();
bool HasWildcard = !Tok.startswith("\"") && hasWildcard(Tok);
Ret.push_back({unquote(Tok), IsCXX, HasWildcard});
@@ -1171,7 +1250,7 @@ uint64_t ScriptParser::readMemoryAssignment(StringRef S1, StringRef S2,
// MEMORY { name [(attr)] : ORIGIN = origin, LENGTH = len ... }
void ScriptParser::readMemory() {
expect("{");
- while (!Error && !consume("}")) {
+ while (!ErrorCount && !consume("}")) {
StringRef Name = next();
uint32_t Flags = 0;
@@ -1186,13 +1265,12 @@ void ScriptParser::readMemory() {
expect(",");
uint64_t Length = readMemoryAssignment("LENGTH", "len", "l");
- // Add the memory region to the region map (if it doesn't already exist).
- auto It = Script->Opt.MemoryRegions.find(Name);
- if (It != Script->Opt.MemoryRegions.end())
+ // Add the memory region to the region map.
+ if (Script->MemoryRegions.count(Name))
setError("region '" + Name + "' already defined");
- else
- Script->Opt.MemoryRegions[Name] = {Name, Origin, Length,
- Origin, Flags, NegFlags};
+ MemoryRegion *MR = make<MemoryRegion>();
+ *MR = {Name, Origin, Length, Flags, NegFlags};
+ Script->MemoryRegions[Name] = MR;
}
}
diff --git a/ELF/ScriptParser.h b/ELF/ScriptParser.h
index 02f3a2bd9..f820044cc 100644
--- a/ELF/ScriptParser.h
+++ b/ELF/ScriptParser.h
@@ -10,7 +10,7 @@
#ifndef LLD_ELF_SCRIPT_PARSER_H
#define LLD_ELF_SCRIPT_PARSER_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/Support/MemoryBuffer.h"
namespace lld {
diff --git a/ELF/Strings.cpp b/ELF/Strings.cpp
index 2e88bfba0..766258f00 100644
--- a/ELF/Strings.cpp
+++ b/ELF/Strings.cpp
@@ -38,29 +38,6 @@ bool StringMatcher::match(StringRef S) const {
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 (!to_integer(S.substr(Pos + 1), V, 10))
- 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;
@@ -77,16 +54,11 @@ std::vector<uint8_t> elf::parseHex(StringRef S) {
return Hex;
}
-static bool isAlpha(char C) {
- return ('a' <= C && C <= 'z') || ('A' <= C && C <= 'Z') || C == '_';
-}
-
-static bool isAlnum(char C) { return isAlpha(C) || ('0' <= C && C <= '9'); }
-
// Returns true if S is valid as a C language identifier.
bool elf::isValidCIdentifier(StringRef S) {
- return !S.empty() && isAlpha(S[0]) &&
- std::all_of(S.begin() + 1, S.end(), isAlnum);
+ return !S.empty() && (isAlpha(S[0]) || S[0] == '_') &&
+ std::all_of(S.begin() + 1, S.end(),
+ [](char C) { return C == '_' || isAlnum(C); });
}
// Returns the demangled C++ symbol name for Name.
diff --git a/ELF/Strings.h b/ELF/Strings.h
index fd1aa4053..3c7e7445a 100644
--- a/ELF/Strings.h
+++ b/ELF/Strings.h
@@ -10,7 +10,7 @@
#ifndef LLD_ELF_STRINGS_H
#define LLD_ELF_STRINGS_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/Optional.h"
@@ -21,11 +21,8 @@
namespace lld {
namespace elf {
-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
@@ -76,7 +73,7 @@ llvm::Optional<std::string> demangle(StringRef Name);
inline ArrayRef<uint8_t> toArrayRef(StringRef S) {
return {(const uint8_t *)S.data(), S.size()};
}
-}
-}
+} // namespace elf
+} // namespace lld
#endif
diff --git a/ELF/SymbolTable.cpp b/ELF/SymbolTable.cpp
index a223aec98..7f388866f 100644
--- a/ELF/SymbolTable.cpp
+++ b/ELF/SymbolTable.cpp
@@ -29,6 +29,16 @@ using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
+SymbolTable *elf::Symtab;
+
+static InputFile *getFirstElf() {
+ if (!ObjectFiles.empty())
+ return ObjectFiles[0];
+ if (!SharedFiles.empty())
+ return SharedFiles[0];
+ return nullptr;
+}
+
// All input object files must be for the same architecture
// (e.g. it does not make sense to link x86 object files with
// MIPS object files.) This function checks for that error.
@@ -46,15 +56,12 @@ template <class ELFT> static bool isCompatible(InputFile *F) {
if (!Config->Emulation.empty())
error(toString(F) + " is incompatible with " + Config->Emulation);
else
- error(toString(F) + " is incompatible with " + toString(Config->FirstElf));
+ error(toString(F) + " is incompatible with " + toString(getFirstElf()));
return false;
}
// Add symbols in File to the symbol table.
-template <class ELFT> void SymbolTable<ELFT>::addFile(InputFile *File) {
- if (!Config->FirstElf && isa<ELFFileBase<ELFT>>(File))
- Config->FirstElf = File;
-
+template <class ELFT> void SymbolTable::addFile(InputFile *File) {
if (!isCompatible<ELFT>(File))
return;
@@ -72,7 +79,7 @@ template <class ELFT> void SymbolTable<ELFT>::addFile(InputFile *File) {
}
// Lazy object file
- if (auto *F = dyn_cast<LazyObjectFile>(File)) {
+ if (auto *F = dyn_cast<LazyObjFile>(File)) {
F->parse<ELFT>();
return;
}
@@ -99,9 +106,8 @@ template <class ELFT> void SymbolTable<ELFT>::addFile(InputFile *File) {
}
// Regular object file
- auto *F = cast<ObjectFile<ELFT>>(File);
- ObjectFiles.push_back(F);
- F->parse(ComdatGroups);
+ ObjectFiles.push_back(File);
+ cast<ObjFile<ELFT>>(File)->parse(ComdatGroups);
}
// This function is where all the optimizations of link-time
@@ -111,7 +117,7 @@ template <class ELFT> void SymbolTable<ELFT>::addFile(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::addCombinedLTOObject() {
if (BitcodeFiles.empty())
return;
@@ -121,86 +127,95 @@ template <class ELFT> void SymbolTable<ELFT>::addCombinedLTOObject() {
LTO->add(*F);
for (InputFile *File : LTO->compile()) {
- ObjectFile<ELFT> *Obj = cast<ObjectFile<ELFT>>(File);
DenseSet<CachedHashStringRef> DummyGroups;
- Obj->parse(DummyGroups);
- ObjectFiles.push_back(Obj);
+ cast<ObjFile<ELFT>>(File)->parse(DummyGroups);
+ ObjectFiles.push_back(File);
}
}
template <class ELFT>
-DefinedRegular *SymbolTable<ELFT>::addAbsolute(StringRef Name,
- uint8_t Visibility,
- uint8_t Binding) {
- Symbol *Sym =
- addRegular(Name, Visibility, STT_NOTYPE, 0, 0, Binding, nullptr, nullptr);
+DefinedRegular *SymbolTable::addAbsolute(StringRef Name, uint8_t Visibility,
+ uint8_t Binding) {
+ Symbol *Sym = addRegular<ELFT>(Name, Visibility, STT_NOTYPE, 0, 0, Binding,
+ nullptr, nullptr);
return cast<DefinedRegular>(Sym->body());
}
-// Add Name as an "ignored" symbol. An ignored symbol is a regular
-// linker-synthesized defined symbol, but is only defined if needed.
-template <class ELFT>
-DefinedRegular *SymbolTable<ELFT>::addIgnored(StringRef Name,
- uint8_t Visibility) {
- SymbolBody *S = find(Name);
- if (!S || S->isInCurrentDSO())
- return nullptr;
- return addAbsolute(Name, Visibility);
-}
-
// 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) {
+void SymbolTable::trace(StringRef Name) {
Symtab.insert({CachedHashStringRef(Name), {-1, true}});
}
// Rename SYM as __wrap_SYM. The original symbol is preserved as __real_SYM.
// Used to implement --wrap.
-template <class ELFT> void SymbolTable<ELFT>::addSymbolWrap(StringRef Name) {
+template <class ELFT> void SymbolTable::addSymbolWrap(StringRef Name) {
SymbolBody *B = find(Name);
if (!B)
return;
Symbol *Sym = B->symbol();
- Symbol *Real = addUndefined(Saver.save("__real_" + Name));
- Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name));
+ Symbol *Real = addUndefined<ELFT>(Saver.save("__real_" + Name));
+ Symbol *Wrap = addUndefined<ELFT>(Saver.save("__wrap_" + Name));
- // Tell LTO not to eliminate this symbol
- Wrap->IsUsedInRegularObj = true;
+ defsym(Real, Sym);
+ defsym(Sym, Wrap);
- Config->RenamedSymbols[Real] = RenamedSymbol{Sym, Real->Binding};
- Config->RenamedSymbols[Sym] = RenamedSymbol{Wrap, Sym->Binding};
+ WrapSymbols.push_back({Wrap, Real});
}
// Creates alias for symbol. Used to implement --defsym=ALIAS=SYM.
-template <class ELFT> void SymbolTable<ELFT>::addSymbolAlias(StringRef Alias,
- StringRef Name) {
+template <class ELFT>
+void SymbolTable::addSymbolAlias(StringRef Alias, StringRef Name) {
SymbolBody *B = find(Name);
if (!B) {
error("-defsym: undefined symbol: " + Name);
return;
}
- Symbol *Sym = B->symbol();
- Symbol *AliasSym = addUndefined(Alias);
- // Tell LTO not to eliminate this symbol
- Sym->IsUsedInRegularObj = true;
- Config->RenamedSymbols[AliasSym] = RenamedSymbol{Sym, AliasSym->Binding};
+ defsym(addUndefined<ELFT>(Alias), B->symbol());
}
// Apply symbol renames created by -wrap and -defsym. The renames are created
// before LTO in addSymbolWrap() and addSymbolAlias() to have a chance to inform
// LTO (if LTO is running) not to include these symbols in IPO. Now that the
// symbols are finalized, we can perform the replacement.
-template <class ELFT> void SymbolTable<ELFT>::applySymbolRenames() {
- for (auto &KV : Config->RenamedSymbols) {
- Symbol *Sym = KV.first;
- Symbol *Rename = KV.second.Target;
- Sym->Binding = KV.second.OrigBinding;
+void SymbolTable::applySymbolRenames() {
+ // This function rotates 3 symbols:
+ //
+ // __real_foo becomes foo
+ // foo becomes __wrap_foo
+ // __wrap_foo becomes __real_foo
+ //
+ // The last part is special in that we don't want to change what references to
+ // __wrap_foo point to, we just want have __real_foo in the symbol table.
+
+ // First make a copy of __real_foo
+ std::vector<Symbol> Origs;
+ for (const auto &P : WrapSymbols)
+ Origs.push_back(*P.second);
+
+ // Replace __real_foo with foo and foo with __wrap_foo
+ for (SymbolRenaming &S : Defsyms) {
+ S.Dst->body()->copyFrom(S.Src->body());
+ S.Dst->File = S.Src->File;
+ S.Dst->Binding = S.Binding;
+ }
- // 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.
- memcpy(Sym->Body.buffer, Rename->Body.buffer, sizeof(Sym->Body));
+ // Hide one of the copies of __wrap_foo, create a new symbol and copy
+ // __real_foo into it.
+ for (unsigned I = 0, N = WrapSymbols.size(); I < N; ++I) {
+ // We now have two copies of __wrap_foo. Drop one.
+ Symbol *Wrap = WrapSymbols[I].first;
+ Wrap->IsUsedInRegularObj = false;
+
+ Symbol *Real = &Origs[I];
+ // If __real_foo was undefined, we don't want it in the symbol table.
+ if (!Real->body()->isInCurrentDSO())
+ continue;
+
+ auto *NewSym = make<Symbol>();
+ memcpy(NewSym, Real, sizeof(Symbol));
+ SymVector.push_back(NewSym);
}
}
@@ -213,8 +228,17 @@ 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) {
+std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
+ // <name>@@<version> means the symbol is the default version. In that
+ // case <name>@@<version> will be used to resolve references to <name>.
+ //
+ // Since this is a hot path, the following string search code is
+ // optimized for speed. StringRef::find(char) is much faster than
+ // StringRef::find(StringRef).
+ size_t Pos = Name.find('@');
+ if (Pos != StringRef::npos && Pos + 1 < Name.size() && Name[Pos + 1] == '@')
+ Name = Name.take_front(Pos);
+
auto P = Symtab.insert(
{CachedHashStringRef(Name), SymIndex((int)SymVector.size(), false)});
SymIndex &V = P.first->second;
@@ -233,6 +257,7 @@ std::pair<Symbol *, bool> SymbolTable<ELFT>::insert(StringRef Name) {
Sym->Visibility = STV_DEFAULT;
Sym->IsUsedInRegularObj = false;
Sym->ExportDynamic = false;
+ Sym->CanInline = true;
Sym->Traced = V.Traced;
Sym->VersionId = Config->DefaultSymbolVersion;
SymVector.push_back(Sym);
@@ -244,11 +269,11 @@ std::pair<Symbol *, bool> SymbolTable<ELFT>::insert(StringRef Name) {
// 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, InputFile *File) {
- bool IsUsedInRegularObj = !File || File->kind() == InputFile::ObjectKind;
+std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name, uint8_t Type,
+ uint8_t Visibility,
+ bool CanOmitFromDynSym,
+ InputFile *File) {
+ bool IsUsedInRegularObj = !File || File->kind() == InputFile::ObjKind;
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
@@ -265,26 +290,25 @@ SymbolTable<ELFT>::insert(StringRef Name, uint8_t Type, uint8_t Visibility,
if (!WasInserted && S->body()->Type != SymbolBody::UnknownType &&
((Type == STT_TLS) != S->body()->isTls())) {
error("TLS attribute mismatch: " + toString(*S->body()) +
- "\n>>> defined in " + toString(S->body()->File) +
- "\n>>> defined in " + toString(File));
+ "\n>>> defined in " + toString(S->File) + "\n>>> defined in " +
+ toString(File));
}
return {S, WasInserted};
}
-template <class ELFT> Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name) {
- return addUndefined(Name, /*IsLocal=*/false, STB_GLOBAL, STV_DEFAULT,
- /*Type*/ 0,
- /*CanOmitFromDynSym*/ false, /*File*/ nullptr);
+template <class ELFT> Symbol *SymbolTable::addUndefined(StringRef Name) {
+ return addUndefined<ELFT>(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, bool IsLocal,
- uint8_t Binding, uint8_t StOther,
- uint8_t Type, bool CanOmitFromDynSym,
- InputFile *File) {
+Symbol *SymbolTable::addUndefined(StringRef Name, bool IsLocal, uint8_t Binding,
+ uint8_t StOther, uint8_t Type,
+ bool CanOmitFromDynSym, InputFile *File) {
Symbol *S;
bool WasInserted;
uint8_t Visibility = getVisibility(StOther);
@@ -295,15 +319,15 @@ Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, bool IsLocal,
if (WasInserted ||
(isa<SharedSymbol>(S->body()) && Visibility != STV_DEFAULT)) {
S->Binding = Binding;
- replaceBody<Undefined>(S, Name, IsLocal, StOther, Type, File);
+ replaceBody<Undefined>(S, File, Name, IsLocal, StOther, Type);
return S;
}
if (Binding != STB_WEAK) {
SymbolBody *B = S->body();
- if (B->isShared() || B->isLazy() || B->isUndefined())
+ if (!B->isInCurrentDSO())
S->Binding = Binding;
if (auto *SS = dyn_cast<SharedSymbol>(B))
- cast<SharedFile<ELFT>>(SS->File)->IsUsed = true;
+ SS->getFile<ELFT>()->IsUsed = true;
}
if (auto *L = dyn_cast<Lazy>(S->body())) {
// An undefined weak will not fetch archive members, but we have to remember
@@ -311,20 +335,41 @@ Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, bool IsLocal,
if (S->isWeak())
L->Type = Type;
else if (InputFile *F = L->fetch())
- addFile(F);
+ addFile<ELFT>(F);
}
return S;
}
+// Using .symver foo,foo@@VER unfortunately creates two symbols: foo and
+// foo@@VER. We want to effectively ignore foo, so give precedence to
+// foo@@VER.
+// FIXME: If users can transition to using
+// .symver foo,foo@@@VER
+// we can delete this hack.
+static int compareVersion(Symbol *S, StringRef Name) {
+ bool A = Name.contains("@@");
+ bool B = S->body()->getName().contains("@@");
+ if (A && !B)
+ return 1;
+ if (!A && B)
+ return -1;
+ return 0;
+}
+
// We have a new defined symbol with the specified binding. Return 1 if the new
// symbol should win, -1 if the new symbol should lose, or 0 if both symbols are
// strong defined symbols.
-static int compareDefined(Symbol *S, bool WasInserted, uint8_t Binding) {
+static int compareDefined(Symbol *S, bool WasInserted, uint8_t Binding,
+ StringRef Name) {
if (WasInserted)
return 1;
SymbolBody *Body = S->body();
- if (Body->isLazy() || !Body->isInCurrentDSO())
+ if (!Body->isInCurrentDSO())
return 1;
+
+ if (int R = compareVersion(S, Name))
+ return R;
+
if (Binding == STB_WEAK)
return -1;
if (S->isWeak())
@@ -335,10 +380,10 @@ 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.
-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)) {
+ bool IsAbsolute, uint64_t Value,
+ StringRef Name) {
+ if (int Cmp = compareDefined(S, WasInserted, Binding, Name)) {
if (Cmp > 0)
S->Binding = Binding;
return Cmp;
@@ -357,19 +402,17 @@ static int compareDefinedNonCommon(Symbol *S, bool WasInserted, uint8_t Binding,
return 0;
}
-template <class ELFT>
-Symbol *SymbolTable<ELFT>::addCommon(StringRef N, uint64_t Size,
- uint32_t Alignment, uint8_t Binding,
- uint8_t StOther, uint8_t Type,
- InputFile *File) {
+Symbol *SymbolTable::addCommon(StringRef N, uint64_t Size, uint32_t Alignment,
+ uint8_t Binding, uint8_t StOther, uint8_t Type,
+ InputFile *File) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(N, Type, getVisibility(StOther),
/*CanOmitFromDynSym*/ false, File);
- int Cmp = compareDefined(S, WasInserted, Binding);
+ int Cmp = compareDefined(S, WasInserted, Binding, N);
if (Cmp > 0) {
S->Binding = Binding;
- replaceBody<DefinedCommon>(S, N, Size, Alignment, StOther, Type, File);
+ replaceBody<DefinedCommon>(S, File, N, Size, Alignment, StOther, Type);
} else if (Cmp == 0) {
auto *C = dyn_cast<DefinedCommon>(S->body());
if (!C) {
@@ -384,7 +427,7 @@ Symbol *SymbolTable<ELFT>::addCommon(StringRef N, uint64_t Size,
Alignment = C->Alignment = std::max(C->Alignment, Alignment);
if (Size > C->Size)
- replaceBody<DefinedCommon>(S, N, Size, Alignment, StOther, Type, File);
+ replaceBody<DefinedCommon>(S, File, N, Size, Alignment, StOther, Type);
}
return S;
}
@@ -397,9 +440,9 @@ static void warnOrError(const Twine &Msg) {
}
static void reportDuplicate(SymbolBody *Sym, InputFile *NewFile) {
- warnOrError("duplicate symbol: " + toString(*Sym) +
- "\n>>> defined in " + toString(Sym->File) +
- "\n>>> defined in " + toString(NewFile));
+ warnOrError("duplicate symbol: " + toString(*Sym) + "\n>>> defined in " +
+ toString(Sym->getFile()) + "\n>>> defined in " +
+ toString(NewFile));
}
template <class ELFT>
@@ -407,7 +450,7 @@ static void reportDuplicate(SymbolBody *Sym, InputSectionBase *ErrSec,
typename ELFT::uint ErrOffset) {
DefinedRegular *D = dyn_cast<DefinedRegular>(Sym);
if (!D || !D->Section || !ErrSec) {
- reportDuplicate(Sym, ErrSec ? ErrSec->getFile<ELFT>() : nullptr);
+ reportDuplicate(Sym, ErrSec ? ErrSec->File : nullptr);
return;
}
@@ -435,19 +478,18 @@ static void reportDuplicate(SymbolBody *Sym, InputSectionBase *ErrSec,
}
template <typename ELFT>
-Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, uint8_t StOther,
- uint8_t Type, uint64_t Value,
- uint64_t Size, uint8_t Binding,
- SectionBase *Section, InputFile *File) {
+Symbol *SymbolTable::addRegular(StringRef Name, uint8_t StOther, uint8_t Type,
+ uint64_t Value, uint64_t Size, uint8_t Binding,
+ SectionBase *Section, InputFile *File) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name, Type, getVisibility(StOther),
/*CanOmitFromDynSym*/ false, File);
- int Cmp = compareDefinedNonCommon<ELFT>(S, WasInserted, Binding,
- Section == nullptr, Value);
+ int Cmp = compareDefinedNonCommon(S, WasInserted, Binding, Section == nullptr,
+ Value, Name);
if (Cmp > 0)
- replaceBody<DefinedRegular>(S, Name, /*IsLocal=*/false, StOther, Type,
- Value, Size, Section, File);
+ replaceBody<DefinedRegular>(S, File, Name, /*IsLocal=*/false, StOther, Type,
+ Value, Size, Section);
else if (Cmp == 0)
reportDuplicate<ELFT>(S->body(),
dyn_cast_or_null<InputSectionBase>(Section), Value);
@@ -455,9 +497,9 @@ Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, uint8_t StOther,
}
template <typename ELFT>
-void SymbolTable<ELFT>::addShared(SharedFile<ELFT> *File, StringRef Name,
- const Elf_Sym &Sym,
- const typename ELFT::Verdef *Verdef) {
+void SymbolTable::addShared(StringRef Name, SharedFile<ELFT> *File,
+ const typename ELFT::Sym &Sym,
+ const typename ELFT::Verdef *Verdef) {
// DSO symbols do not affect visibility in the output, so we pass STV_DEFAULT
// as the visibility, which will leave the visibility in the symbol table
// unchanged.
@@ -472,8 +514,8 @@ void SymbolTable<ELFT>::addShared(SharedFile<ELFT> *File, StringRef Name,
SymbolBody *Body = S->body();
// An undefined symbol with non default visibility must be satisfied
// in the same DSO.
- if (WasInserted ||
- (isa<Undefined>(Body) && Body->getVisibility() == STV_DEFAULT)) {
+ if (WasInserted || ((Body->isUndefined() || Body->isLazy()) &&
+ Body->getVisibility() == STV_DEFAULT)) {
replaceBody<SharedSymbol>(S, File, Name, Sym.st_other, Sym.getType(), &Sym,
Verdef);
if (!S->isWeak())
@@ -481,25 +523,24 @@ void SymbolTable<ELFT>::addShared(SharedFile<ELFT> *File, StringRef Name,
}
}
-template <class ELFT>
-Symbol *SymbolTable<ELFT>::addBitcode(StringRef Name, uint8_t Binding,
- uint8_t StOther, uint8_t Type,
- bool CanOmitFromDynSym, BitcodeFile *F) {
+Symbol *SymbolTable::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, getVisibility(StOther), CanOmitFromDynSym, F);
- int Cmp = compareDefinedNonCommon<ELFT>(S, WasInserted, Binding,
- /*IsAbs*/ false, /*Value*/ 0);
+ int Cmp = compareDefinedNonCommon(S, WasInserted, Binding,
+ /*IsAbs*/ false, /*Value*/ 0, Name);
if (Cmp > 0)
- replaceBody<DefinedRegular>(S, Name, /*IsLocal=*/false, StOther, Type, 0, 0,
- nullptr, F);
+ replaceBody<DefinedRegular>(S, F, Name, /*IsLocal=*/false, StOther, Type, 0,
+ 0, nullptr);
else if (Cmp == 0)
reportDuplicate(S->body(), F);
return S;
}
-template <class ELFT> SymbolBody *SymbolTable<ELFT>::find(StringRef Name) {
+SymbolBody *SymbolTable::find(StringRef Name) {
auto It = Symtab.find(CachedHashStringRef(Name));
if (It == Symtab.end())
return nullptr;
@@ -509,27 +550,29 @@ template <class ELFT> SymbolBody *SymbolTable<ELFT>::find(StringRef Name) {
return SymVector[V.Idx]->body();
}
-template <class ELFT>
-SymbolBody *SymbolTable<ELFT>::findInCurrentDSO(StringRef Name) {
- if (SymbolBody *S = find(Name))
- if (S->isInCurrentDSO())
- return S;
- return nullptr;
+void SymbolTable::defsym(Symbol *Dst, Symbol *Src) {
+ // We want to tell LTO not to inline Dst symbol because LTO doesn't
+ // know the final symbol contents after renaming.
+ Dst->CanInline = false;
+
+ // Tell LTO not to eliminate this symbol.
+ Src->IsUsedInRegularObj = true;
+
+ Defsyms.push_back({Dst, Src, Dst->Binding});
}
template <class ELFT>
-void SymbolTable<ELFT>::addLazyArchive(ArchiveFile *F,
- const object::Archive::Symbol Sym) {
+Symbol *SymbolTable::addLazyArchive(StringRef Name, ArchiveFile *F,
+ const object::Archive::Symbol Sym) {
Symbol *S;
bool WasInserted;
- StringRef Name = Sym.getName();
std::tie(S, WasInserted) = insert(Name);
if (WasInserted) {
- replaceBody<LazyArchive>(S, *F, Sym, SymbolBody::UnknownType);
- return;
+ replaceBody<LazyArchive>(S, F, Sym, SymbolBody::UnknownType);
+ return S;
}
if (!S->body()->isUndefined())
- return;
+ return S;
// Weak undefined symbols should not fetch members from archives. If we were
// to keep old symbol we would not know that an archive member was available
@@ -539,21 +582,22 @@ void SymbolTable<ELFT>::addLazyArchive(ArchiveFile *F,
// this symbol as used when we added it to the symbol table, but we also need
// to preserve its type. FIXME: Move the Type field to Symbol.
if (S->isWeak()) {
- replaceBody<LazyArchive>(S, *F, Sym, S->body()->Type);
- return;
+ replaceBody<LazyArchive>(S, F, Sym, S->body()->Type);
+ return S;
}
std::pair<MemoryBufferRef, uint64_t> MBInfo = F->getMember(&Sym);
if (!MBInfo.first.getBuffer().empty())
- addFile(createObjectFile(MBInfo.first, F->getName(), MBInfo.second));
+ addFile<ELFT>(createObjectFile(MBInfo.first, F->getName(), MBInfo.second));
+ return S;
}
template <class ELFT>
-void SymbolTable<ELFT>::addLazyObject(StringRef Name, LazyObjectFile &Obj) {
+void SymbolTable::addLazyObject(StringRef Name, LazyObjFile &Obj) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
if (WasInserted) {
- replaceBody<LazyObject>(S, Name, Obj, SymbolBody::UnknownType);
+ replaceBody<LazyObject>(S, &Obj, Name, SymbolBody::UnknownType);
return;
}
if (!S->body()->isUndefined())
@@ -561,17 +605,21 @@ void SymbolTable<ELFT>::addLazyObject(StringRef Name, LazyObjectFile &Obj) {
// See comment for addLazyArchive above.
if (S->isWeak())
- replaceBody<LazyObject>(S, Name, Obj, S->body()->Type);
+ replaceBody<LazyObject>(S, &Obj, Name, S->body()->Type);
else if (InputFile *F = Obj.fetch())
- addFile(F);
+ addFile<ELFT>(F);
}
-// Process undefined (-u) flags by loading lazy symbols named by those flags.
-template <class ELFT> void SymbolTable<ELFT>::scanUndefinedFlags() {
- for (StringRef S : Config->Undefined)
- if (auto *L = dyn_cast_or_null<Lazy>(find(S)))
+// If we already saw this symbol, force loading its file.
+template <class ELFT> void SymbolTable::fetchIfLazy(StringRef Name) {
+ if (SymbolBody *B = find(Name)) {
+ // Mark the symbol not to be eliminated by LTO
+ // even if it is a bitcode symbol.
+ B->symbol()->IsUsedInRegularObj = true;
+ if (auto *L = dyn_cast_or_null<Lazy>(B))
if (InputFile *File = L->fetch())
- addFile(File);
+ addFile<ELFT>(File);
+ }
}
// This function takes care of the case in which shared libraries depend on
@@ -581,9 +629,9 @@ template <class ELFT> void SymbolTable<ELFT>::scanUndefinedFlags() {
// We need to put such symbols to the main program's .dynsym so that
// shared libraries can find them.
// Except this, we ignore undefined symbols in DSOs.
-template <class ELFT> void SymbolTable<ELFT>::scanShlibUndefined() {
- for (SharedFile<ELFT> *File : SharedFiles) {
- for (StringRef U : File->getUndefinedSymbols()) {
+template <class ELFT> void SymbolTable::scanShlibUndefined() {
+ for (InputFile *F : SharedFiles) {
+ for (StringRef U : cast<SharedFile<ELFT>>(F)->getUndefinedSymbols()) {
SymbolBody *Sym = find(U);
if (!Sym || !Sym->isDefined())
continue;
@@ -611,13 +659,12 @@ template <class ELFT> void SymbolTable<ELFT>::scanShlibUndefined() {
// 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() {
+StringMap<std::vector<SymbolBody *>> &SymbolTable::getDemangledSyms() {
if (!DemangledSyms) {
DemangledSyms.emplace();
for (Symbol *Sym : SymVector) {
SymbolBody *B = Sym->body();
- if (B->isUndefined())
+ if (!B->isInCurrentDSO())
continue;
if (Optional<std::string> S = demangle(B->getName()))
(*DemangledSyms)[*S].push_back(B);
@@ -628,19 +675,16 @@ StringMap<std::vector<SymbolBody *>> &SymbolTable<ELFT>::getDemangledSyms() {
return *DemangledSyms;
}
-template <class ELFT>
-std::vector<SymbolBody *> SymbolTable<ELFT>::findByVersion(SymbolVersion Ver) {
+std::vector<SymbolBody *> SymbolTable::findByVersion(SymbolVersion Ver) {
if (Ver.IsExternCpp)
return getDemangledSyms().lookup(Ver.Name);
if (SymbolBody *B = find(Ver.Name))
- if (!B->isUndefined())
+ if (B->isInCurrentDSO())
return {B};
return {};
}
-template <class ELFT>
-std::vector<SymbolBody *>
-SymbolTable<ELFT>::findAllByVersion(SymbolVersion Ver) {
+std::vector<SymbolBody *> SymbolTable::findAllByVersion(SymbolVersion Ver) {
std::vector<SymbolBody *> Res;
StringMatcher M(Ver.Name);
@@ -653,7 +697,7 @@ SymbolTable<ELFT>::findAllByVersion(SymbolVersion Ver) {
for (Symbol *Sym : SymVector) {
SymbolBody *B = Sym->body();
- if (!B->isUndefined() && M.match(B->getName()))
+ if (B->isInCurrentDSO() && M.match(B->getName()))
Res.push_back(B);
}
return Res;
@@ -662,7 +706,7 @@ SymbolTable<ELFT>::findAllByVersion(SymbolVersion Ver) {
// 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.
-template <class ELFT> void SymbolTable<ELFT>::handleAnonymousVersion() {
+void SymbolTable::handleAnonymousVersion() {
for (SymbolVersion &Ver : Config->VersionScriptGlobals)
assignExactVersion(Ver, VER_NDX_GLOBAL, "global");
for (SymbolVersion &Ver : Config->VersionScriptGlobals)
@@ -673,11 +717,28 @@ template <class ELFT> void SymbolTable<ELFT>::handleAnonymousVersion() {
assignWildcardVersion(Ver, VER_NDX_LOCAL);
}
+// Handles -dynamic-list.
+void SymbolTable::handleDynamicList() {
+ for (SymbolVersion &Ver : Config->DynamicList) {
+ std::vector<SymbolBody *> Syms;
+ if (Ver.HasWildcard)
+ Syms = findByVersion(Ver);
+ else
+ Syms = findAllByVersion(Ver);
+
+ for (SymbolBody *B : Syms) {
+ if (!Config->Shared)
+ B->symbol()->ExportDynamic = true;
+ else if (B->symbol()->includeInDynsym())
+ B->IsPreemptible = true;
+ }
+ }
+}
+
// 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) {
+void SymbolTable::assignExactVersion(SymbolVersion Ver, uint16_t VersionId,
+ StringRef VersionName) {
if (Ver.HasWildcard)
return;
@@ -692,6 +753,12 @@ void SymbolTable<ELFT>::assignExactVersion(SymbolVersion Ver, uint16_t VersionId
// Assign the version.
for (SymbolBody *B : Syms) {
+ // Skip symbols containing version info because symbol versions
+ // specified by symbol names take precedence over version scripts.
+ // See parseSymbolVersion().
+ if (B->getName().contains('@'))
+ continue;
+
Symbol *Sym = B->symbol();
if (Sym->InVersionScript)
warn("duplicate symbol '" + Ver.Name + "' in version script");
@@ -700,36 +767,24 @@ void SymbolTable<ELFT>::assignExactVersion(SymbolVersion Ver, uint16_t VersionId
}
}
-template <class ELFT>
-void SymbolTable<ELFT>::assignWildcardVersion(SymbolVersion Ver,
- uint16_t VersionId) {
+void SymbolTable::assignWildcardVersion(SymbolVersion Ver, uint16_t VersionId) {
if (!Ver.HasWildcard)
return;
- std::vector<SymbolBody *> Syms = findAllByVersion(Ver);
// 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)
+ for (SymbolBody *B : findAllByVersion(Ver))
if (B->symbol()->VersionId == Config->DefaultSymbolVersion)
B->symbol()->VersionId = VersionId;
}
// 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();
-
+void SymbolTable::scanVersionScript() {
// Handle edge cases first.
handleAnonymousVersion();
-
- if (Config->VersionDefinitions.empty())
- return;
+ handleDynamicList();
// 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
@@ -748,9 +803,106 @@ template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
for (VersionDefinition &V : llvm::reverse(Config->VersionDefinitions))
for (SymbolVersion &Ver : V.Globals)
assignWildcardVersion(Ver, V.Id);
-}
-template class elf::SymbolTable<ELF32LE>;
-template class elf::SymbolTable<ELF32BE>;
-template class elf::SymbolTable<ELF64LE>;
-template class elf::SymbolTable<ELF64BE>;
+ // Symbol themselves might know their versions because symbols
+ // can contain versions in the form of <name>@<version>.
+ // Let them parse and update their names to exclude version suffix.
+ for (Symbol *Sym : SymVector)
+ Sym->body()->parseSymbolVersion();
+}
+
+template void SymbolTable::addSymbolWrap<ELF32LE>(StringRef);
+template void SymbolTable::addSymbolWrap<ELF32BE>(StringRef);
+template void SymbolTable::addSymbolWrap<ELF64LE>(StringRef);
+template void SymbolTable::addSymbolWrap<ELF64BE>(StringRef);
+
+template Symbol *SymbolTable::addUndefined<ELF32LE>(StringRef);
+template Symbol *SymbolTable::addUndefined<ELF32BE>(StringRef);
+template Symbol *SymbolTable::addUndefined<ELF64LE>(StringRef);
+template Symbol *SymbolTable::addUndefined<ELF64BE>(StringRef);
+
+template Symbol *SymbolTable::addUndefined<ELF32LE>(StringRef, bool, uint8_t,
+ uint8_t, uint8_t, bool,
+ InputFile *);
+template Symbol *SymbolTable::addUndefined<ELF32BE>(StringRef, bool, uint8_t,
+ uint8_t, uint8_t, bool,
+ InputFile *);
+template Symbol *SymbolTable::addUndefined<ELF64LE>(StringRef, bool, uint8_t,
+ uint8_t, uint8_t, bool,
+ InputFile *);
+template Symbol *SymbolTable::addUndefined<ELF64BE>(StringRef, bool, uint8_t,
+ uint8_t, uint8_t, bool,
+ InputFile *);
+
+template void SymbolTable::addSymbolAlias<ELF32LE>(StringRef, StringRef);
+template void SymbolTable::addSymbolAlias<ELF32BE>(StringRef, StringRef);
+template void SymbolTable::addSymbolAlias<ELF64LE>(StringRef, StringRef);
+template void SymbolTable::addSymbolAlias<ELF64BE>(StringRef, StringRef);
+
+template void SymbolTable::addCombinedLTOObject<ELF32LE>();
+template void SymbolTable::addCombinedLTOObject<ELF32BE>();
+template void SymbolTable::addCombinedLTOObject<ELF64LE>();
+template void SymbolTable::addCombinedLTOObject<ELF64BE>();
+
+template Symbol *SymbolTable::addRegular<ELF32LE>(StringRef, uint8_t, uint8_t,
+ uint64_t, uint64_t, uint8_t,
+ SectionBase *, InputFile *);
+template Symbol *SymbolTable::addRegular<ELF32BE>(StringRef, uint8_t, uint8_t,
+ uint64_t, uint64_t, uint8_t,
+ SectionBase *, InputFile *);
+template Symbol *SymbolTable::addRegular<ELF64LE>(StringRef, uint8_t, uint8_t,
+ uint64_t, uint64_t, uint8_t,
+ SectionBase *, InputFile *);
+template Symbol *SymbolTable::addRegular<ELF64BE>(StringRef, uint8_t, uint8_t,
+ uint64_t, uint64_t, uint8_t,
+ SectionBase *, InputFile *);
+
+template DefinedRegular *SymbolTable::addAbsolute<ELF32LE>(StringRef, uint8_t,
+ uint8_t);
+template DefinedRegular *SymbolTable::addAbsolute<ELF32BE>(StringRef, uint8_t,
+ uint8_t);
+template DefinedRegular *SymbolTable::addAbsolute<ELF64LE>(StringRef, uint8_t,
+ uint8_t);
+template DefinedRegular *SymbolTable::addAbsolute<ELF64BE>(StringRef, uint8_t,
+ uint8_t);
+
+template Symbol *
+SymbolTable::addLazyArchive<ELF32LE>(StringRef, ArchiveFile *,
+ const object::Archive::Symbol);
+template Symbol *
+SymbolTable::addLazyArchive<ELF32BE>(StringRef, ArchiveFile *,
+ const object::Archive::Symbol);
+template Symbol *
+SymbolTable::addLazyArchive<ELF64LE>(StringRef, ArchiveFile *,
+ const object::Archive::Symbol);
+template Symbol *
+SymbolTable::addLazyArchive<ELF64BE>(StringRef, ArchiveFile *,
+ const object::Archive::Symbol);
+
+template void SymbolTable::addLazyObject<ELF32LE>(StringRef, LazyObjFile &);
+template void SymbolTable::addLazyObject<ELF32BE>(StringRef, LazyObjFile &);
+template void SymbolTable::addLazyObject<ELF64LE>(StringRef, LazyObjFile &);
+template void SymbolTable::addLazyObject<ELF64BE>(StringRef, LazyObjFile &);
+
+template void SymbolTable::addShared<ELF32LE>(StringRef, SharedFile<ELF32LE> *,
+ const typename ELF32LE::Sym &,
+ const typename ELF32LE::Verdef *);
+template void SymbolTable::addShared<ELF32BE>(StringRef, SharedFile<ELF32BE> *,
+ const typename ELF32BE::Sym &,
+ const typename ELF32BE::Verdef *);
+template void SymbolTable::addShared<ELF64LE>(StringRef, SharedFile<ELF64LE> *,
+ const typename ELF64LE::Sym &,
+ const typename ELF64LE::Verdef *);
+template void SymbolTable::addShared<ELF64BE>(StringRef, SharedFile<ELF64BE> *,
+ const typename ELF64BE::Sym &,
+ const typename ELF64BE::Verdef *);
+
+template void SymbolTable::fetchIfLazy<ELF32LE>(StringRef);
+template void SymbolTable::fetchIfLazy<ELF32BE>(StringRef);
+template void SymbolTable::fetchIfLazy<ELF64LE>(StringRef);
+template void SymbolTable::fetchIfLazy<ELF64BE>(StringRef);
+
+template void SymbolTable::scanShlibUndefined<ELF32LE>();
+template void SymbolTable::scanShlibUndefined<ELF32BE>();
+template void SymbolTable::scanShlibUndefined<ELF64LE>();
+template void SymbolTable::scanShlibUndefined<ELF64BE>();
diff --git a/ELF/SymbolTable.h b/ELF/SymbolTable.h
index f38d09760..1d4ffb28c 100644
--- a/ELF/SymbolTable.h
+++ b/ELF/SymbolTable.h
@@ -18,7 +18,7 @@
namespace lld {
namespace elf {
-class Lazy;
+
struct Symbol;
// SymbolTable is a bucket of all known symbols, including defined,
@@ -33,45 +33,46 @@ struct Symbol;
// 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.
-template <class ELFT> class SymbolTable {
- typedef typename ELFT::Sym Elf_Sym;
-
+class SymbolTable {
public:
- void addFile(InputFile *File);
- void addCombinedLTOObject();
- void addSymbolAlias(StringRef Alias, StringRef Name);
- void addSymbolWrap(StringRef Name);
+ template <class ELFT> void addFile(InputFile *File);
+ template <class ELFT> void addCombinedLTOObject();
+ template <class ELFT> void addSymbolAlias(StringRef Alias, StringRef Name);
+ template <class ELFT> void addSymbolWrap(StringRef Name);
void applySymbolRenames();
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; }
+ template <class ELFT>
DefinedRegular *addAbsolute(StringRef Name,
uint8_t Visibility = llvm::ELF::STV_HIDDEN,
uint8_t Binding = llvm::ELF::STB_GLOBAL);
- DefinedRegular *addIgnored(StringRef Name,
- uint8_t Visibility = llvm::ELF::STV_HIDDEN);
- Symbol *addUndefined(StringRef Name);
+ template <class ELFT> Symbol *addUndefined(StringRef Name);
+ template <class ELFT>
Symbol *addUndefined(StringRef Name, bool IsLocal, uint8_t Binding,
uint8_t StOther, uint8_t Type, bool CanOmitFromDynSym,
InputFile *File);
-
+ template <class ELFT>
Symbol *addRegular(StringRef Name, uint8_t StOther, uint8_t Type,
uint64_t Value, uint64_t Size, uint8_t Binding,
SectionBase *Section, InputFile *File);
- void addShared(SharedFile<ELFT> *F, StringRef Name, const Elf_Sym &Sym,
+ template <class ELFT>
+ void addShared(StringRef Name, SharedFile<ELFT> *F,
+ const typename ELFT::Sym &Sym,
const typename ELFT::Verdef *Verdef);
- void addLazyArchive(ArchiveFile *F, const llvm::object::Archive::Symbol S);
- void addLazyObject(StringRef Name, LazyObjectFile &Obj);
+ template <class ELFT>
+ Symbol *addLazyArchive(StringRef Name, ArchiveFile *F,
+ const llvm::object::Archive::Symbol S);
+
+ template <class ELFT> void addLazyObject(StringRef Name, LazyObjFile &Obj);
+
Symbol *addBitcode(StringRef Name, uint8_t Binding, uint8_t StOther,
uint8_t Type, bool CanOmitFromDynSym, BitcodeFile *File);
- Symbol *addCommon(StringRef N, uint64_t Size, uint32_t Alignment,
+ Symbol *addCommon(StringRef Name, uint64_t Size, uint32_t Alignment,
uint8_t Binding, uint8_t StOther, uint8_t Type,
InputFile *File);
@@ -80,18 +81,20 @@ public:
uint8_t Visibility, bool CanOmitFromDynSym,
InputFile *File);
- void scanUndefinedFlags();
- void scanShlibUndefined();
+ template <class ELFT> void fetchIfLazy(StringRef Name);
+ template <class ELFT> void scanShlibUndefined();
void scanVersionScript();
SymbolBody *find(StringRef Name);
- SymbolBody *findInCurrentDSO(StringRef Name);
void trace(StringRef Name);
+ void handleDynamicList();
+
private:
std::vector<SymbolBody *> findByVersion(SymbolVersion Ver);
std::vector<SymbolBody *> findAllByVersion(SymbolVersion Ver);
+ void defsym(Symbol *Dst, Symbol *Src);
llvm::StringMap<std::vector<SymbolBody *>> &getDemangledSyms();
void handleAnonymousVersion();
@@ -120,11 +123,6 @@ private:
// is used to uniquify them.
llvm::DenseSet<llvm::CachedHashStringRef> ComdatGroups;
- 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;
@@ -134,13 +132,23 @@ private:
// directive in version scripts.
llvm::Optional<llvm::StringMap<std::vector<SymbolBody *>>> DemangledSyms;
+ struct SymbolRenaming {
+ Symbol *Dst;
+ Symbol *Src;
+ uint8_t Binding;
+ };
+
+ // For -defsym or -wrap.
+ std::vector<SymbolRenaming> Defsyms;
+
+ // For -wrap.
+ std::vector<std::pair<Symbol *, Symbol *>> WrapSymbols;
+
// For LTO.
std::unique_ptr<BitcodeCompiler> LTO;
};
-template <class ELFT> struct Symtab { static SymbolTable<ELFT> *X; };
-template <class ELFT> SymbolTable<ELFT> *Symtab<ELFT>::X;
-
+extern SymbolTable *Symtab;
} // namespace elf
} // namespace lld
diff --git a/ELF/Symbols.cpp b/ELF/Symbols.cpp
index 8f9b20477..103993cb9 100644
--- a/ELF/Symbols.cpp
+++ b/ELF/Symbols.cpp
@@ -35,6 +35,7 @@ DefinedRegular *ElfSym::Edata1;
DefinedRegular *ElfSym::Edata2;
DefinedRegular *ElfSym::End1;
DefinedRegular *ElfSym::End2;
+DefinedRegular *ElfSym::GlobalOffsetTable;
DefinedRegular *ElfSym::MipsGp;
DefinedRegular *ElfSym::MipsGpDisp;
DefinedRegular *ElfSym::MipsLocalGp;
@@ -92,22 +93,18 @@ static uint64_t getSymVA(const SymbolBody &Body, int64_t &Addend) {
if (D.isTls() && !Config->Relocatable) {
if (!Out::TlsPhdr)
- fatal(toString(D.File) +
- " has a STT_TLS symbol but doesn't have a PT_TLS section");
+ fatal(toString(D.getFile()) +
+ " has an STT_TLS symbol but doesn't have an SHF_TLS section");
return VA - Out::TlsPhdr->p_vaddr;
}
return VA;
}
case SymbolBody::DefinedCommonKind:
- if (!Config->DefineCommon)
- return 0;
- return InX::Common->getParent()->Addr + InX::Common->OutSecOff +
- cast<DefinedCommon>(Body).Offset;
+ llvm_unreachable("common are converted to bss");
case SymbolBody::SharedKind: {
auto &SS = cast<SharedSymbol>(Body);
- if (SS.NeedsCopy)
- return SS.CopyRelSec->getParent()->Addr + SS.CopyRelSec->OutSecOff +
- SS.CopyRelSecOff;
+ if (SS.CopyRelSec)
+ return SS.CopyRelSec->getParent()->Addr + SS.CopyRelSec->OutSecOff;
if (SS.NeedsPltAddr)
return Body.getPltVA();
return 0;
@@ -124,38 +121,42 @@ static uint64_t getSymVA(const SymbolBody &Body, int64_t &Addend) {
SymbolBody::SymbolBody(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
uint8_t Type)
- : SymbolKind(K), NeedsCopy(false), NeedsPltAddr(false), IsLocal(IsLocal),
+ : SymbolKind(K), IsLocal(IsLocal), NeedsPltAddr(false),
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.
-bool SymbolBody::isPreemptible() const {
- if (isLocal())
- return false;
-
- // Shared symbols resolve to the definition in the DSO. The exceptions are
- // symbols with copy relocations (which resolve to .bss) or preempt plt
- // entries (which resolve to that plt entry).
- if (isShared())
- return !NeedsCopy && !NeedsPltAddr;
-
- // That's all that can be preempted in a non-DSO.
- if (!Config->Shared)
- return false;
-
- // Only symbols that appear in dynsym can be preempted.
- if (!symbol()->includeInDynsym())
- return false;
+ IsInIgot(false), IsPreemptible(false), Type(Type), StOther(StOther),
+ Name(Name) {}
+
+// Returns true if this is a weak undefined symbol.
+bool SymbolBody::isUndefWeak() const {
+ // A note on isLazy() in the following expression: If you add a weak
+ // undefined symbol and then a lazy symbol to the symbol table, the
+ // combined result is a lazy weak symbol. isLazy is for that situation.
+ //
+ // Weak undefined symbols shouldn't fetch archive members (for
+ // compatibility with other linkers), but we still want to memorize
+ // that there are lazy symbols, because strong undefined symbols
+ // could be added later which triggers archive member fetching.
+ // Thus, the weak lazy symbol is a valid concept in lld.
+ return !isLocal() && symbol()->isWeak() && (isUndefined() || isLazy());
+}
- // Only default visibility symbols can be preempted.
- if (symbol()->Visibility != STV_DEFAULT)
- return false;
+InputFile *SymbolBody::getFile() const {
+ if (isLocal()) {
+ const SectionBase *Sec = cast<DefinedRegular>(this)->Section;
+ // Local absolute symbols actually have a file, but that is not currently
+ // used. We could support that by having a mostly redundant InputFile in
+ // SymbolBody, or having a special absolute section if needed.
+ return Sec ? cast<InputSectionBase>(Sec)->File : nullptr;
+ }
+ return symbol()->File;
+}
- // -Bsymbolic means that definitions are not preempted.
- if (Config->Bsymbolic || (Config->BsymbolicFunctions && isFunc()))
- return !isDefined();
- return true;
+// Overwrites all attributes with Other's so that this symbol becomes
+// an alias to Other. This is useful for handling some options such as
+// --wrap.
+void SymbolBody::copyFrom(SymbolBody *Other) {
+ memcpy(symbol()->Body.buffer, Other->symbol()->Body.buffer,
+ sizeof(Symbol::Body));
}
uint64_t SymbolBody::getVA(int64_t Addend) const {
@@ -206,14 +207,14 @@ OutputSection *SymbolBody::getOutputSection() const {
}
if (auto *S = dyn_cast<SharedSymbol>(this)) {
- if (S->NeedsCopy)
+ if (S->CopyRelSec)
return S->CopyRelSec->getParent();
return nullptr;
}
- if (isa<DefinedCommon>(this)) {
+ if (auto *S = dyn_cast<DefinedCommon>(this)) {
if (Config->DefineCommon)
- return InX::Common->getParent();
+ return S->Section->getParent();
return nullptr;
}
@@ -256,7 +257,12 @@ void SymbolBody::parseSymbolVersion() {
}
// It is an error if the specified version is not defined.
- error(toString(File) + ": symbol " + S + " has undefined version " + Verstr);
+ // Usually version script is not provided when linking executable,
+ // but we may still want to override a versioned symbol from DSO,
+ // so we do not report error in this case.
+ if (Config->Shared)
+ error(toString(getFile()) + ": symbol " + S + " has undefined version " +
+ Verstr);
}
Defined::Defined(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
@@ -275,24 +281,20 @@ template <class ELFT> bool DefinedRegular::isMipsPIC() const {
}
Undefined::Undefined(StringRefZ Name, bool IsLocal, uint8_t StOther,
- uint8_t Type, InputFile *File)
- : SymbolBody(SymbolBody::UndefinedKind, Name, IsLocal, StOther, Type) {
- this->File = File;
-}
+ uint8_t Type)
+ : SymbolBody(SymbolBody::UndefinedKind, Name, IsLocal, StOther, Type) {}
DefinedCommon::DefinedCommon(StringRef Name, uint64_t Size, uint32_t Alignment,
- uint8_t StOther, uint8_t Type, InputFile *File)
+ uint8_t StOther, uint8_t Type)
: Defined(SymbolBody::DefinedCommonKind, Name, /*IsLocal=*/false, StOther,
Type),
- Alignment(Alignment), Size(Size) {
- this->File = File;
-}
+ Alignment(Alignment), Size(Size) {}
// If a shared symbol is referred via a copy relocation, its alignment
// becomes part of the ABI. This function returns a symbol alignment.
// Because symbols don't have alignment attributes, we need to infer that.
template <class ELFT> uint32_t SharedSymbol::getAlignment() const {
- auto *File = cast<SharedFile<ELFT>>(this->File);
+ SharedFile<ELFT> *File = getFile<ELFT>();
uint32_t SecAlign = File->getSection(getSym<ELFT>())->sh_addralign;
uint64_t SymValue = getSym<ELFT>().st_value;
uint32_t SymAlign = uint32_t(1) << countTrailingZeros(SymValue);
@@ -305,28 +307,31 @@ InputFile *Lazy::fetch() {
return cast<LazyObject>(this)->fetch();
}
-LazyArchive::LazyArchive(ArchiveFile &File,
- const llvm::object::Archive::Symbol S, uint8_t Type)
- : Lazy(LazyArchiveKind, S.getName(), Type), Sym(S) {
- this->File = &File;
-}
+LazyArchive::LazyArchive(const llvm::object::Archive::Symbol S, uint8_t Type)
+ : Lazy(LazyArchiveKind, S.getName(), Type), Sym(S) {}
-LazyObject::LazyObject(StringRef Name, LazyObjectFile &File, uint8_t Type)
- : Lazy(LazyObjectKind, Name, Type) {
- this->File = &File;
+LazyObject::LazyObject(StringRef Name, uint8_t Type)
+ : Lazy(LazyObjectKind, Name, Type) {}
+
+ArchiveFile *LazyArchive::getFile() {
+ return cast<ArchiveFile>(SymbolBody::getFile());
}
InputFile *LazyArchive::fetch() {
- std::pair<MemoryBufferRef, uint64_t> MBInfo = file()->getMember(&Sym);
+ std::pair<MemoryBufferRef, uint64_t> MBInfo = getFile()->getMember(&Sym);
// getMember returns an empty buffer if the member was already
// read from the library.
if (MBInfo.first.getBuffer().empty())
return nullptr;
- return createObjectFile(MBInfo.first, file()->getName(), MBInfo.second);
+ return createObjectFile(MBInfo.first, getFile()->getName(), MBInfo.second);
+}
+
+LazyObjFile *LazyObject::getFile() {
+ return cast<LazyObjFile>(SymbolBody::getFile());
}
-InputFile *LazyObject::fetch() { return file()->fetch(); }
+InputFile *LazyObject::fetch() { return getFile()->fetch(); }
uint8_t Symbol::computeBinding() const {
if (Config->Relocatable)
@@ -341,10 +346,13 @@ uint8_t Symbol::computeBinding() const {
}
bool Symbol::includeInDynsym() const {
+ if (!Config->HasDynSymTab)
+ return false;
if (computeBinding() == STB_LOCAL)
return false;
- return ExportDynamic || body()->isShared() ||
- (body()->isUndefined() && Config->Shared);
+ if (!body()->isInCurrentDSO())
+ return true;
+ return ExportDynamic;
}
// Print out a log message for --trace-symbol.
@@ -358,7 +366,7 @@ void elf::printTraceSymbol(Symbol *Sym) {
else
S = ": definition of ";
- message(toString(B->File) + S + B->getName());
+ message(toString(Sym->File) + S + B->getName());
}
// Returns a symbol for an error message.
diff --git a/ELF/Symbols.h b/ELF/Symbols.h
index 030527f63..122d0163c 100644
--- a/ELF/Symbols.h
+++ b/ELF/Symbols.h
@@ -18,7 +18,7 @@
#include "InputSection.h"
#include "Strings.h"
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
@@ -27,9 +27,10 @@ namespace elf {
class ArchiveFile;
class BitcodeFile;
+class BssSection;
class InputFile;
-class LazyObjectFile;
-template <class ELFT> class ObjectFile;
+class LazyObjFile;
+template <class ELFT> class ObjFile;
class OutputSection;
template <class ELFT> class SharedFile;
@@ -61,16 +62,26 @@ public:
bool isUndefined() const { return SymbolKind == UndefinedKind; }
bool isDefined() const { return SymbolKind <= DefinedLast; }
bool isCommon() const { return SymbolKind == DefinedCommonKind; }
+ bool isShared() const { return SymbolKind == SharedKind; }
+ bool isLocal() const { return IsLocal; }
+
bool isLazy() const {
return SymbolKind == LazyArchiveKind || SymbolKind == LazyObjectKind;
}
- bool isShared() const { return SymbolKind == SharedKind; }
- bool isInCurrentDSO() const { return !isUndefined() && !isShared(); }
- bool isLocal() const { return IsLocal; }
- bool isPreemptible() const;
+
+ bool isInCurrentDSO() const {
+ return SymbolKind == DefinedRegularKind || SymbolKind == DefinedCommonKind;
+ }
+
+ // True is this is an undefined weak symbol. This only works once
+ // all input files have been added.
+ bool isUndefWeak() const;
+
+ InputFile *getFile() const;
StringRef getName() const { return Name; }
uint8_t getVisibility() const { return StOther & 0x3; }
void parseSymbolVersion();
+ void copyFrom(SymbolBody *Other);
bool isInGot() const { return GotIndex != -1U; }
bool isInPlt() const { return PltIndex != -1U; }
@@ -85,9 +96,6 @@ public:
template <class ELFT> typename ELFT::uint getSize() const;
OutputSection *getOutputSection() const;
- // The file from which this symbol was created.
- InputFile *File = nullptr;
-
uint32_t DynsymIndex = 0;
uint32_t GotIndex = -1;
uint32_t GotPltIndex = -1;
@@ -100,18 +108,13 @@ protected:
const unsigned SymbolKind : 8;
-public:
- // True if the linker has to generate a copy relocation.
- // For SharedSymbol only.
- unsigned NeedsCopy : 1;
+ // True if this is a local symbol.
+ unsigned IsLocal : 1;
+public:
// True the symbol should point to its PLT entry.
// For SharedSymbol only.
unsigned NeedsPltAddr : 1;
-
- // True if this is a local symbol.
- unsigned IsLocal : 1;
-
// True if this symbol has an entry in the global part of MIPS GOT.
unsigned IsInGlobalMipsGot : 1;
@@ -124,6 +127,8 @@ public:
// True if this symbol is in the Igot sub-section of the .got.plt or .got.
unsigned IsInIgot : 1;
+ unsigned IsPreemptible : 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
@@ -156,32 +161,28 @@ public:
class DefinedCommon : public Defined {
public:
DefinedCommon(StringRef N, uint64_t Size, uint32_t Alignment, uint8_t StOther,
- uint8_t Type, InputFile *File);
+ uint8_t Type);
static bool classof(const SymbolBody *S) {
return S->kind() == SymbolBody::DefinedCommonKind;
}
- // The output offset of this common symbol in the output bss. Computed by the
- // writer.
- uint64_t Offset;
-
// The maximum alignment we have seen for this symbol.
uint32_t Alignment;
+ // The output offset of this common symbol in the output bss.
+ // Computed by the writer.
uint64_t Size;
+ BssSection *Section = nullptr;
};
// Regular defined symbols read from object file symbol tables.
class DefinedRegular : public Defined {
public:
DefinedRegular(StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type,
- uint64_t Value, uint64_t Size, SectionBase *Section,
- InputFile *File)
+ uint64_t Value, uint64_t Size, SectionBase *Section)
: Defined(SymbolBody::DefinedRegularKind, Name, IsLocal, StOther, Type),
- Value(Value), Size(Size), Section(Section) {
- this->File = File;
- }
+ Value(Value), Size(Size), Section(Section) {}
// Return true if the symbol is a PIC function.
template <class ELFT> bool isMipsPIC() const;
@@ -197,8 +198,7 @@ public:
class Undefined : public SymbolBody {
public:
- Undefined(StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type,
- InputFile *F);
+ Undefined(StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type);
static bool classof(const SymbolBody *S) {
return S->kind() == UndefinedKind;
@@ -211,14 +211,32 @@ public:
return S->kind() == SymbolBody::SharedKind;
}
- SharedSymbol(InputFile *File, StringRef Name, uint8_t StOther, uint8_t Type,
+ SharedSymbol(StringRef Name, uint8_t StOther, uint8_t Type,
const void *ElfSym, const void *Verdef)
: Defined(SymbolBody::SharedKind, Name, /*IsLocal=*/false, StOther, Type),
Verdef(Verdef), ElfSym(ElfSym) {
- // IFuncs defined in DSOs are treated as functions by the static linker.
- if (isGnuIFunc())
- Type = llvm::ELF::STT_FUNC;
- this->File = File;
+ // GNU ifunc is a mechanism to allow user-supplied functions to
+ // resolve PLT slot values at load-time. This is contrary to the
+ // regualr symbol resolution scheme in which symbols are resolved just
+ // by name. Using this hook, you can program how symbols are solved
+ // for you program. For example, you can make "memcpy" to be resolved
+ // to a SSE-enabled version of memcpy only when a machine running the
+ // program supports the SSE instruction set.
+ //
+ // Naturally, such symbols should always be called through their PLT
+ // slots. What GNU ifunc symbols point to are resolver functions, and
+ // calling them directly doesn't make sense (unless you are writing a
+ // loader).
+ //
+ // For DSO symbols, we always call them through PLT slots anyway.
+ // So there's no difference between GNU ifunc and regular function
+ // symbols if they are in DSOs. So we can handle GNU_IFUNC as FUNC.
+ if (this->Type == llvm::ELF::STT_GNU_IFUNC)
+ this->Type = llvm::ELF::STT_FUNC;
+ }
+
+ template <class ELFT> SharedFile<ELFT> *getFile() const {
+ return cast<SharedFile<ELFT>>(SymbolBody::getFile());
}
template <class ELFT> uint64_t getShndx() const {
@@ -238,9 +256,8 @@ public:
// This field is a pointer to the symbol's version definition.
const void *Verdef;
- // CopyRelSec and CopyRelSecOff are significant only when NeedsCopy is true.
- InputSection *CopyRelSec;
- uint64_t CopyRelSecOff;
+ // If not null, there is a copy relocation to this section.
+ InputSection *CopyRelSec = nullptr;
private:
template <class ELFT> const typename ELFT::Sym &getSym() const {
@@ -271,14 +288,13 @@ protected:
// LazyArchive symbols represents symbols in archive files.
class LazyArchive : public Lazy {
public:
- LazyArchive(ArchiveFile &File, const llvm::object::Archive::Symbol S,
- uint8_t Type);
+ LazyArchive(const llvm::object::Archive::Symbol S, uint8_t Type);
static bool classof(const SymbolBody *S) {
return S->kind() == LazyArchiveKind;
}
- ArchiveFile *file() { return (ArchiveFile *)this->File; }
+ ArchiveFile *getFile();
InputFile *fetch();
private:
@@ -289,13 +305,13 @@ private:
// --start-lib and --end-lib options.
class LazyObject : public Lazy {
public:
- LazyObject(StringRef Name, LazyObjectFile &File, uint8_t Type);
+ LazyObject(StringRef Name, uint8_t Type);
static bool classof(const SymbolBody *S) {
return S->kind() == LazyObjectKind;
}
- LazyObjectFile *file() { return (LazyObjectFile *)this->File; }
+ LazyObjFile *getFile();
InputFile *fetch();
};
@@ -317,6 +333,11 @@ struct ElfSym {
static DefinedRegular *End1;
static DefinedRegular *End2;
+ // The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention to
+ // be at some offset from the base of the .got section, usually 0 or
+ // the end of the .got.
+ static DefinedRegular *GlobalOffsetTable;
+
// _gp, _gp_disp and __gnu_local_gp symbols. Only for MIPS.
static DefinedRegular *MipsGp;
static DefinedRegular *MipsGpDisp;
@@ -354,12 +375,20 @@ struct Symbol {
// --export-dynamic, and by dynamic lists.
unsigned ExportDynamic : 1;
+ // False if LTO shouldn't inline whatever this symbol points to. If a symbol
+ // is overwritten after LTO, LTO shouldn't inline the symbol because it
+ // doesn't know the final contents of the symbol.
+ unsigned CanInline : 1;
+
// 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;
+ // The file from which this symbol was created.
+ InputFile *File = nullptr;
+
bool includeInDynsym() const;
uint8_t computeBinding() const;
bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
@@ -378,13 +407,13 @@ struct Symbol {
void printTraceSymbol(Symbol *Sym);
template <typename T, typename... ArgT>
-void replaceBody(Symbol *S, ArgT &&... Arg) {
+void replaceBody(Symbol *S, InputFile *File, 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");
-
+ S->File = File;
new (S->Body.buffer) T(std::forward<ArgT>(Arg)...);
// Print out a log message if --trace-symbol was specified.
diff --git a/ELF/SyntheticSections.cpp b/ELF/SyntheticSections.cpp
index cb1494d42..884075c5e 100644
--- a/ELF/SyntheticSections.cpp
+++ b/ELF/SyntheticSections.cpp
@@ -24,9 +24,9 @@
#include "Strings.h"
#include "SymbolTable.h"
#include "Target.h"
-#include "Threads.h"
#include "Writer.h"
-#include "lld/Config/Version.h"
+#include "lld/Common/Threads.h"
+#include "lld/Common/Version.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h"
#include "llvm/Object/Decompressor.h"
@@ -37,6 +37,7 @@
#include "llvm/Support/SHA1.h"
#include "llvm/Support/xxhash.h"
#include <cstdlib>
+#include <thread>
using namespace llvm;
using namespace llvm::dwarf;
@@ -48,39 +49,35 @@ using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
+constexpr size_t MergeNoTailSection::NumShards;
+
uint64_t SyntheticSection::getVA() const {
if (OutputSection *Sec = getParent())
return Sec->Addr + OutSecOff;
return 0;
}
-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 *elf::createCommonSection() {
- if (!Config->DefineCommon)
- return nullptr;
-
- // Sort the common symbols by alignment as an heuristic to pack them better.
- std::vector<DefinedCommon *> Syms = getCommonSymbols<ELFT>();
- if (Syms.empty())
- return nullptr;
+// Create a .bss section for each common symbol and replace the common symbol
+// with a DefinedRegular symbol.
+template <class ELFT> void elf::createCommonSections() {
+ for (Symbol *S : Symtab->getSymbols()) {
+ auto *Sym = dyn_cast<DefinedCommon>(S->body());
- std::stable_sort(Syms.begin(), Syms.end(),
- [](const DefinedCommon *A, const DefinedCommon *B) {
- return A->Alignment > B->Alignment;
- });
+ if (!Sym)
+ continue;
- BssSection *Sec = make<BssSection>("COMMON");
- for (DefinedCommon *Sym : Syms)
- Sym->Offset = Sec->reserveSpace(Sym->Size, Sym->Alignment);
- return Sec;
+ // Create a synthetic section for the common data.
+ auto *Section = make<BssSection>("COMMON", Sym->Size, Sym->Alignment);
+ Section->File = Sym->getFile();
+ Section->Live = !Config->GcSections;
+ InputSections.push_back(Section);
+
+ // Replace all DefinedCommon symbols with DefinedRegular symbols so that we
+ // don't have to care about DefinedCommon symbols beyond this point.
+ replaceBody<DefinedRegular>(S, Sym->getFile(), Sym->getName(),
+ static_cast<bool>(Sym->isLocal()), Sym->StOther,
+ Sym->Type, 0, Sym->getSize<ELFT>(), Section);
+ }
}
// Returns an LLD version string.
@@ -108,9 +105,8 @@ template <class ELFT> MergeInputSection *elf::createCommentSection() {
Hdr.sh_addralign = 1;
auto *Ret =
- make<MergeInputSection>((ObjectFile<ELFT> *)nullptr, &Hdr, ".comment");
+ make<MergeInputSection>((ObjFile<ELFT> *)nullptr, &Hdr, ".comment");
Ret->Data = getVersion();
- Ret->splitIntoPieces();
return Ret;
}
@@ -154,7 +150,7 @@ MipsAbiFlagsSection<ELFT> *MipsAbiFlagsSection<ELFT>::create() {
return nullptr;
}
- // LLD checks ISA compatibility in getMipsEFlags(). Here we just
+ // LLD checks ISA compatibility in calcMipsEFlags(). 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);
@@ -297,7 +293,7 @@ InputSection *elf::createInterpSection() {
SymbolBody *elf::addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
uint64_t Size, InputSectionBase *Section) {
auto *S = make<DefinedRegular>(Name, /*IsLocal*/ true, STV_DEFAULT, Type,
- Value, Size, Section, nullptr);
+ Value, Size, Section);
if (InX::SymTab)
InX::SymTab->addSymbol(S);
return S;
@@ -364,15 +360,11 @@ void BuildIdSection::computeHash(
HashFn(HashBuf, Hashes);
}
-BssSection::BssSection(StringRef Name)
- : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_NOBITS, 0, Name) {}
-
-size_t BssSection::reserveSpace(uint64_t Size, uint32_t Alignment) {
+BssSection::BssSection(StringRef Name, uint64_t Size, uint32_t Alignment)
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_NOBITS, Alignment, Name) {
if (OutputSection *Sec = getParent())
- Sec->updateAlignment(Alignment);
- this->Size = alignTo(this->Size, Alignment) + Size;
- this->Alignment = std::max(this->Alignment, Alignment);
- return this->Size - Size;
+ Sec->Alignment = std::max(Sec->Alignment, Alignment);
+ this->Size = Size;
}
void BuildIdSection::writeBuildId(ArrayRef<uint8_t> Buf) {
@@ -413,48 +405,54 @@ EhFrameSection<ELFT>::EhFrameSection()
// and where their relocations point to.
template <class ELFT>
template <class RelTy>
-CieRecord *EhFrameSection<ELFT>::addCie(EhSectionPiece &Piece,
+CieRecord *EhFrameSection<ELFT>::addCie(EhSectionPiece &Cie,
ArrayRef<RelTy> Rels) {
- auto *Sec = cast<EhInputSection>(Piece.ID);
+ auto *Sec = cast<EhInputSection>(Cie.Sec);
const endianness E = ELFT::TargetEndianness;
- if (read32<E>(Piece.data().data() + 4) != 0)
+ if (read32<E>(Cie.data().data() + 4) != 0)
fatal(toString(Sec) + ": CIE expected at beginning of .eh_frame");
SymbolBody *Personality = nullptr;
- unsigned FirstRelI = Piece.FirstRelocation;
+ unsigned FirstRelI = Cie.FirstRelocation;
if (FirstRelI != (unsigned)-1)
Personality =
&Sec->template getFile<ELFT>()->getRelocTargetSym(Rels[FirstRelI]);
// Search for an existing CIE by CIE contents/relocation target pair.
- CieRecord *Cie = &CieMap[{Piece.data(), Personality}];
+ CieRecord *&Rec = CieMap[{Cie.data(), Personality}];
// If not found, create a new one.
- if (Cie->Piece == nullptr) {
- Cie->Piece = &Piece;
- Cies.push_back(Cie);
+ if (!Rec) {
+ Rec = make<CieRecord>();
+ Rec->Cie = &Cie;
+ CieRecords.push_back(Rec);
}
- return Cie;
+ return Rec;
}
// There is one FDE per function. Returns true if a given FDE
// points to a live function.
template <class ELFT>
template <class RelTy>
-bool EhFrameSection<ELFT>::isFdeLive(EhSectionPiece &Piece,
+bool EhFrameSection<ELFT>::isFdeLive(EhSectionPiece &Fde,
ArrayRef<RelTy> Rels) {
- auto *Sec = cast<EhInputSection>(Piece.ID);
- unsigned FirstRelI = Piece.FirstRelocation;
+ auto *Sec = cast<EhInputSection>(Fde.Sec);
+ unsigned FirstRelI = Fde.FirstRelocation;
+
+ // An FDE should point to some function because FDEs are to describe
+ // functions. That's however not always the case due to an issue of
+ // ld.gold with -r. ld.gold may discard only functions and leave their
+ // corresponding FDEs, which results in creating bad .eh_frame sections.
+ // To deal with that, we ignore such FDEs.
if (FirstRelI == (unsigned)-1)
return false;
+
const RelTy &Rel = Rels[FirstRelI];
SymbolBody &B = Sec->template getFile<ELFT>()->getRelocTargetSym(Rel);
- auto *D = dyn_cast<DefinedRegular>(&B);
- if (!D || !D->Section)
- return false;
- auto *Target =
- cast<InputSectionBase>(cast<InputSectionBase>(D->Section)->Repl);
- return Target && Target->Live;
+ if (auto *D = dyn_cast<DefinedRegular>(&B))
+ if (D->Section)
+ return cast<InputSectionBase>(D->Section)->Repl->Live;
+ return false;
}
// .eh_frame is a sequence of CIE or FDE records. In general, there
@@ -470,7 +468,7 @@ void EhFrameSection<ELFT>::addSectionAux(EhInputSection *Sec,
DenseMap<size_t, CieRecord *> OffsetToCie;
for (EhSectionPiece &Piece : Sec->Pieces) {
// The empty record is the end marker.
- if (Piece.size() == 4)
+ if (Piece.Size == 4)
return;
size_t Offset = Piece.InputOff;
@@ -481,13 +479,13 @@ void EhFrameSection<ELFT>::addSectionAux(EhInputSection *Sec,
}
uint32_t CieOffset = Offset + 4 - ID;
- CieRecord *Cie = OffsetToCie[CieOffset];
- if (!Cie)
+ CieRecord *Rec = OffsetToCie[CieOffset];
+ if (!Rec)
fatal(toString(Sec) + ": invalid CIE reference");
if (!isFdeLive(Piece, Rels))
continue;
- Cie->FdePieces.push_back(&Piece);
+ Rec->Fdes.push_back(&Piece);
NumFdes++;
}
}
@@ -496,8 +494,10 @@ template <class ELFT>
void EhFrameSection<ELFT>::addSection(InputSectionBase *C) {
auto *Sec = cast<EhInputSection>(C);
Sec->Parent = this;
- updateAlignment(Sec->Alignment);
+
+ Alignment = std::max(Alignment, Sec->Alignment);
Sections.push_back(Sec);
+
for (auto *DS : Sec->DependentSections)
DependentSections.push_back(DS);
@@ -508,23 +508,26 @@ void EhFrameSection<ELFT>::addSection(InputSectionBase *C) {
if (Sec->Pieces.empty())
return;
- if (Sec->NumRelocations) {
- if (Sec->AreRelocsRela)
- addSectionAux(Sec, Sec->template relas<ELFT>());
- else
- addSectionAux(Sec, Sec->template rels<ELFT>());
- return;
- }
- addSectionAux(Sec, makeArrayRef<Elf_Rela>(nullptr, nullptr));
+ if (Sec->NumRelocations == 0)
+ addSectionAux(Sec, makeArrayRef<Elf_Rela>(nullptr, nullptr));
+ else if (Sec->AreRelocsRela)
+ addSectionAux(Sec, Sec->template relas<ELFT>());
+ else
+ addSectionAux(Sec, Sec->template rels<ELFT>());
}
template <class ELFT>
static void writeCieFde(uint8_t *Buf, ArrayRef<uint8_t> D) {
memcpy(Buf, D.data(), D.size());
+ size_t Aligned = alignTo(D.size(), sizeof(typename ELFT::uint));
+
+ // Zero-clear trailing padding if it exists.
+ memset(Buf + D.size(), 0, Aligned - D.size());
+
// Fix the size field. -4 since size does not include the size field itself.
const endianness E = ELFT::TargetEndianness;
- write32<E>(Buf, alignTo(D.size(), sizeof(typename ELFT::uint)) - 4);
+ write32<E>(Buf, Aligned - 4);
}
template <class ELFT> void EhFrameSection<ELFT>::finalizeContents() {
@@ -532,13 +535,13 @@ template <class ELFT> void EhFrameSection<ELFT>::finalizeContents() {
return; // Already finalized.
size_t Off = 0;
- for (CieRecord *Cie : Cies) {
- Cie->Piece->OutputOff = Off;
- Off += alignTo(Cie->Piece->size(), Config->Wordsize);
+ for (CieRecord *Rec : CieRecords) {
+ Rec->Cie->OutputOff = Off;
+ Off += alignTo(Rec->Cie->Size, Config->Wordsize);
- for (EhSectionPiece *Fde : Cie->FdePieces) {
+ for (EhSectionPiece *Fde : Rec->Fdes) {
Fde->OutputOff = Off;
- Off += alignTo(Fde->size(), Config->Wordsize);
+ Off += alignTo(Fde->Size, Config->Wordsize);
}
}
@@ -586,11 +589,13 @@ uint64_t EhFrameSection<ELFT>::getFdePc(uint8_t *Buf, size_t FdeOff,
template <class ELFT> void EhFrameSection<ELFT>::writeTo(uint8_t *Buf) {
const endianness E = ELFT::TargetEndianness;
- for (CieRecord *Cie : Cies) {
- size_t CieOffset = Cie->Piece->OutputOff;
- writeCieFde<ELFT>(Buf + CieOffset, Cie->Piece->data());
- for (EhSectionPiece *Fde : Cie->FdePieces) {
+ // Write CIE and FDE records.
+ for (CieRecord *Rec : CieRecords) {
+ size_t CieOffset = Rec->Cie->OutputOff;
+ writeCieFde<ELFT>(Buf + CieOffset, Rec->Cie->data());
+
+ for (EhSectionPiece *Fde : Rec->Fdes) {
size_t Off = Fde->OutputOff;
writeCieFde<ELFT>(Buf + Off, Fde->data());
@@ -600,6 +605,9 @@ template <class ELFT> void EhFrameSection<ELFT>::writeTo(uint8_t *Buf) {
}
}
+ // Apply relocations. .eh_frame section contents are not contiguous
+ // in the output buffer, but relocateAlloc() still works because
+ // getOffset() takes care of discontiguous section pieces.
for (EhInputSection *S : Sections)
S->relocateAlloc(Buf, nullptr);
@@ -607,9 +615,9 @@ template <class ELFT> void EhFrameSection<ELFT>::writeTo(uint8_t *Buf) {
// 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 (In<ELFT>::EhFrameHdr) {
- for (CieRecord *Cie : Cies) {
- uint8_t Enc = getFdeEncoding<ELFT>(Cie->Piece);
- for (SectionPiece *Fde : Cie->FdePieces) {
+ for (CieRecord *Rec : CieRecords) {
+ uint8_t Enc = getFdeEncoding<ELFT>(Rec->Cie);
+ for (EhSectionPiece *Fde : Rec->Fdes) {
uint64_t Pc = getFdePc(Buf, Fde->OutputOff, Enc);
uint64_t FdeVA = getParent()->Addr + Fde->OutputOff;
In<ELFT>::EhFrameHdr->addFde(Pc, FdeVA);
@@ -657,12 +665,18 @@ uint64_t GotSection::getGlobalDynOffset(const SymbolBody &B) const {
void GotSection::finalizeContents() { Size = NumEntries * Config->Wordsize; }
bool GotSection::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;
+ // We need to emit a GOT even if it's empty if there's a relocation that is
+ // relative to GOT(such as GOTOFFREL) or there's a symbol that points to a GOT
+ // (i.e. _GLOBAL_OFFSET_TABLE_).
+ return NumEntries == 0 && !HasGotOffRel && !ElfSym::GlobalOffsetTable;
}
-void GotSection::writeTo(uint8_t *Buf) { relocateAlloc(Buf, Buf + Size); }
+void GotSection::writeTo(uint8_t *Buf) {
+ // Buf points to the start of this section's buffer,
+ // whereas InputSectionBase::relocateAlloc() expects its argument
+ // to point to the start of the output section.
+ relocateAlloc(Buf - OutSecOff, Buf - OutSecOff + Size);
+}
MipsGotSection::MipsGotSection()
: SyntheticSection(SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL, SHT_PROGBITS, 16,
@@ -720,7 +734,7 @@ void MipsGotSection::addEntry(SymbolBody &Sym, int64_t Addend, RelExpr Expr) {
if (!A)
S.GotIndex = NewIndex;
};
- if (Sym.isPreemptible()) {
+ if (Sym.IsPreemptible) {
// Ignore addends for preemptible symbols. They got single GOT entry anyway.
AddEntry(Sym, 0, GlobalEntries);
Sym.IsInGlobalMipsGot = true;
@@ -812,9 +826,7 @@ unsigned MipsGotSection::getLocalEntriesNum() const {
LocalEntries32.size();
}
-void MipsGotSection::finalizeContents() {
- updateAllocSize();
-}
+void MipsGotSection::finalizeContents() { updateAllocSize(); }
void MipsGotSection::updateAllocSize() {
PageEntriesNum = 0;
@@ -838,9 +850,7 @@ bool MipsGotSection::empty() const {
return Config->Relocatable;
}
-uint64_t MipsGotSection::getGp() const {
- return ElfSym::MipsGp->getVA(0);
-}
+uint64_t MipsGotSection::getGp() const { return ElfSym::MipsGp->getVA(0); }
static uint64_t readUint(uint8_t *Buf) {
if (Config->Is64)
@@ -900,7 +910,7 @@ void MipsGotSection::writeTo(uint8_t *Buf) {
if (TlsIndexOff != -1U && !Config->Pic)
writeUint(Buf + TlsIndexOff, 1);
for (const SymbolBody *B : TlsEntries) {
- if (!B || B->isPreemptible())
+ if (!B || B->IsPreemptible)
continue;
uint64_t VA = B->getVA();
if (B->GotIndex != -1U) {
@@ -989,6 +999,7 @@ unsigned StringTableSection::addString(StringRef S, bool HashIt) {
void StringTableSection::writeTo(uint8_t *Buf) {
for (StringRef S : Strings) {
memcpy(Buf, S.data(), S.size());
+ Buf[S.size()] = '\0';
Buf += S.size() + 1;
}
}
@@ -1019,14 +1030,18 @@ DynamicSection<ELFT>::DynamicSection()
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->FilterList)
+ add({DT_FILTER, InX::DynStrTab->addString(S)});
for (StringRef S : Config->AuxiliaryList)
add({DT_AUXILIARY, InX::DynStrTab->addString(S)});
if (!Config->Rpath.empty())
add({Config->EnableNewDtags ? DT_RUNPATH : DT_RPATH,
InX::DynStrTab->addString(Config->Rpath)});
- for (SharedFile<ELFT> *F : Symtab<ELFT>::X->getSharedFiles())
+ for (InputFile *File : SharedFiles) {
+ SharedFile<ELFT> *F = cast<SharedFile<ELFT>>(File);
if (F->isNeeded())
add({DT_NEEDED, InX::DynStrTab->addString(F->SoName)});
+ }
if (!Config->SoName.empty())
add({DT_SONAME, InX::DynStrTab->addString(Config->SoName)});
@@ -1071,10 +1086,11 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
return; // Already finalized.
this->Link = InX::DynStrTab->getParent()->SectionIndex;
- if (In<ELFT>::RelaDyn->getParent()->Size > 0) {
+ if (In<ELFT>::RelaDyn->getParent() && !In<ELFT>::RelaDyn->empty()) {
bool IsRela = Config->IsRela;
add({IsRela ? DT_RELA : DT_REL, In<ELFT>::RelaDyn});
- add({IsRela ? DT_RELASZ : DT_RELSZ, In<ELFT>::RelaDyn->getParent()->Size});
+ add({IsRela ? DT_RELASZ : DT_RELSZ, In<ELFT>::RelaDyn->getParent(),
+ Entry::SecSize});
add({IsRela ? DT_RELAENT : DT_RELENT,
uint64_t(IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel))});
@@ -1087,11 +1103,20 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
add({IsRela ? DT_RELACOUNT : DT_RELCOUNT, NumRelativeRels});
}
}
- if (In<ELFT>::RelaPlt->getParent()->Size > 0) {
+ if (In<ELFT>::RelaPlt->getParent() && !In<ELFT>::RelaPlt->empty()) {
add({DT_JMPREL, In<ELFT>::RelaPlt});
- add({DT_PLTRELSZ, In<ELFT>::RelaPlt->getParent()->Size});
- add({Config->EMachine == EM_MIPS ? DT_MIPS_PLTGOT : DT_PLTGOT,
- InX::GotPlt});
+ add({DT_PLTRELSZ, In<ELFT>::RelaPlt->getParent(), Entry::SecSize});
+ switch (Config->EMachine) {
+ case EM_MIPS:
+ add({DT_MIPS_PLTGOT, In<ELFT>::GotPlt});
+ break;
+ case EM_SPARCV9:
+ add({DT_PLTGOT, In<ELFT>::Plt});
+ break;
+ default:
+ add({DT_PLTGOT, In<ELFT>::GotPlt});
+ break;
+ }
add({DT_PLTREL, uint64_t(Config->IsRela ? DT_RELA : DT_REL)});
}
@@ -1103,8 +1128,8 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
add({DT_TEXTREL, (uint64_t)0});
if (InX::GnuHashTab)
add({DT_GNU_HASH, InX::GnuHashTab});
- if (In<ELFT>::HashTab)
- add({DT_HASH, In<ELFT>::HashTab});
+ if (InX::HashTab)
+ add({DT_HASH, InX::HashTab});
if (Out::PreinitArray) {
add({DT_PREINIT_ARRAY, Out::PreinitArray});
@@ -1119,10 +1144,12 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
add({DT_FINI_ARRAYSZ, Out::FiniArray, Entry::SecSize});
}
- if (SymbolBody *B = Symtab<ELFT>::X->findInCurrentDSO(Config->Init))
- add({DT_INIT, B});
- if (SymbolBody *B = Symtab<ELFT>::X->findInCurrentDSO(Config->Fini))
- add({DT_FINI, B});
+ if (SymbolBody *B = Symtab->find(Config->Init))
+ if (B->isInCurrentDSO())
+ add({DT_INIT, B});
+ if (SymbolBody *B = Symtab->find(Config->Fini))
+ if (B->isInCurrentDSO())
+ add({DT_FINI, B});
bool HasVerNeed = In<ELFT>::VerNeed->getNeedNum() != 0;
if (HasVerNeed || In<ELFT>::VerDef)
@@ -1139,7 +1166,7 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
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_BASE_ADDRESS, Target->getImageBase()});
add({DT_MIPS_SYMTABNO, InX::DynSymTab->getNumSymbols()});
add({DT_MIPS_LOCAL_GOTNO, InX::MipsGot->getLocalEntriesNum()});
if (const SymbolBody *B = InX::MipsGot->getFirstGlobalEntry())
@@ -1151,10 +1178,10 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
add({DT_MIPS_RLD_MAP, InX::MipsRldMap});
}
- getParent()->Link = this->Link;
+ add({DT_NULL, (uint64_t)0});
- // +1 for DT_NULL
- this->Size = (Entries.size() + 1) * this->Entsize;
+ getParent()->Link = this->Link;
+ this->Size = Entries.size() * this->Entsize;
}
template <class ELFT> void DynamicSection<ELFT>::writeTo(uint8_t *Buf) {
@@ -1257,8 +1284,10 @@ template <class ELFT> unsigned RelocationSection<ELFT>::getRelocOffset() {
}
template <class ELFT> void RelocationSection<ELFT>::finalizeContents() {
- this->Link = InX::DynSymTab ? InX::DynSymTab->getParent()->SectionIndex
- : InX::SymTab->getParent()->SectionIndex;
+ // If all relocations are *RELATIVE they don't refer to any
+ // dynamic symbol and we don't need a dynamic symbol table. If that
+ // is the case, just use 0 as the link.
+ this->Link = InX::DynSymTab ? InX::DynSymTab->getParent()->SectionIndex : 0;
// Set required output section properties.
getParent()->Link = this->Link;
@@ -1287,10 +1316,6 @@ static bool sortMipsSymbols(const SymbolTableEntry &L,
return L.Symbol->GotIndex < R.Symbol->GotIndex;
}
-// Finalize a symbol table. The ELF spec requires that all local
-// symbols precede global symbols, so we sort symbol entries in this
-// function. (For .dynsym, we don't do that because symbols for
-// dynamic linking are inherently all globals.)
void SymbolTableBaseSection::finalizeContents() {
getParent()->Link = StrTabSec.getParent()->SectionIndex;
@@ -1315,6 +1340,9 @@ void SymbolTableBaseSection::finalizeContents() {
}
}
+// The ELF spec requires that all local symbols precede global symbols, so we
+// sort symbol entries in this function. (For .dynsym, we don't do that because
+// symbols for dynamic linking are inherently all globals.)
void SymbolTableBaseSection::postThunkContents() {
if (this->Type == SHT_DYNSYM)
return;
@@ -1337,18 +1365,24 @@ void SymbolTableBaseSection::addSymbol(SymbolBody *B) {
}
size_t SymbolTableBaseSection::getSymbolIndex(SymbolBody *Body) {
- auto I = llvm::find_if(Symbols, [&](const SymbolTableEntry &E) {
- if (E.Symbol == Body)
- return true;
- // This is used for -r, so we have to handle multiple section
- // symbols being combined.
- if (Body->Type == STT_SECTION && E.Symbol->Type == STT_SECTION)
- return Body->getOutputSection() == E.Symbol->getOutputSection();
- return false;
+ // Initializes symbol lookup tables lazily. This is used only
+ // for -r or -emit-relocs.
+ llvm::call_once(OnceFlag, [&] {
+ SymbolIndexMap.reserve(Symbols.size());
+ size_t I = 0;
+ for (const SymbolTableEntry &E : Symbols) {
+ if (E.Symbol->Type == STT_SECTION)
+ SectionIndexMap[E.Symbol->getOutputSection()] = ++I;
+ else
+ SymbolIndexMap[E.Symbol] = ++I;
+ }
});
- if (I == Symbols.end())
- return 0;
- return I - Symbols.begin() + 1;
+
+ // Section symbols are mapped based on their output sections
+ // to maintain their semantics.
+ if (Body->Type == STT_SECTION)
+ return SectionIndexMap.lookup(Body->getOutputSection());
+ return SymbolIndexMap.lookup(Body);
}
template <class ELFT>
@@ -1360,6 +1394,7 @@ SymbolTableSection<ELFT>::SymbolTableSection(StringTableSection &StrTabSec)
// Write the internal symbol table contents to the output symbol table.
template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) {
// The first entry is a null entry as per the ELF spec.
+ memset(Buf, 0, sizeof(Elf_Sym));
Buf += sizeof(Elf_Sym);
auto *ESym = reinterpret_cast<Elf_Sym *>(Buf);
@@ -1368,6 +1403,7 @@ template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) {
SymbolBody *Body = Ent.Symbol;
// Set st_info and st_other.
+ ESym->st_other = 0;
if (Body->isLocal()) {
ESym->setBindingAndType(STB_LOCAL, Body->Type);
} else {
@@ -1376,7 +1412,6 @@ template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) {
}
ESym->st_name = Ent.StrTabOffset;
- ESym->st_size = Body->getSize<ELFT>();
// Set a section index.
if (const OutputSection *OutSec = Body->getOutputSection())
@@ -1385,6 +1420,18 @@ template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) {
ESym->st_shndx = SHN_ABS;
else if (isa<DefinedCommon>(Body))
ESym->st_shndx = SHN_COMMON;
+ else
+ ESym->st_shndx = SHN_UNDEF;
+
+ // Copy symbol size if it is a defined symbol. st_size is not significant
+ // for undefined symbols, so whether copying it or not is up to us if that's
+ // the case. We'll leave it as zero because by not setting a value, we can
+ // get the exact same outputs for two sets of input files that differ only
+ // in undefined symbol size in DSOs.
+ if (ESym->st_shndx == SHN_UNDEF)
+ ESym->st_size = 0;
+ else
+ ESym->st_size = Body->getSize<ELFT>();
// st_value is usually an address of a symbol, but that has a
// special meaining for uninstantiated common symbols (this can
@@ -1560,7 +1607,11 @@ void GnuHashTableSection::addSymbols(std::vector<SymbolTableEntry> &V) {
// its type correctly.
std::vector<SymbolTableEntry>::iterator Mid =
std::stable_partition(V.begin(), V.end(), [](const SymbolTableEntry &S) {
- return S.Symbol->isUndefined();
+ // Shared symbols that this executable preempts are special. The dynamic
+ // linker has to look them up, so they have to be in the hash table.
+ if (auto *SS = dyn_cast<SharedSymbol>(S.Symbol))
+ return SS->CopyRelSec == nullptr && !SS->NeedsPltAddr;
+ return !S.Symbol->isInCurrentDSO();
});
if (Mid == V.end())
return;
@@ -1581,37 +1632,31 @@ void GnuHashTableSection::addSymbols(std::vector<SymbolTableEntry> &V) {
V.push_back({Ent.Body, Ent.StrTabOffset});
}
-template <class ELFT>
-HashTableSection<ELFT>::HashTableSection()
+HashTableSection::HashTableSection()
: SyntheticSection(SHF_ALLOC, SHT_HASH, 4, ".hash") {
this->Entsize = 4;
}
-template <class ELFT> void HashTableSection<ELFT>::finalizeContents() {
+void HashTableSection::finalizeContents() {
getParent()->Link = InX::DynSymTab->getParent()->SectionIndex;
- unsigned NumEntries = 2; // nbucket and nchain.
+ unsigned NumEntries = 2; // nbucket and nchain.
NumEntries += InX::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 += InX::DynSymTab->getNumSymbols();
this->Size = NumEntries * 4;
}
-template <class ELFT> void HashTableSection<ELFT>::writeTo(uint8_t *Buf) {
- // A 32-bit integer type in the target endianness.
- typedef typename ELFT::Word Elf_Word;
-
+void HashTableSection::writeTo(uint8_t *Buf) {
unsigned NumSymbols = InX::DynSymTab->getNumSymbols();
- auto *P = reinterpret_cast<Elf_Word *>(Buf);
- *P++ = NumSymbols; // nbucket
- *P++ = NumSymbols; // nchain
+ uint32_t *P = reinterpret_cast<uint32_t *>(Buf);
+ write32(P++, NumSymbols, Config->Endianness); // nbucket
+ write32(P++, NumSymbols, Config->Endianness); // nchain
- Elf_Word *Buckets = P;
- Elf_Word *Chains = P + NumSymbols;
+ uint32_t *Buckets = P;
+ uint32_t *Chains = P + NumSymbols;
for (const SymbolTableEntry &S : InX::DynSymTab->getSymbols()) {
SymbolBody *Body = S.Symbol;
@@ -1619,13 +1664,18 @@ template <class ELFT> void HashTableSection<ELFT>::writeTo(uint8_t *Buf) {
unsigned I = Body->DynsymIndex;
uint32_t Hash = hashSysV(Name) % NumSymbols;
Chains[I] = Buckets[Hash];
- Buckets[Hash] = I;
+ write32(Buckets + Hash, I, Config->Endianness);
}
}
PltSection::PltSection(size_t S)
: SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, ".plt"),
- HeaderSize(S) {}
+ HeaderSize(S) {
+ // The PLT needs to be writable on SPARC as the dynamic linker will
+ // modify the instructions in the PLT entries.
+ if (Config->EMachine == EM_SPARCV9)
+ this->Flags |= SHF_WRITE;
+}
void PltSection::writeTo(uint8_t *Buf) {
// At beginning of PLT but not the IPLT, we have code to call the dynamic
@@ -1678,36 +1728,29 @@ unsigned PltSection::getPltRelocOff() const {
return (HeaderSize == 0) ? InX::Plt->getSize() : 0;
}
-GdbIndexSection::GdbIndexSection()
- : SyntheticSection(0, SHT_PROGBITS, 1, ".gdb_index"),
- StringPool(llvm::StringTableBuilder::ELF) {}
-
-// 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;
+// The string hash function for .gdb_index.
+static uint32_t computeGdbHash(StringRef S) {
+ uint32_t H = 0;
+ for (uint8_t C : S)
+ H = H * 67 + tolower(C) - 113;
+ return H;
}
-static std::vector<CompilationUnitEntry> readCuList(DWARFContext &Dwarf,
- InputSection *Sec) {
- std::vector<CompilationUnitEntry> Ret;
- for (std::unique_ptr<DWARFCompileUnit> &CU : Dwarf.compile_units())
- Ret.push_back({Sec->OutSecOff + CU->getOffset(), CU->getLength() + 4});
+static std::vector<GdbIndexChunk::CuEntry> readCuList(DWARFContext &Dwarf) {
+ std::vector<GdbIndexChunk::CuEntry> Ret;
+ for (std::unique_ptr<DWARFCompileUnit> &Cu : Dwarf.compile_units())
+ Ret.push_back({Cu->getOffset(), Cu->getLength() + 4});
return Ret;
}
-static std::vector<AddressEntry> readAddressArea(DWARFContext &Dwarf,
- InputSection *Sec) {
- std::vector<AddressEntry> Ret;
+static std::vector<GdbIndexChunk::AddressEntry>
+readAddressAreas(DWARFContext &Dwarf, InputSection *Sec) {
+ std::vector<GdbIndexChunk::AddressEntry> Ret;
- uint32_t CurrentCu = 0;
- for (std::unique_ptr<DWARFCompileUnit> &CU : Dwarf.compile_units()) {
+ uint32_t CuIdx = 0;
+ for (std::unique_ptr<DWARFCompileUnit> &Cu : Dwarf.compile_units()) {
DWARFAddressRangesVector Ranges;
- CU->collectAddressRanges(Ranges);
+ Cu->collectAddressRanges(Ranges);
ArrayRef<InputSectionBase *> Sections = Sec->File->getSections();
for (DWARFAddressRange &R : Ranges) {
@@ -1717,24 +1760,29 @@ static std::vector<AddressEntry> readAddressArea(DWARFContext &Dwarf,
// Range list with zero size has no effect.
if (R.LowPC == R.HighPC)
continue;
- Ret.push_back({cast<InputSection>(S), R.LowPC, R.HighPC, CurrentCu});
+ auto *IS = cast<InputSection>(S);
+ uint64_t Offset = IS->getOffsetInFile();
+ Ret.push_back({IS, R.LowPC - Offset, R.HighPC - Offset, CuIdx});
}
- ++CurrentCu;
+ ++CuIdx;
}
return Ret;
}
-static std::vector<NameTypeEntry> readPubNamesAndTypes(DWARFContext &Dwarf,
- bool IsLE) {
- StringRef Data[] = {Dwarf.getGnuPubNamesSection(),
- Dwarf.getGnuPubTypesSection()};
+static std::vector<GdbIndexChunk::NameTypeEntry>
+readPubNamesAndTypes(DWARFContext &Dwarf) {
+ StringRef Sec1 = Dwarf.getDWARFObj().getGnuPubNamesSection();
+ StringRef Sec2 = Dwarf.getDWARFObj().getGnuPubTypesSection();
- std::vector<NameTypeEntry> Ret;
- for (StringRef D : Data) {
- DWARFDebugPubTable PubTable(D, IsLE, true);
- for (const DWARFDebugPubTable::Set &Set : PubTable.getData())
- for (const DWARFDebugPubTable::Entry &Ent : Set.Entries)
- Ret.push_back({Ent.Name, Ent.Descriptor.toBits()});
+ std::vector<GdbIndexChunk::NameTypeEntry> Ret;
+ for (StringRef Sec : {Sec1, Sec2}) {
+ DWARFDebugPubTable Table(Sec, Config->IsLE, true);
+ for (const DWARFDebugPubTable::Set &Set : Table.getData()) {
+ for (const DWARFDebugPubTable::Entry &Ent : Set.Entries) {
+ CachedHashStringRef S(Ent.Name, computeGdbHash(Ent.Name));
+ Ret.push_back({S, Ent.Descriptor.toBits()});
+ }
+ }
}
return Ret;
}
@@ -1743,119 +1791,146 @@ static std::vector<InputSection *> getDebugInfoSections() {
std::vector<InputSection *> Ret;
for (InputSectionBase *S : InputSections)
if (InputSection *IS = dyn_cast<InputSection>(S))
- if (IS->getParent() && IS->Name == ".debug_info")
+ if (IS->Name == ".debug_info")
Ret.push_back(IS);
return Ret;
}
-void GdbIndexSection::buildIndex() {
- std::vector<InputSection *> V = getDebugInfoSections();
- if (V.empty())
- return;
+void GdbIndexSection::fixCuIndex() {
+ uint32_t Idx = 0;
+ for (GdbIndexChunk &Chunk : Chunks) {
+ for (GdbIndexChunk::AddressEntry &Ent : Chunk.AddressAreas)
+ Ent.CuIndex += Idx;
+ Idx += Chunk.CompilationUnits.size();
+ }
+}
- for (InputSection *Sec : V)
- Chunks.push_back(readDwarf(Sec));
+std::vector<std::vector<uint32_t>> GdbIndexSection::createCuVectors() {
+ std::vector<std::vector<uint32_t>> Ret;
+ uint32_t Idx = 0;
+ uint32_t Off = 0;
- uint32_t CuId = 0;
- for (GdbIndexChunk &D : Chunks) {
- for (AddressEntry &E : D.AddressArea)
- E.CuIndex += CuId;
-
- // Populate constant pool area.
- for (NameTypeEntry &NameType : D.NamesAndTypes) {
- uint32_t Hash = hash(NameType.Name);
- size_t Offset = StringPool.add(NameType.Name);
-
- bool IsNew;
- GdbSymbol *Sym;
- std::tie(IsNew, Sym) = SymbolTable.add(Hash, Offset);
- if (IsNew) {
- Sym->CuVectorIndex = CuVectors.size();
- CuVectors.resize(CuVectors.size() + 1);
+ for (GdbIndexChunk &Chunk : Chunks) {
+ for (GdbIndexChunk::NameTypeEntry &Ent : Chunk.NamesAndTypes) {
+ GdbSymbol *&Sym = Symbols[Ent.Name];
+ if (!Sym) {
+ Sym = make<GdbSymbol>(GdbSymbol{Ent.Name.hash(), Off, Ret.size()});
+ Off += Ent.Name.size() + 1;
+ Ret.push_back({});
}
- CuVectors[Sym->CuVectorIndex].insert(CuId | (NameType.Type << 24));
+ // gcc 5.4.1 produces a buggy .debug_gnu_pubnames that contains
+ // duplicate entries, so we want to dedup them.
+ std::vector<uint32_t> &Vec = Ret[Sym->CuVectorIndex];
+ uint32_t Val = (Ent.Type << 24) | Idx;
+ if (Vec.empty() || Vec.back() != Val)
+ Vec.push_back(Val);
}
-
- CuId += D.CompilationUnits.size();
+ Idx += Chunk.CompilationUnits.size();
}
+
+ StringPoolSize = Off;
+ return Ret;
}
-GdbIndexChunk GdbIndexSection::readDwarf(InputSection *Sec) {
- Expected<std::unique_ptr<object::ObjectFile>> Obj =
- object::ObjectFile::createObjectFile(Sec->File->MB);
- if (!Obj) {
- error(toString(Sec->File) + ": error creating DWARF context");
- return {};
- }
+template <class ELFT> GdbIndexSection *elf::createGdbIndex() {
+ // Gather debug info to create a .gdb_index section.
+ std::vector<InputSection *> Sections = getDebugInfoSections();
+ std::vector<GdbIndexChunk> Chunks(Sections.size());
+
+ parallelForEachN(0, Chunks.size(), [&](size_t I) {
+ ObjFile<ELFT> *File = Sections[I]->getFile<ELFT>();
+ DWARFContext Dwarf(make_unique<LLDDwarfObj<ELFT>>(File));
- DWARFContextInMemory Dwarf(*Obj.get());
+ Chunks[I].DebugInfoSec = Sections[I];
+ Chunks[I].CompilationUnits = readCuList(Dwarf);
+ Chunks[I].AddressAreas = readAddressAreas(Dwarf, Sections[I]);
+ Chunks[I].NamesAndTypes = readPubNamesAndTypes(Dwarf);
+ });
- GdbIndexChunk Ret;
- Ret.CompilationUnits = readCuList(Dwarf, Sec);
- Ret.AddressArea = readAddressArea(Dwarf, Sec);
- Ret.NamesAndTypes = readPubNamesAndTypes(Dwarf, Config->IsLE);
- return Ret;
+ // .debug_gnu_pub{names,types} are useless in executables.
+ // They are present in input object files solely for creating
+ // a .gdb_index. So we can remove it from the output.
+ for (InputSectionBase *S : InputSections)
+ if (S->Name == ".debug_gnu_pubnames" || S->Name == ".debug_gnu_pubtypes")
+ S->Live = false;
+
+ // Create a .gdb_index and returns it.
+ return make<GdbIndexSection>(std::move(Chunks));
}
-static size_t getCuSize(std::vector<GdbIndexChunk> &C) {
+static size_t getCuSize(ArrayRef<GdbIndexChunk> Arr) {
size_t Ret = 0;
- for (GdbIndexChunk &D : C)
+ for (const GdbIndexChunk &D : Arr)
Ret += D.CompilationUnits.size();
return Ret;
}
-static size_t getAddressAreaSize(std::vector<GdbIndexChunk> &C) {
+static size_t getAddressAreaSize(ArrayRef<GdbIndexChunk> Arr) {
size_t Ret = 0;
- for (GdbIndexChunk &D : C)
- Ret += D.AddressArea.size();
+ for (const GdbIndexChunk &D : Arr)
+ Ret += D.AddressAreas.size();
return Ret;
}
-void GdbIndexSection::finalizeContents() {
- if (Finalized)
- return;
- Finalized = true;
+std::vector<GdbSymbol *> GdbIndexSection::createGdbSymtab() {
+ uint32_t Size = NextPowerOf2(Symbols.size() * 4 / 3);
+ if (Size < 1024)
+ Size = 1024;
- buildIndex();
+ uint32_t Mask = Size - 1;
+ std::vector<GdbSymbol *> Ret(Size);
- SymbolTable.finalizeContents();
+ for (auto &KV : Symbols) {
+ GdbSymbol *Sym = KV.second;
+ uint32_t I = Sym->NameHash & Mask;
+ uint32_t Step = ((Sym->NameHash * 17) & Mask) | 1;
- // GdbIndex header consist from version fields
- // and 5 more fields with different kinds of offsets.
- CuTypesOffset = CuListOffset + getCuSize(Chunks) * CompilationUnitSize;
- SymTabOffset = CuTypesOffset + getAddressAreaSize(Chunks) * AddressEntrySize;
+ while (Ret[I])
+ I = (I + Step) & Mask;
+ Ret[I] = Sym;
+ }
+ return Ret;
+}
- ConstantPoolOffset =
- SymTabOffset + SymbolTable.getCapacity() * SymTabEntrySize;
+GdbIndexSection::GdbIndexSection(std::vector<GdbIndexChunk> &&C)
+ : SyntheticSection(0, SHT_PROGBITS, 1, ".gdb_index"), Chunks(std::move(C)) {
+ fixCuIndex();
+ CuVectors = createCuVectors();
+ GdbSymtab = createGdbSymtab();
- for (std::set<uint32_t> &CuVec : CuVectors) {
- CuVectorsOffset.push_back(CuVectorsSize);
- CuVectorsSize += OffsetTypeSize * (CuVec.size() + 1);
- }
- StringPoolOffset = ConstantPoolOffset + CuVectorsSize;
+ // Compute offsets early to know the section size.
+ // Each chunk size needs to be in sync with what we write in writeTo.
+ CuTypesOffset = CuListOffset + getCuSize(Chunks) * 16;
+ SymtabOffset = CuTypesOffset + getAddressAreaSize(Chunks) * 20;
+ ConstantPoolOffset = SymtabOffset + GdbSymtab.size() * 8;
- StringPool.finalizeInOrder();
+ size_t Off = 0;
+ for (ArrayRef<uint32_t> Vec : CuVectors) {
+ CuVectorOffsets.push_back(Off);
+ Off += (Vec.size() + 1) * 4;
+ }
+ StringPoolOffset = ConstantPoolOffset + Off;
}
size_t GdbIndexSection::getSize() const {
- const_cast<GdbIndexSection *>(this)->finalizeContents();
- return StringPoolOffset + StringPool.getSize();
+ return StringPoolOffset + StringPoolSize;
}
void GdbIndexSection::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.
+ // Write the section header.
+ write32le(Buf, 7);
+ write32le(Buf + 4, CuListOffset);
+ write32le(Buf + 8, CuTypesOffset);
+ write32le(Buf + 12, CuTypesOffset);
+ write32le(Buf + 16, SymtabOffset);
+ write32le(Buf + 20, ConstantPoolOffset);
Buf += 24;
// Write the CU list.
for (GdbIndexChunk &D : Chunks) {
- for (CompilationUnitEntry &Cu : D.CompilationUnits) {
- write64le(Buf, Cu.CuOffset);
+ for (GdbIndexChunk::CuEntry &Cu : D.CompilationUnits) {
+ write64le(Buf, D.DebugInfoSec->OutSecOff + Cu.CuOffset);
write64le(Buf + 8, Cu.CuLength);
Buf += 16;
}
@@ -1863,7 +1938,7 @@ void GdbIndexSection::writeTo(uint8_t *Buf) {
// Write the address area.
for (GdbIndexChunk &D : Chunks) {
- for (AddressEntry &E : D.AddressArea) {
+ for (GdbIndexChunk::AddressEntry &E : D.AddressAreas) {
uint64_t BaseAddr =
E.Section->getParent()->Addr + E.Section->getOffset(0);
write64le(Buf, BaseAddr + E.LowAddress);
@@ -1874,34 +1949,35 @@ void GdbIndexSection::writeTo(uint8_t *Buf) {
}
// Write the symbol table.
- for (size_t I = 0; I < SymbolTable.getCapacity(); ++I) {
- GdbSymbol *Sym = SymbolTable.getSymbol(I);
+ for (GdbSymbol *Sym : GdbSymtab) {
if (Sym) {
- size_t NameOffset =
- Sym->NameOffset + StringPoolOffset - ConstantPoolOffset;
- size_t CuVectorOffset = CuVectorsOffset[Sym->CuVectorIndex];
- write32le(Buf, NameOffset);
- write32le(Buf + 4, CuVectorOffset);
+ write32le(Buf, Sym->NameOffset + StringPoolOffset - ConstantPoolOffset);
+ write32le(Buf + 4, CuVectorOffsets[Sym->CuVectorIndex]);
}
Buf += 8;
}
- // Write the CU vectors into the constant pool.
- for (std::set<uint32_t> &CuVec : CuVectors) {
- write32le(Buf, CuVec.size());
+ // Write the CU vectors.
+ for (ArrayRef<uint32_t> Vec : CuVectors) {
+ write32le(Buf, Vec.size());
Buf += 4;
- for (uint32_t Val : CuVec) {
+ for (uint32_t Val : Vec) {
write32le(Buf, Val);
Buf += 4;
}
}
- StringPool.write(Buf);
+ // Write the string pool.
+ for (auto &KV : Symbols) {
+ CachedHashStringRef S = KV.first;
+ GdbSymbol *Sym = KV.second;
+ size_t Off = Sym->NameOffset;
+ memcpy(Buf + Off, S.val().data(), S.size());
+ Buf[Off + S.size()] = '\0';
+ }
}
-bool GdbIndexSection::empty() const {
- return !Out::DebugInfo;
-}
+bool GdbIndexSection::empty() const { return !Out::DebugInfo; }
template <class ELFT>
EhFrameHeader<ELFT>::EhFrameHeader()
@@ -2058,7 +2134,7 @@ void VersionNeedSection<ELFT>::addSymbol(SharedSymbol *SS) {
return;
}
- auto *File = cast<SharedFile<ELFT>>(SS->File);
+ SharedFile<ELFT> *File = SS->getFile<ELFT>();
// 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
@@ -2128,23 +2204,21 @@ template <class ELFT> bool VersionNeedSection<ELFT>::empty() const {
return getNeedNum() == 0;
}
-MergeSyntheticSection::MergeSyntheticSection(StringRef Name, uint32_t Type,
- uint64_t Flags, uint32_t Alignment)
- : SyntheticSection(Flags, Type, Alignment, Name),
- Builder(StringTableBuilder::RAW, Alignment) {}
-
void MergeSyntheticSection::addSection(MergeInputSection *MS) {
MS->Parent = this;
Sections.push_back(MS);
}
-void MergeSyntheticSection::writeTo(uint8_t *Buf) { Builder.write(Buf); }
+MergeTailSection::MergeTailSection(StringRef Name, uint32_t Type,
+ uint64_t Flags, uint32_t Alignment)
+ : MergeSyntheticSection(Name, Type, Flags, Alignment),
+ Builder(StringTableBuilder::RAW, Alignment) {}
-bool MergeSyntheticSection::shouldTailMerge() const {
- return (this->Flags & SHF_STRINGS) && Config->Optimize >= 2;
-}
+size_t MergeTailSection::getSize() const { return Builder.getSize(); }
-void MergeSyntheticSection::finalizeTailMerge() {
+void MergeTailSection::writeTo(uint8_t *Buf) { Builder.write(Buf); }
+
+void MergeTailSection::finalizeContents() {
// Add all string pieces to the string table builder to create section
// contents.
for (MergeInputSection *Sec : Sections)
@@ -2164,48 +2238,99 @@ void MergeSyntheticSection::finalizeTailMerge() {
Sec->Pieces[I].OutputOff = Builder.getOffset(Sec->getData(I));
}
-void MergeSyntheticSection::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 *Sec : Sections)
+void MergeNoTailSection::writeTo(uint8_t *Buf) {
+ for (size_t I = 0; I < NumShards; ++I)
+ Shards[I].write(Buf + ShardOffsets[I]);
+}
+
+// This function is very hot (i.e. it can take several seconds to finish)
+// because sometimes the number of inputs is in an order of magnitude of
+// millions. So, we use multi-threading.
+//
+// For any strings S and T, we know S is not mergeable with T if S's hash
+// value is different from T's. If that's the case, we can safely put S and
+// T into different string builders without worrying about merge misses.
+// We do it in parallel.
+void MergeNoTailSection::finalizeContents() {
+ // Initializes string table builders.
+ for (size_t I = 0; I < NumShards; ++I)
+ Shards.emplace_back(StringTableBuilder::RAW, Alignment);
+
+ // Concurrency level. Must be a power of 2 to avoid expensive modulo
+ // operations in the following tight loop.
+ size_t Concurrency = 1;
+ if (ThreadsEnabled)
+ Concurrency =
+ std::min<size_t>(PowerOf2Floor(hardware_concurrency()), NumShards);
+
+ // Add section pieces to the builders.
+ parallelForEachN(0, Concurrency, [&](size_t ThreadId) {
+ for (MergeInputSection *Sec : Sections) {
+ for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I) {
+ if (!Sec->Pieces[I].Live)
+ continue;
+ CachedHashStringRef Str = Sec->getData(I);
+ size_t ShardId = getShardId(Str.hash());
+ if ((ShardId & (Concurrency - 1)) == ThreadId)
+ Sec->Pieces[I].OutputOff = Shards[ShardId].add(Str);
+ }
+ }
+ });
+
+ // Compute an in-section offset for each shard.
+ size_t Off = 0;
+ for (size_t I = 0; I < NumShards; ++I) {
+ Shards[I].finalizeInOrder();
+ if (Shards[I].getSize() > 0)
+ Off = alignTo(Off, Alignment);
+ ShardOffsets[I] = Off;
+ Off += Shards[I].getSize();
+ }
+ Size = Off;
+
+ // So far, section pieces have offsets from beginning of shards, but
+ // we want offsets from beginning of the whole section. Fix them.
+ parallelForEach(Sections, [&](MergeInputSection *Sec) {
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));
+ Sec->Pieces[I].OutputOff +=
+ ShardOffsets[getShardId(Sec->getData(I).hash())];
+ });
+}
- Builder.finalizeInOrder();
+static MergeSyntheticSection *createMergeSynthetic(StringRef Name,
+ uint32_t Type,
+ uint64_t Flags,
+ uint32_t Alignment) {
+ bool ShouldTailMerge = (Flags & SHF_STRINGS) && Config->Optimize >= 2;
+ if (ShouldTailMerge)
+ return make<MergeTailSection>(Name, Type, Flags, Alignment);
+ return make<MergeNoTailSection>(Name, Type, Flags, Alignment);
}
-void MergeSyntheticSection::finalizeContents() {
- if (shouldTailMerge())
- finalizeTailMerge();
- else
- finalizeNoTailMerge();
-}
-
-size_t MergeSyntheticSection::getSize() const {
- return Builder.getSize();
-}
-
-// This function decompresses compressed sections and scans over the input
-// sections to create mergeable synthetic sections. It removes
-// MergeInputSections from the input section array and adds new synthetic
-// sections at the location of the first input section that it replaces. It then
-// finalizes each synthetic section in order to compute an output offset for
-// each piece of each input section.
-void elf::decompressAndMergeSections() {
- // splitIntoPieces needs to be called on each MergeInputSection before calling
- // finalizeContents(). Do that first.
- parallelForEach(InputSections.begin(), InputSections.end(),
- [](InputSectionBase *S) {
- if (!S->Live)
- return;
- if (Decompressor::isCompressedELFSection(S->Flags, S->Name))
- S->uncompress();
- if (auto *MS = dyn_cast<MergeInputSection>(S))
- MS->splitIntoPieces();
- });
+// Debug sections may be compressed by zlib. Uncompress if exists.
+void elf::decompressSections() {
+ parallelForEach(InputSections, [](InputSectionBase *Sec) {
+ if (Sec->Live)
+ Sec->maybeUncompress();
+ });
+}
+
+// This function scans over the inputsections to create mergeable
+// synthetic sections.
+//
+// It removes MergeInputSections from the input section array and adds
+// new synthetic sections at the location of the first input section
+// that it replaces. It then finalizes each synthetic section in order
+// to compute an output offset for each piece of each input section.
+void elf::mergeSections() {
+ // splitIntoPieces needs to be called on each MergeInputSection
+ // before calling finalizeContents(). Do that first.
+ parallelForEach(InputSections, [](InputSectionBase *Sec) {
+ if (Sec->Live)
+ if (auto *S = dyn_cast<MergeInputSection>(Sec))
+ S->splitIntoPieces();
+ });
std::vector<MergeSyntheticSection *> MergeSections;
for (InputSectionBase *&S : InputSections) {
@@ -2219,16 +2344,15 @@ void elf::decompressAndMergeSections() {
continue;
StringRef OutsecName = getOutputSectionName(MS->Name);
- uint64_t Flags = MS->Flags & ~(uint64_t)SHF_GROUP;
uint32_t Alignment = std::max<uint32_t>(MS->Alignment, MS->Entsize);
auto I = llvm::find_if(MergeSections, [=](MergeSyntheticSection *Sec) {
- return Sec->Name == OutsecName && Sec->Flags == Flags &&
+ return Sec->Name == OutsecName && Sec->Flags == MS->Flags &&
Sec->Alignment == Alignment;
});
if (I == MergeSections.end()) {
MergeSyntheticSection *Syn =
- make<MergeSyntheticSection>(OutsecName, MS->Type, Flags, Alignment);
+ createMergeSynthetic(OutsecName, MS->Type, MS->Flags, Alignment);
MergeSections.push_back(Syn);
I = std::prev(MergeSections.end());
S = Syn;
@@ -2263,11 +2387,12 @@ void ARMExidxSentinelSection::writeTo(uint8_t *Buf) {
// sentinel last. We need to find the InputSection that precedes the
// sentinel. By construction the Sentinel is in the last
// InputSectionDescription as the InputSection that precedes it.
- OutputSectionCommand *C = Script->getCmd(getParent());
- auto ISD = std::find_if(C->Commands.rbegin(), C->Commands.rend(),
- [](const BaseCommand *Base) {
- return isa<InputSectionDescription>(Base);
- });
+ OutputSection *C = getParent();
+ auto ISD =
+ std::find_if(C->SectionCommands.rbegin(), C->SectionCommands.rend(),
+ [](const BaseCommand *Base) {
+ return isa<InputSectionDescription>(Base);
+ });
auto L = cast<InputSectionDescription>(*ISD);
InputSection *Highest = L->Sections[L->Sections.size() - 2];
InputSection *LS = Highest->getLinkOrderDep();
@@ -2285,7 +2410,7 @@ ThunkSection::ThunkSection(OutputSection *OS, uint64_t Off)
}
void ThunkSection::addThunk(Thunk *T) {
- uint64_t Off = alignTo(Size, T->alignment);
+ uint64_t Off = alignTo(Size, T->Alignment);
T->Offset = Off;
Thunks.push_back(T);
T->addSymbols(*this);
@@ -2306,7 +2431,6 @@ InputSection *InX::ARMAttributes;
BssSection *InX::Bss;
BssSection *InX::BssRelRo;
BuildIdSection *InX::BuildId;
-InputSection *InX::Common;
SyntheticSection *InX::Dynamic;
StringTableSection *InX::DynStrTab;
SymbolTableBaseSection *InX::DynSymTab;
@@ -2315,6 +2439,7 @@ GdbIndexSection *InX::GdbIndex;
GotSection *InX::Got;
GotPltSection *InX::GotPlt;
GnuHashTableSection *InX::GnuHashTab;
+HashTableSection *InX::HashTab;
IgotPltSection *InX::IgotPlt;
MipsGotSection *InX::MipsGot;
MipsRldMapSection *InX::MipsRldMap;
@@ -2324,15 +2449,20 @@ StringTableSection *InX::ShStrTab;
StringTableSection *InX::StrTab;
SymbolTableBaseSection *InX::SymTab;
+template GdbIndexSection *elf::createGdbIndex<ELF32LE>();
+template GdbIndexSection *elf::createGdbIndex<ELF32BE>();
+template GdbIndexSection *elf::createGdbIndex<ELF64LE>();
+template GdbIndexSection *elf::createGdbIndex<ELF64BE>();
+
template void PltSection::addEntry<ELF32LE>(SymbolBody &Sym);
template void PltSection::addEntry<ELF32BE>(SymbolBody &Sym);
template void PltSection::addEntry<ELF64LE>(SymbolBody &Sym);
template void PltSection::addEntry<ELF64BE>(SymbolBody &Sym);
-template InputSection *elf::createCommonSection<ELF32LE>();
-template InputSection *elf::createCommonSection<ELF32BE>();
-template InputSection *elf::createCommonSection<ELF64LE>();
-template InputSection *elf::createCommonSection<ELF64BE>();
+template void elf::createCommonSections<ELF32LE>();
+template void elf::createCommonSections<ELF32BE>();
+template void elf::createCommonSections<ELF64LE>();
+template void elf::createCommonSections<ELF64BE>();
template MergeInputSection *elf::createCommentSection<ELF32LE>();
template MergeInputSection *elf::createCommentSection<ELF32BE>();
@@ -2369,11 +2499,6 @@ template class elf::SymbolTableSection<ELF32BE>;
template class elf::SymbolTableSection<ELF64LE>;
template class elf::SymbolTableSection<ELF64BE>;
-template class elf::HashTableSection<ELF32LE>;
-template class elf::HashTableSection<ELF32BE>;
-template class elf::HashTableSection<ELF64LE>;
-template class elf::HashTableSection<ELF64BE>;
-
template class elf::EhFrameHeader<ELF32LE>;
template class elf::EhFrameHeader<ELF32BE>;
template class elf::EhFrameHeader<ELF64LE>;
diff --git a/ELF/SyntheticSections.h b/ELF/SyntheticSections.h
index be9a43c81..c6a51ad33 100644
--- a/ELF/SyntheticSections.h
+++ b/ELF/SyntheticSections.h
@@ -27,8 +27,6 @@
#include "llvm/ADT/MapVector.h"
#include "llvm/MC/StringTableBuilder.h"
-#include <set>
-
namespace lld {
namespace elf {
@@ -59,8 +57,8 @@ public:
};
struct CieRecord {
- EhSectionPiece *Piece = nullptr;
- std::vector<EhSectionPiece *> FdePieces;
+ EhSectionPiece *Cie = nullptr;
+ std::vector<EhSectionPiece *> Fdes;
};
// Section for .eh_frame.
@@ -69,11 +67,6 @@ template <class ELFT> class EhFrameSection final : public SyntheticSection {
typedef typename ELFT::Rel Elf_Rel;
typedef typename ELFT::Rela Elf_Rela;
- void updateAlignment(uint64_t Val) {
- if (Val > this->Alignment)
- this->Alignment = Val;
- }
-
public:
EhFrameSection();
void writeTo(uint8_t *Buf) override;
@@ -100,10 +93,11 @@ private:
uint64_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc);
- std::vector<CieRecord *> Cies;
+ std::vector<CieRecord *> CieRecords;
// CIE records are uniquified by their contents and personality functions.
- llvm::DenseMap<std::pair<ArrayRef<uint8_t>, SymbolBody *>, CieRecord> CieMap;
+ llvm::DenseMap<std::pair<ArrayRef<uint8_t>, SymbolBody *>, CieRecord *>
+ CieMap;
};
class GotSection : public SyntheticSection {
@@ -158,14 +152,13 @@ private:
// respectively.
class BssSection final : public SyntheticSection {
public:
- BssSection(StringRef Name);
+ BssSection(StringRef Name, uint64_t Size, uint32_t Alignment);
void writeTo(uint8_t *) override {}
bool empty() const override { return getSize() == 0; }
- size_t reserveSpace(uint64_t Size, uint32_t Alignment);
size_t getSize() const override { return Size; }
private:
- uint64_t Size = 0;
+ uint64_t Size;
};
class MipsGotSection final : public SyntheticSection {
@@ -419,6 +412,10 @@ protected:
std::vector<SymbolTableEntry> Symbols;
StringTableSection &StrTabSec;
+
+ llvm::once_flag OnceFlag;
+ llvm::DenseMap<SymbolBody *, size_t> SymbolIndexMap;
+ llvm::DenseMap<OutputSection *, size_t> SectionIndexMap;
};
template <class ELFT>
@@ -461,7 +458,7 @@ private:
size_t Size = 0;
};
-template <class ELFT> class HashTableSection final : public SyntheticSection {
+class HashTableSection final : public SyntheticSection {
public:
HashTableSection();
void finalizeContents() override;
@@ -487,57 +484,82 @@ public:
template <class ELFT> void addEntry(SymbolBody &Sym);
private:
- void writeHeader(uint8_t *Buf){};
- void addHeaderSymbols(){};
unsigned getPltRelocOff() const;
std::vector<std::pair<const SymbolBody *, unsigned>> Entries;
// Iplt always has HeaderSize of 0, the Plt HeaderSize is always non-zero
size_t HeaderSize;
};
-class GdbIndexSection final : public SyntheticSection {
- const unsigned OffsetTypeSize = 4;
- const unsigned CuListOffset = 6 * OffsetTypeSize;
- const unsigned CompilationUnitSize = 16;
- const unsigned AddressEntrySize = 16 + OffsetTypeSize;
- const unsigned SymTabEntrySize = 2 * OffsetTypeSize;
+// GdbIndexChunk is created for each .debug_info section and contains
+// information to create a part of .gdb_index for a given input section.
+struct GdbIndexChunk {
+ struct AddressEntry {
+ InputSection *Section;
+ uint64_t LowAddress;
+ uint64_t HighAddress;
+ uint32_t CuIndex;
+ };
+
+ struct CuEntry {
+ uint64_t CuOffset;
+ uint64_t CuLength;
+ };
+
+ struct NameTypeEntry {
+ llvm::CachedHashStringRef Name;
+ uint8_t Type;
+ };
+
+ InputSection *DebugInfoSec;
+ std::vector<AddressEntry> AddressAreas;
+ std::vector<CuEntry> CompilationUnits;
+ std::vector<NameTypeEntry> NamesAndTypes;
+};
+// The symbol type for the .gdb_index section.
+struct GdbSymbol {
+ uint32_t NameHash;
+ size_t NameOffset;
+ size_t CuVectorIndex;
+};
+
+class GdbIndexSection final : public SyntheticSection {
public:
- GdbIndexSection();
- void finalizeContents() override;
+ GdbIndexSection(std::vector<GdbIndexChunk> &&Chunks);
void writeTo(uint8_t *Buf) override;
size_t getSize() const override;
bool empty() const override;
- // Symbol table is a hash table for types and names.
- // It is the area of gdb index.
- GdbHashTab SymbolTable;
+private:
+ void fixCuIndex();
+ std::vector<std::vector<uint32_t>> createCuVectors();
+ std::vector<GdbSymbol *> createGdbSymtab();
+
+ // A symbol table for this .gdb_index section.
+ std::vector<GdbSymbol *> GdbSymtab;
// CU vector is a part of constant pool area of section.
- std::vector<std::set<uint32_t>> CuVectors;
+ std::vector<std::vector<uint32_t>> CuVectors;
- // String pool is also a part of constant pool, it follows CU vectors.
- llvm::StringTableBuilder StringPool;
+ // Symbol table contents.
+ llvm::DenseMap<llvm::CachedHashStringRef, GdbSymbol *> Symbols;
// Each chunk contains information gathered from a debug sections of single
// object and used to build different areas of gdb index.
std::vector<GdbIndexChunk> Chunks;
-private:
- GdbIndexChunk readDwarf(InputSection *Sec);
- void buildIndex();
-
+ static constexpr uint32_t CuListOffset = 24;
uint32_t CuTypesOffset;
- uint32_t SymTabOffset;
+ uint32_t SymtabOffset;
uint32_t ConstantPoolOffset;
uint32_t StringPoolOffset;
+ uint32_t StringPoolSize;
- size_t CuVectorsSize = 0;
- std::vector<size_t> CuVectorsOffset;
-
- bool Finalized = false;
+ std::vector<size_t> CuVectorOffsets;
};
+template <class ELFT> GdbIndexSection *createGdbIndex();
+
// --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.
@@ -637,22 +659,58 @@ public:
// with different attributes in a single output sections. To do that
// we put them into MergeSyntheticSection synthetic input sections which are
// attached to regular output sections.
-class MergeSyntheticSection final : public SyntheticSection {
+class MergeSyntheticSection : public SyntheticSection {
public:
- MergeSyntheticSection(StringRef Name, uint32_t Type, uint64_t Flags,
- uint32_t Alignment);
void addSection(MergeInputSection *MS);
+
+protected:
+ MergeSyntheticSection(StringRef Name, uint32_t Type, uint64_t Flags,
+ uint32_t Alignment)
+ : SyntheticSection(Flags, Type, Alignment, Name) {}
+
+ std::vector<MergeInputSection *> Sections;
+};
+
+class MergeTailSection final : public MergeSyntheticSection {
+public:
+ MergeTailSection(StringRef Name, uint32_t Type, uint64_t Flags,
+ uint32_t Alignment);
+
+ size_t getSize() const override;
void writeTo(uint8_t *Buf) override;
void finalizeContents() override;
- bool shouldTailMerge() const;
- size_t getSize() const override;
private:
- void finalizeTailMerge();
- void finalizeNoTailMerge();
-
llvm::StringTableBuilder Builder;
- std::vector<MergeInputSection *> Sections;
+};
+
+class MergeNoTailSection final : public MergeSyntheticSection {
+public:
+ MergeNoTailSection(StringRef Name, uint32_t Type, uint64_t Flags,
+ uint32_t Alignment)
+ : MergeSyntheticSection(Name, Type, Flags, Alignment) {}
+
+ size_t getSize() const override { return Size; }
+ void writeTo(uint8_t *Buf) override;
+ void finalizeContents() override;
+
+private:
+ // We use the most significant bits of a hash as a shard ID.
+ // The reason why we don't want to use the least significant bits is
+ // because DenseMap also uses lower bits to determine a bucket ID.
+ // If we use lower bits, it significantly increases the probability of
+ // hash collisons.
+ size_t getShardId(uint32_t Hash) {
+ return Hash >> (32 - llvm::countTrailingZeros(NumShards));
+ }
+
+ // Section size
+ size_t Size;
+
+ // String table contents
+ constexpr static size_t NumShards = 32;
+ std::vector<llvm::StringTableBuilder> Shards;
+ size_t ShardOffsets[NumShards];
};
// .MIPS.abiflags section.
@@ -744,10 +802,11 @@ private:
size_t Size = 0;
};
-template <class ELFT> InputSection *createCommonSection();
+template <class ELFT> void createCommonSections();
InputSection *createInterpSection();
template <class ELFT> MergeInputSection *createCommentSection();
-void decompressAndMergeSections();
+void decompressSections();
+void mergeSections();
SymbolBody *addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
uint64_t Size, InputSectionBase *Section);
@@ -758,11 +817,11 @@ struct InX {
static BssSection *Bss;
static BssSection *BssRelRo;
static BuildIdSection *BuildId;
- static InputSection *Common;
static SyntheticSection *Dynamic;
static StringTableSection *DynStrTab;
static SymbolTableBaseSection *DynSymTab;
static GnuHashTableSection *GnuHashTab;
+ static HashTableSection *HashTab;
static InputSection *Interp;
static GdbIndexSection *GdbIndex;
static GotSection *Got;
@@ -780,7 +839,6 @@ struct InX {
template <class ELFT> struct In : public InX {
static EhFrameHeader<ELFT> *EhFrameHdr;
static EhFrameSection<ELFT> *EhFrame;
- static HashTableSection<ELFT> *HashTab;
static RelocationSection<ELFT> *RelaDyn;
static RelocationSection<ELFT> *RelaPlt;
static RelocationSection<ELFT> *RelaIplt;
@@ -791,7 +849,6 @@ template <class ELFT> struct In : public InX {
template <class ELFT> EhFrameHeader<ELFT> *In<ELFT>::EhFrameHdr;
template <class ELFT> EhFrameSection<ELFT> *In<ELFT>::EhFrame;
-template <class ELFT> HashTableSection<ELFT> *In<ELFT>::HashTab;
template <class ELFT> RelocationSection<ELFT> *In<ELFT>::RelaDyn;
template <class ELFT> RelocationSection<ELFT> *In<ELFT>::RelaPlt;
template <class ELFT> RelocationSection<ELFT> *In<ELFT>::RelaIplt;
diff --git a/ELF/Target.cpp b/ELF/Target.cpp
index c1a85e165..7ebecdb1e 100644
--- a/ELF/Target.cpp
+++ b/ELF/Target.cpp
@@ -40,7 +40,7 @@ using namespace lld::elf;
TargetInfo *elf::Target;
-std::string lld::toString(uint32_t Type) {
+std::string lld::toString(RelType Type) {
StringRef S = getELFRelocationTypeName(elf::Config->EMachine, Type);
if (S == "Unknown")
return ("Unknown (" + Twine(Type) + ")").str();
@@ -77,6 +77,8 @@ TargetInfo *elf::getTarget() {
return getPPCTargetInfo();
case EM_PPC64:
return getPPC64TargetInfo();
+ case EM_SPARCV9:
+ return getSPARCV9TargetInfo();
case EM_X86_64:
if (Config->EKind == ELF32LEKind)
return getX32TargetInfo();
@@ -115,22 +117,26 @@ std::string elf::getErrorLocation(const uint8_t *Loc) {
TargetInfo::~TargetInfo() {}
-int64_t TargetInfo::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
+int64_t TargetInfo::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
return 0;
}
-bool TargetInfo::usesOnlyLowPageBits(uint32_t Type) const { return false; }
+bool TargetInfo::usesOnlyLowPageBits(RelType Type) const { return false; }
-bool TargetInfo::needsThunk(RelExpr Expr, uint32_t RelocType,
- const InputFile *File, const SymbolBody &S) const {
+bool TargetInfo::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
+ const SymbolBody &S) const {
return false;
}
+bool TargetInfo::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
+ return true;
+}
+
void TargetInfo::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
writeGotPlt(Buf, S);
}
-RelExpr TargetInfo::adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
+RelExpr TargetInfo::adjustRelaxExpr(RelType Type, const uint8_t *Data,
RelExpr Expr) const {
return Expr;
}
@@ -139,22 +145,29 @@ void TargetInfo::relaxGot(uint8_t *Loc, uint64_t Val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type,
+void TargetInfo::relaxTlsGdToLe(uint8_t *Loc, RelType Type,
uint64_t Val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type,
+void TargetInfo::relaxTlsGdToIe(uint8_t *Loc, RelType Type,
uint64_t Val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type,
+void TargetInfo::relaxTlsIeToLe(uint8_t *Loc, RelType Type,
uint64_t Val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type,
+void TargetInfo::relaxTlsLdToLe(uint8_t *Loc, RelType Type,
uint64_t Val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
+
+uint64_t TargetInfo::getImageBase() {
+ // Use -image-base if set. Fall back to the target default if not.
+ if (Config->ImageBase)
+ return *Config->ImageBase;
+ return Config->Pic ? 0 : DefaultImageBase;
+}
diff --git a/ELF/Target.h b/ELF/Target.h
index e3a6d98a3..957af6ae4 100644
--- a/ELF/Target.h
+++ b/ELF/Target.h
@@ -15,7 +15,7 @@
#include "llvm/Object/ELF.h"
namespace lld {
-std::string toString(uint32_t RelType);
+std::string toString(elf::RelType Type);
namespace elf {
class InputFile;
@@ -23,12 +23,12 @@ class SymbolBody;
class TargetInfo {
public:
- virtual bool isPicRel(uint32_t Type) const { return true; }
- virtual uint32_t getDynRel(uint32_t Type) const { return Type; }
+ virtual bool isPicRel(RelType Type) const { return true; }
+ virtual RelType getDynRel(RelType 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 int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const;
+ virtual int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const;
// If lazy binding is supported, the first entry of the PLT has code
// to call the dynamic linker to resolve PLT entries the first time
@@ -40,41 +40,48 @@ public:
unsigned RelOff) const {}
virtual void addPltHeaderSymbols(InputSectionBase *IS) const {}
virtual void addPltSymbols(InputSectionBase *IS, uint64_t Off) const {}
+
// Returns true if a relocation only uses the low bits of a value such that
// all those bits are in in the same page. For example, if the relocation
// only uses the low 12 bits in a system with 4k pages. If this is true, the
// bits will always have the same value at runtime and we don't have to emit
// a dynamic relocation.
- virtual bool usesOnlyLowPageBits(uint32_t Type) const;
+ virtual bool usesOnlyLowPageBits(RelType Type) const;
// Decide whether a Thunk is needed for the relocation from File
// targeting S.
- virtual bool needsThunk(RelExpr Expr, uint32_t RelocType,
- const InputFile *File, const SymbolBody &S) const;
- virtual RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ virtual bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
+ const SymbolBody &S) const;
+
+ // Return true if we can reach Dst from Src with Relocation Type
+ virtual bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const;
+
+ virtual RelExpr getRelExpr(RelType Type, const SymbolBody &S,
const uint8_t *Loc) const = 0;
- virtual void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const = 0;
+
+ virtual void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const = 0;
+
virtual ~TargetInfo();
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
- // 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.
- uint64_t DefaultImageBase = 0x10000;
-
- uint32_t CopyRel;
- uint32_t GotRel;
- uint32_t PltRel;
- uint32_t RelativeRel;
- uint32_t IRelativeRel;
- uint32_t TlsDescRel;
- uint32_t TlsGotRel;
- uint32_t TlsModuleIndexRel;
- uint32_t TlsOffsetRel;
+ uint64_t getImageBase();
+
+ // Offset of _GLOBAL_OFFSET_TABLE_ from base of .got section. Use -1 for
+ // end of .got
+ uint64_t GotBaseSymOff = 0;
+
+ RelType CopyRel;
+ RelType GotRel;
+ RelType PltRel;
+ RelType RelativeRel;
+ RelType IRelativeRel;
+ RelType TlsDescRel;
+ RelType TlsGotRel;
+ RelType TlsModuleIndexRel;
+ RelType TlsOffsetRel;
unsigned GotEntrySize = 0;
unsigned GotPltEntrySize = 0;
unsigned PltEntrySize;
@@ -93,13 +100,20 @@ public:
// executable OutputSections.
uint32_t TrapInstr = 0;
- virtual RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
+ virtual RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
RelExpr Expr) const;
virtual void relaxGot(uint8_t *Loc, uint64_t Val) const;
- virtual void relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
- virtual void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
- virtual void relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
- virtual void relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
+ virtual void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const;
+ virtual void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const;
+ virtual void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const;
+ virtual void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const;
+
+protected:
+ // 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.
+ uint64_t DefaultImageBase = 0x10000;
};
TargetInfo *getAArch64TargetInfo();
@@ -108,6 +122,7 @@ TargetInfo *getARMTargetInfo();
TargetInfo *getAVRTargetInfo();
TargetInfo *getPPC64TargetInfo();
TargetInfo *getPPCTargetInfo();
+TargetInfo *getSPARCV9TargetInfo();
TargetInfo *getX32TargetInfo();
TargetInfo *getX86TargetInfo();
TargetInfo *getX86_64TargetInfo();
@@ -122,33 +137,33 @@ extern TargetInfo *Target;
TargetInfo *getTarget();
template <unsigned N>
-static void checkInt(uint8_t *Loc, int64_t V, uint32_t Type) {
+static void checkInt(uint8_t *Loc, int64_t V, RelType Type) {
if (!llvm::isInt<N>(V))
error(getErrorLocation(Loc) + "relocation " + lld::toString(Type) +
" out of range");
}
template <unsigned N>
-static void checkUInt(uint8_t *Loc, uint64_t V, uint32_t Type) {
+static void checkUInt(uint8_t *Loc, uint64_t V, RelType Type) {
if (!llvm::isUInt<N>(V))
error(getErrorLocation(Loc) + "relocation " + lld::toString(Type) +
" out of range");
}
template <unsigned N>
-static void checkIntUInt(uint8_t *Loc, uint64_t V, uint32_t Type) {
+static void checkIntUInt(uint8_t *Loc, uint64_t V, RelType Type) {
if (!llvm::isInt<N>(V) && !llvm::isUInt<N>(V))
error(getErrorLocation(Loc) + "relocation " + lld::toString(Type) +
" out of range");
}
template <unsigned N>
-static void checkAlignment(uint8_t *Loc, uint64_t V, uint32_t Type) {
+static void checkAlignment(uint8_t *Loc, uint64_t V, RelType Type) {
if ((V & (N - 1)) != 0)
error(getErrorLocation(Loc) + "improper alignment for relocation " +
lld::toString(Type));
}
} // namespace elf
-}
+} // namespace lld
#endif
diff --git a/ELF/Thunks.cpp b/ELF/Thunks.cpp
index 752a881d7..9f05af72c 100644
--- a/ELF/Thunks.cpp
+++ b/ELF/Thunks.cpp
@@ -57,6 +57,7 @@ public:
uint32_t size() const override { return 12; }
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
void addSymbols(ThunkSection &IS) override;
+ bool isCompatibleWith(RelType Type) const override;
};
class ARMV7PILongThunk final : public Thunk {
@@ -66,28 +67,27 @@ public:
uint32_t size() const override { return 16; }
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
void addSymbols(ThunkSection &IS) override;
+ bool isCompatibleWith(RelType Type) const override;
};
class ThumbV7ABSLongThunk final : public Thunk {
public:
- ThumbV7ABSLongThunk(const SymbolBody &Dest) : Thunk(Dest) {
- this->alignment = 2;
- }
+ ThumbV7ABSLongThunk(const SymbolBody &Dest) : Thunk(Dest) { Alignment = 2; }
uint32_t size() const override { return 10; }
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
void addSymbols(ThunkSection &IS) override;
+ bool isCompatibleWith(RelType Type) const override;
};
class ThumbV7PILongThunk final : public Thunk {
public:
- ThumbV7PILongThunk(const SymbolBody &Dest) : Thunk(Dest) {
- this->alignment = 2;
- }
+ ThumbV7PILongThunk(const SymbolBody &Dest) : Thunk(Dest) { Alignment = 2; }
uint32_t size() const override { return 12; }
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
void addSymbols(ThunkSection &IS) override;
+ bool isCompatibleWith(RelType Type) const override;
};
// MIPS LA25 thunk
@@ -101,6 +101,28 @@ public:
InputSection *getTargetInputSection() const override;
};
+// microMIPS R2-R5 LA25 thunk
+class MicroMipsThunk final : public Thunk {
+public:
+ MicroMipsThunk(const SymbolBody &Dest) : Thunk(Dest) {}
+
+ uint32_t size() const override { return 14; }
+ void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ void addSymbols(ThunkSection &IS) override;
+ InputSection *getTargetInputSection() const override;
+};
+
+// microMIPS R6 LA25 thunk
+class MicroMipsR6Thunk final : public Thunk {
+public:
+ MicroMipsR6Thunk(const SymbolBody &Dest) : Thunk(Dest) {}
+
+ uint32_t size() const override { return 12; }
+ void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ void addSymbols(ThunkSection &IS) override;
+ InputSection *getTargetInputSection() const override;
+};
+
} // end anonymous namespace
// ARM Target Thunks
@@ -128,6 +150,11 @@ void ARMV7ABSLongThunk::addSymbols(ThunkSection &IS) {
addSyntheticLocal("$a", STT_NOTYPE, Offset, 0, &IS);
}
+bool ARMV7ABSLongThunk::isCompatibleWith(RelType Type) const {
+ // Thumb branch relocations can't use BLX
+ return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
+}
+
void ThumbV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
const uint8_t Data[] = {
0x40, 0xf2, 0x00, 0x0c, // movw ip, :lower16:S
@@ -147,18 +174,24 @@ void ThumbV7ABSLongThunk::addSymbols(ThunkSection &IS) {
addSyntheticLocal("$t", STT_NOTYPE, Offset, 0, &IS);
}
+bool ThumbV7ABSLongThunk::isCompatibleWith(RelType Type) const {
+ // ARM branch relocations can't use BLX
+ return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
+}
+
void ARMV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
const uint8_t Data[] = {
- 0xf0, 0xcf, 0x0f, 0xe3, // P: movw ip,:lower16:S - (P + (L1-P) +8)
- 0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S - (P + (L1-P+4) +8)
+ 0xf0, 0xcf, 0x0f, 0xe3, // P: movw ip,:lower16:S - (P + (L1-P) + 8)
+ 0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S - (P + (L1-P) + 8)
0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
0x1c, 0xff, 0x2f, 0xe1, // bx r12
};
uint64_t S = getARMThunkDestVA(Destination);
uint64_t P = ThunkSym->getVA();
+ uint64_t Offset = S - P - 16;
memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, S - P - 16);
- Target->relocateOne(Buf + 4, R_ARM_MOVT_PREL, S - P - 12);
+ Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, Offset);
+ Target->relocateOne(Buf + 4, R_ARM_MOVT_PREL, Offset);
}
void ARMV7PILongThunk::addSymbols(ThunkSection &IS) {
@@ -168,18 +201,24 @@ void ARMV7PILongThunk::addSymbols(ThunkSection &IS) {
addSyntheticLocal("$a", STT_NOTYPE, Offset, 0, &IS);
}
+bool ARMV7PILongThunk::isCompatibleWith(RelType Type) const {
+ // Thumb branch relocations can't use BLX
+ return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
+}
+
void ThumbV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
const uint8_t Data[] = {
0x4f, 0xf6, 0xf4, 0x7c, // P: movw ip,:lower16:S - (P + (L1-P) + 4)
- 0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P+4) + 4)
+ 0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4)
0xfc, 0x44, // L1: add r12, pc
0x60, 0x47, // bx r12
};
uint64_t S = getARMThunkDestVA(Destination);
uint64_t P = ThunkSym->getVA() & ~0x1;
+ uint64_t Offset = S - P - 12;
memcpy(Buf, Data, sizeof(Data));
- Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, S - P - 12);
- Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_PREL, S - P - 8);
+ Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, Offset);
+ Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_PREL, Offset);
}
void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) {
@@ -189,13 +228,18 @@ void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) {
addSyntheticLocal("$t", STT_NOTYPE, Offset, 0, &IS);
}
+bool ThumbV7PILongThunk::isCompatibleWith(RelType Type) const {
+ // ARM branch relocations can't use BLX
+ return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
+}
+
// Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
void MipsThunk::writeTo(uint8_t *Buf, ThunkSection &) const {
- uint64_t S = this->Destination.getVA();
- write32(Buf, 0x3c190000, Config->Endianness); // lui $25, %hi(func)
+ uint64_t S = Destination.getVA();
+ write32(Buf, 0x3c190000, Config->Endianness); // lui $25, %hi(func)
write32(Buf + 4, 0x08000000 | (S >> 2), Config->Endianness); // j func
- write32(Buf + 8, 0x27390000, Config->Endianness); // addiu $25, $25, %lo(func)
- write32(Buf + 12, 0x00000000, Config->Endianness); // nop
+ write32(Buf + 8, 0x27390000, Config->Endianness); // addiu $25, $25, %lo(func)
+ write32(Buf + 12, 0x00000000, Config->Endianness); // nop
Target->relocateOne(Buf, R_MIPS_HI16, S);
Target->relocateOne(Buf + 8, R_MIPS_LO16, S);
}
@@ -211,12 +255,62 @@ InputSection *MipsThunk::getTargetInputSection() const {
return dyn_cast<InputSection>(DR->Section);
}
+// Write microMIPS R2-R5 LA25 thunk code
+// to call PIC function from the non-PIC one.
+void MicroMipsThunk::writeTo(uint8_t *Buf, ThunkSection &) const {
+ uint64_t S = Destination.getVA();
+ write16(Buf, 0x41b9, Config->Endianness); // lui $25, %hi(func)
+ write16(Buf + 4, 0xd400, Config->Endianness); // j func
+ write16(Buf + 8, 0x3339, Config->Endianness); // addiu $25, $25, %lo(func)
+ write16(Buf + 12, 0x0c00, Config->Endianness); // nop
+ Target->relocateOne(Buf, R_MICROMIPS_HI16, S);
+ Target->relocateOne(Buf + 4, R_MICROMIPS_26_S1, S);
+ Target->relocateOne(Buf + 8, R_MICROMIPS_LO16, S);
+}
+
+void MicroMipsThunk::addSymbols(ThunkSection &IS) {
+ ThunkSym =
+ addSyntheticLocal(Saver.save("__microLA25Thunk_" + Destination.getName()),
+ STT_FUNC, Offset, size(), &IS);
+ ThunkSym->StOther |= STO_MIPS_MICROMIPS;
+}
+
+InputSection *MicroMipsThunk::getTargetInputSection() const {
+ auto *DR = dyn_cast<DefinedRegular>(&Destination);
+ return dyn_cast<InputSection>(DR->Section);
+}
+
+// Write microMIPS R6 LA25 thunk code
+// to call PIC function from the non-PIC one.
+void MicroMipsR6Thunk::writeTo(uint8_t *Buf, ThunkSection &) const {
+ uint64_t S = Destination.getVA();
+ uint64_t P = ThunkSym->getVA();
+ write16(Buf, 0x1320, Config->Endianness); // lui $25, %hi(func)
+ write16(Buf + 4, 0x3339, Config->Endianness); // addiu $25, $25, %lo(func)
+ write16(Buf + 8, 0x9400, Config->Endianness); // bc func
+ Target->relocateOne(Buf, R_MICROMIPS_HI16, S);
+ Target->relocateOne(Buf + 4, R_MICROMIPS_LO16, S);
+ Target->relocateOne(Buf + 8, R_MICROMIPS_PC26_S1, S - P - 12);
+}
+
+void MicroMipsR6Thunk::addSymbols(ThunkSection &IS) {
+ ThunkSym =
+ addSyntheticLocal(Saver.save("__microLA25Thunk_" + Destination.getName()),
+ STT_FUNC, Offset, size(), &IS);
+ ThunkSym->StOther |= STO_MIPS_MICROMIPS;
+}
+
+InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
+ auto *DR = dyn_cast<DefinedRegular>(&Destination);
+ return dyn_cast<InputSection>(DR->Section);
+}
+
Thunk::Thunk(const SymbolBody &D) : Destination(D), Offset(0) {}
Thunk::~Thunk() = default;
// Creates a thunk for Thumb-ARM interworking.
-static Thunk *addThunkArm(uint32_t Reloc, SymbolBody &S) {
+static Thunk *addThunkArm(RelType 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.
@@ -236,15 +330,19 @@ static Thunk *addThunkArm(uint32_t Reloc, SymbolBody &S) {
fatal("unrecognized relocation type");
}
-static Thunk *addThunkMips(SymbolBody &S) {
+static Thunk *addThunkMips(RelType Type, SymbolBody &S) {
+ if ((S.StOther & STO_MIPS_MICROMIPS) && isMipsR6())
+ return make<MicroMipsR6Thunk>(S);
+ if (S.StOther & STO_MIPS_MICROMIPS)
+ return make<MicroMipsThunk>(S);
return make<MipsThunk>(S);
}
-Thunk *addThunk(uint32_t RelocType, SymbolBody &S) {
+Thunk *addThunk(RelType Type, SymbolBody &S) {
if (Config->EMachine == EM_ARM)
- return addThunkArm(RelocType, S);
+ return addThunkArm(Type, S);
else if (Config->EMachine == EM_MIPS)
- return addThunkMips(S);
+ return addThunkMips(Type, S);
llvm_unreachable("add Thunk only supported for ARM and Mips");
return nullptr;
}
diff --git a/ELF/Thunks.h b/ELF/Thunks.h
index 38ee090e7..2243a113f 100644
--- a/ELF/Thunks.h
+++ b/ELF/Thunks.h
@@ -41,17 +41,21 @@ public:
// a branch and fall through to the first Symbol in the Target.
virtual InputSection *getTargetInputSection() const { return nullptr; }
+ // To reuse a Thunk the caller as identified by the Type must be
+ // compatible with it.
+ virtual bool isCompatibleWith(RelType Type) const { return true; }
+
// The alignment requirement for this Thunk, defaults to the size of the
// typical code section alignment.
const SymbolBody &Destination;
SymbolBody *ThunkSym;
uint64_t Offset;
- uint32_t alignment = 4;
+ uint32_t Alignment = 4;
};
// For a Relocation to symbol S create a Thunk to be added to a synthetic
// ThunkSection. At present there are implementations for ARM and Mips Thunks.
-Thunk *addThunk(uint32_t RelocType, SymbolBody &S);
+Thunk *addThunk(RelType Type, SymbolBody &S);
} // namespace elf
} // namespace lld
diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp
index 4ff06388e..7f10e6cfa 100644
--- a/ELF/Writer.cpp
+++ b/ELF/Writer.cpp
@@ -19,11 +19,10 @@
#include "SymbolTable.h"
#include "SyntheticSections.h"
#include "Target.h"
-#include "Threads.h"
+#include "lld/Common/Threads.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/FileOutputBuffer.h"
-#include "llvm/Support/raw_ostream.h"
#include <climits>
using namespace llvm;
@@ -46,7 +45,6 @@ public:
void run();
private:
- void clearOutputSections();
void createSyntheticSections();
void copyLocalSymbols();
void addSectionSymbols();
@@ -56,16 +54,17 @@ private:
void sortSections();
void finalizeSections();
void addPredefinedSections();
+ void setReservedSymbolSections();
- std::vector<PhdrEntry> createPhdrs();
+ std::vector<PhdrEntry *> createPhdrs();
void removeEmptyPTLoad();
- void addPtArmExid(std::vector<PhdrEntry> &Phdrs);
+ void addPtArmExid(std::vector<PhdrEntry *> &Phdrs);
void assignFileOffsets();
void assignFileOffsetsBinary();
void setPhdrs();
void fixSectionAlignments();
- void fixPredefinedSymbols();
void openFile();
+ void writeTrapInstr();
void writeHeader();
void writeSections();
void writeSectionsBinary();
@@ -73,20 +72,20 @@ private:
std::unique_ptr<FileOutputBuffer> Buffer;
- OutputSectionFactory Factory{OutputSections};
+ OutputSectionFactory Factory;
void addRelIpltSymbols();
void addStartEndSymbols();
void addStartStopSymbols(OutputSection *Sec);
uint64_t getEntryAddr();
OutputSection *findSection(StringRef Name);
- OutputSection *findSectionInScript(StringRef Name);
- OutputSectionCommand *findSectionCommand(StringRef Name);
- std::vector<PhdrEntry> Phdrs;
+ std::vector<PhdrEntry *> Phdrs;
uint64_t FileSize;
uint64_t SectionHeaderOff;
+
+ bool HasGotBaseSym = false;
};
} // anonymous namespace
@@ -102,7 +101,7 @@ StringRef elf::getOutputSectionName(StringRef Name) {
for (StringRef V :
{".text.", ".rodata.", ".data.rel.ro.", ".data.", ".bss.rel.ro.",
".bss.", ".init_array.", ".fini_array.", ".ctors.", ".dtors.", ".tbss.",
- ".gcc_except_table.", ".tdata.", ".ARM.exidx."}) {
+ ".gcc_except_table.", ".tdata.", ".ARM.exidx.", ".ARM.extab."}) {
StringRef Prefix = V.drop_back();
if (Name.startswith(V) || Name == Prefix)
return Prefix;
@@ -116,23 +115,22 @@ StringRef elf::getOutputSectionName(StringRef Name) {
return Name;
}
-template <class ELFT> static bool needsInterpSection() {
- return !Symtab<ELFT>::X->getSharedFiles().empty() &&
- !Config->DynamicLinker.empty() && !Script->ignoreInterpSection();
+static bool needsInterpSection() {
+ return !SharedFiles.empty() && !Config->DynamicLinker.empty() &&
+ Script->needsInterpSection();
}
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)
+ llvm::erase_if(Phdrs, [&](const PhdrEntry *P) {
+ if (P->p_type != PT_LOAD)
return false;
- if (!P.First)
+ if (!P->FirstSec)
return true;
- uint64_t Size = P.Last->Addr + P.Last->Size - P.First->Addr;
+ uint64_t Size = P->LastSec->Addr + P->LastSec->Size - P->FirstSec->Addr;
return Size == 0;
});
- Phdrs.erase(I, Phdrs.end());
}
template <class ELFT> static void combineEhFrameSections() {
@@ -149,19 +147,6 @@ template <class ELFT> static void combineEhFrameSections() {
V.erase(std::remove(V.begin(), V.end(), nullptr), V.end());
}
-template <class ELFT> void Writer<ELFT>::clearOutputSections() {
- if (Script->Opt.HasSections)
- Script->createOrphanCommands();
- else
- Script->fabricateDefaultCommands();
- // Clear the OutputSections to make sure it is not used anymore. Any
- // code from this point on should be using the linker script
- // commands.
- for (OutputSection *Sec : OutputSections)
- Sec->Sections.clear();
- OutputSections.clear();
-}
-
// The main function of the writer.
template <class ELFT> void Writer<ELFT>::run() {
// Create linker-synthesized sections such as .got or .plt.
@@ -176,9 +161,9 @@ template <class ELFT> void Writer<ELFT>::run() {
addReservedSymbols();
// Create output sections.
- if (Script->Opt.HasSections) {
+ if (Script->HasSectionsCommand) {
// If linker script contains SECTIONS commands, let it create sections.
- Script->processCommands(Factory);
+ Script->processSectionCommands(Factory);
// Linker scripts may have left some input sections unassigned.
// Assign such sections using the default rule.
@@ -188,8 +173,8 @@ template <class ELFT> void Writer<ELFT>::run() {
// 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.
+ Script->processSectionCommands(Factory);
createSections();
- Script->processCommands(Factory);
}
if (Config->Discard != DiscardPolicy::All)
@@ -206,17 +191,14 @@ template <class ELFT> void Writer<ELFT>::run() {
if (ErrorCount)
return;
- if (!Script->Opt.HasSections && !Config->Relocatable)
- fixSectionAlignments();
-
// If -compressed-debug-sections is specified, we need to compress
// .debug_* sections. Do it right now because it changes the size of
// output sections.
- parallelForEach(
- OutputSectionCommands.begin(), OutputSectionCommands.end(),
- [](OutputSectionCommand *Cmd) { Cmd->maybeCompress<ELFT>(); });
+ parallelForEach(OutputSections,
+ [](OutputSection *Sec) { Sec->maybeCompress<ELFT>(); });
- Script->assignAddresses(Phdrs);
+ Script->assignAddresses();
+ Script->allocateHeaders(Phdrs);
// 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
@@ -231,10 +213,8 @@ template <class ELFT> void Writer<ELFT>::run() {
setPhdrs();
if (Config->Relocatable) {
- for (OutputSectionCommand *Cmd : OutputSectionCommands)
- Cmd->Sec->Addr = 0;
- } else {
- fixPredefinedSymbols();
+ for (OutputSection *Sec : OutputSections)
+ Sec->Addr = 0;
}
// It does not make sense try to open the file if we have error already.
@@ -246,6 +226,7 @@ template <class ELFT> void Writer<ELFT>::run() {
return;
if (!Config->OFormatBinary) {
+ writeTrapInstr();
writeHeader();
writeSections();
} else {
@@ -258,20 +239,13 @@ template <class ELFT> void Writer<ELFT>::run() {
if (ErrorCount)
return;
-
// Handle -Map option.
- writeMapFile<ELFT>(OutputSectionCommands);
+ writeMapFile<ELFT>();
if (ErrorCount)
return;
if (auto EC = Buffer->commit())
error("failed to write to the output file: " + EC.message());
-
- // 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);
}
// Initialize Out members.
@@ -291,9 +265,9 @@ template <class ELFT> void Writer<ELFT>::createSyntheticSections() {
Out::ElfHeader = make<OutputSection>("", 0, SHF_ALLOC);
Out::ElfHeader->Size = sizeof(Elf_Ehdr);
Out::ProgramHeaders = make<OutputSection>("", 0, SHF_ALLOC);
- Out::ProgramHeaders->updateAlignment(Config->Wordsize);
+ Out::ProgramHeaders->Alignment = Config->Wordsize;
- if (needsInterpSection<ELFT>()) {
+ if (needsInterpSection()) {
InX::Interp = createInterpSection();
Add(InX::Interp);
} else {
@@ -310,20 +284,14 @@ template <class ELFT> void Writer<ELFT>::createSyntheticSections() {
Add(InX::BuildId);
}
- InX::Common = createCommonSection<ELFT>();
- if (InX::Common)
- Add(InX::Common);
-
- InX::Bss = make<BssSection>(".bss");
+ InX::Bss = make<BssSection>(".bss", 0, 1);
Add(InX::Bss);
- InX::BssRelRo = make<BssSection>(".bss.rel.ro");
+ InX::BssRelRo = make<BssSection>(".bss.rel.ro", 0, 1);
Add(InX::BssRelRo);
// Add MIPS-specific sections.
- bool HasDynSymTab = !Symtab<ELFT>::X->getSharedFiles().empty() ||
- Config->Pic || Config->ExportDynamic;
if (Config->EMachine == EM_MIPS) {
- if (!Config->Shared && HasDynSymTab) {
+ if (!Config->Shared && Config->HasDynSymTab) {
InX::MipsRldMap = make<MipsRldMapSection>();
Add(InX::MipsRldMap);
}
@@ -335,7 +303,7 @@ template <class ELFT> void Writer<ELFT>::createSyntheticSections() {
Add(Sec);
}
- if (HasDynSymTab) {
+ if (Config->HasDynSymTab) {
InX::DynSymTab = make<SymbolTableSection<ELFT>>(*InX::DynStrTab);
Add(InX::DynSymTab);
@@ -356,8 +324,8 @@ template <class ELFT> void Writer<ELFT>::createSyntheticSections() {
}
if (Config->SysvHash) {
- In<ELFT>::HashTab = make<HashTableSection<ELFT>>();
- Add(In<ELFT>::HashTab);
+ InX::HashTab = make<HashTableSection>();
+ Add(InX::HashTab);
}
Add(InX::Dynamic);
@@ -381,7 +349,7 @@ template <class ELFT> void Writer<ELFT>::createSyntheticSections() {
Add(InX::IgotPlt);
if (Config->GdbIndex) {
- InX::GdbIndex = make<GdbIndexSection>();
+ InX::GdbIndex = createGdbIndex<ELFT>();
Add(InX::GdbIndex);
}
@@ -473,9 +441,10 @@ static bool includeInSymtab(const SymbolBody &B) {
template <class ELFT> void Writer<ELFT>::copyLocalSymbols() {
if (!InX::SymTab)
return;
- for (elf::ObjectFile<ELFT> *F : Symtab<ELFT>::X->getObjectFiles()) {
+ for (InputFile *File : ObjectFiles) {
+ ObjFile<ELFT> *F = cast<ObjFile<ELFT>>(File);
for (SymbolBody *B : F->getLocalSymbols()) {
- if (!B->IsLocal)
+ if (!B->isLocal())
fatal(toString(F) +
": broken object: getLocalSymbols returns a non-local symbol");
auto *DR = dyn_cast<DefinedRegular>(B);
@@ -497,18 +466,25 @@ template <class ELFT> void Writer<ELFT>::copyLocalSymbols() {
template <class ELFT> void Writer<ELFT>::addSectionSymbols() {
// Create one STT_SECTION symbol for each output section we might
// have a relocation with.
- for (OutputSection *Sec : OutputSections) {
- if (Sec->Sections.empty())
+ for (BaseCommand *Base : Script->SectionCommands) {
+ auto *Sec = dyn_cast<OutputSection>(Base);
+ if (!Sec)
continue;
-
- InputSection *IS = Sec->Sections[0];
+ auto I = llvm::find_if(Sec->SectionCommands, [](BaseCommand *Base) {
+ if (auto *ISD = dyn_cast<InputSectionDescription>(Base))
+ return !ISD->Sections.empty();
+ return false;
+ });
+ if (I == Sec->SectionCommands.end())
+ continue;
+ InputSection *IS = cast<InputSectionDescription>(*I)->Sections[0];
if (isa<SyntheticSection>(IS) || IS->Type == SHT_REL ||
IS->Type == SHT_RELA)
continue;
auto *Sym =
make<DefinedRegular>("", /*IsLocal=*/true, /*StOther=*/0, STT_SECTION,
- /*Value=*/0, /*Size=*/0, IS, nullptr);
+ /*Value=*/0, /*Size=*/0, IS);
InX::SymTab->addSymbol(Sym);
}
}
@@ -519,7 +495,7 @@ template <class ELFT> void Writer<ELFT>::addSectionSymbols() {
//
// This function returns true if a section needs to be put into a
// PT_GNU_RELRO segment.
-bool elf::isRelroSection(const OutputSection *Sec) {
+static bool isRelroSection(const OutputSection *Sec) {
if (!Config->ZRelro)
return false;
@@ -691,8 +667,8 @@ static unsigned getSectionRank(const OutputSection *Sec) {
if (IsNoBits)
Rank |= RF_BSS;
- // // Some architectures have additional ordering restrictions for sections
- // // within the same PT_LOAD.
+ // Some architectures have additional ordering restrictions for sections
+ // within the same PT_LOAD.
if (Config->EMachine == EM_PPC64) {
// PPC64 has a number of special SHT_PROGBITS+SHF_ALLOC+SHF_WRITE sections
// that we would like to make sure appear is a specific order to maximize
@@ -730,8 +706,8 @@ static unsigned getSectionRank(const OutputSection *Sec) {
}
static bool compareSections(const BaseCommand *ACmd, const BaseCommand *BCmd) {
- const OutputSection *A = cast<OutputSectionCommand>(ACmd)->Sec;
- const OutputSection *B = cast<OutputSectionCommand>(BCmd)->Sec;
+ const OutputSection *A = cast<OutputSection>(ACmd);
+ const OutputSection *B = cast<OutputSection>(BCmd);
if (A->SortRank != B->SortRank)
return A->SortRank < B->SortRank;
if (!(A->SortRank & RF_NOT_ADDR_SET))
@@ -741,36 +717,25 @@ static bool compareSections(const BaseCommand *ACmd, const BaseCommand *BCmd) {
}
void PhdrEntry::add(OutputSection *Sec) {
- Last = Sec;
- if (!First)
- First = Sec;
+ LastSec = Sec;
+ if (!FirstSec)
+ FirstSec = Sec;
p_align = std::max(p_align, Sec->Alignment);
if (p_type == PT_LOAD)
- Sec->FirstInPtLoad = First;
-}
-
-template <class ELFT>
-static Symbol *addRegular(StringRef Name, SectionBase *Sec, uint64_t Value,
- uint8_t StOther = STV_HIDDEN,
- uint8_t Binding = STB_WEAK) {
- // The linker generated symbols are added as STB_WEAK to allow user defined
- // ones to override them.
- return Symtab<ELFT>::X->addRegular(Name, StOther, STT_NOTYPE, Value,
- /*Size=*/0, Binding, Sec,
- /*File=*/nullptr);
+ Sec->PtLoad = this;
}
template <class ELFT>
static DefinedRegular *
addOptionalRegular(StringRef Name, SectionBase *Sec, uint64_t Val,
uint8_t StOther = STV_HIDDEN, uint8_t Binding = STB_GLOBAL) {
- SymbolBody *S = Symtab<ELFT>::X->find(Name);
- if (!S)
+ SymbolBody *S = Symtab->find(Name);
+ if (!S || S->isInCurrentDSO())
return nullptr;
- if (S->isInCurrentDSO())
- return nullptr;
- return cast<DefinedRegular>(
- addRegular<ELFT>(Name, Sec, Val, StOther, Binding)->body());
+ Symbol *Sym = Symtab->addRegular<ELFT>(Name, StOther, STT_NOTYPE, Val,
+ /*Size=*/0, Binding, Sec,
+ /*File=*/nullptr);
+ return cast<DefinedRegular>(Sym->body());
}
// The beginning and the ending of .rel[a].plt section are marked
@@ -780,7 +745,7 @@ addOptionalRegular(StringRef Name, SectionBase *Sec, uint64_t Val,
// 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 (InX::DynSymTab)
+ if (!Config->Static)
return;
StringRef S = Config->IsRela ? "__rela_iplt_start" : "__rel_iplt_start";
addOptionalRegular<ELFT>(S, In<ELFT>::RelaIplt, 0, STV_HIDDEN, STB_WEAK);
@@ -798,43 +763,30 @@ template <class ELFT> void Writer<ELFT>::addReservedSymbols() {
// 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
- ElfSym::MipsGp = Symtab<ELFT>::X->addAbsolute("_gp", STV_HIDDEN, STB_LOCAL);
+ ElfSym::MipsGp = Symtab->addAbsolute<ELFT>("_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.
- if (Symtab<ELFT>::X->find("_gp_disp"))
+ if (Symtab->find("_gp_disp"))
ElfSym::MipsGpDisp =
- Symtab<ELFT>::X->addAbsolute("_gp_disp", STV_HIDDEN, STB_LOCAL);
+ Symtab->addAbsolute<ELFT>("_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
- if (Symtab<ELFT>::X->find("__gnu_local_gp"))
+ if (Symtab->find("__gnu_local_gp"))
ElfSym::MipsLocalGp =
- Symtab<ELFT>::X->addAbsolute("__gnu_local_gp", STV_HIDDEN, STB_LOCAL);
+ Symtab->addAbsolute<ELFT>("__gnu_local_gp", STV_HIDDEN, STB_LOCAL);
}
- // In the assembly for 32 bit x86 the _GLOBAL_OFFSET_TABLE_ symbol
- // is magical and is used to produce a R_386_GOTPC relocation.
- // The R_386_GOTPC relocation value doesn't actually depend on the
- // symbol value, so it could use an index of STN_UNDEF which, according
- // to the spec, means the symbol value is 0.
- // Unfortunately both gas and MC keep the _GLOBAL_OFFSET_TABLE_ symbol in
- // the object file.
- // The situation is even stranger on x86_64 where the assembly doesn't
- // need the magical symbol, but gas still puts _GLOBAL_OFFSET_TABLE_ as
- // 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.
- 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 (!InX::DynSymTab)
- Symtab<ELFT>::X->addIgnored("__tls_get_addr");
+ // The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention to
+ // be at some offset from the base of the .got section, usually 0 or the end
+ // of the .got
+ InputSection *GotSection = InX::MipsGot ? cast<InputSection>(InX::MipsGot)
+ : cast<InputSection>(InX::Got);
+ ElfSym::GlobalOffsetTable = addOptionalRegular<ELFT>(
+ "_GLOBAL_OFFSET_TABLE_", GotSection, Target->GotBaseSymOff);
// __ehdr_start is the location of ELF file headers. Note that we define
// this symbol unconditionally even when using a linker script, which
@@ -850,113 +802,163 @@ template <class ELFT> void Writer<ELFT>::addReservedSymbols() {
addOptionalRegular<ELFT>(Name, Out::ElfHeader, 0, STV_HIDDEN);
// If linker script do layout we do not need to create any standart symbols.
- if (Script->Opt.HasSections)
+ if (Script->HasSectionsCommand)
return;
- auto Add = [](StringRef S) {
- return addOptionalRegular<ELFT>(S, Out::ElfHeader, 0, STV_DEFAULT);
+ auto Add = [](StringRef S, int64_t Pos) {
+ return addOptionalRegular<ELFT>(S, Out::ElfHeader, Pos, STV_DEFAULT);
};
- ElfSym::Bss = Add("__bss_start");
- ElfSym::End1 = Add("end");
- ElfSym::End2 = Add("_end");
- ElfSym::Etext1 = Add("etext");
- ElfSym::Etext2 = Add("_etext");
- ElfSym::Edata1 = Add("edata");
- ElfSym::Edata2 = Add("_edata");
+ ElfSym::Bss = Add("__bss_start", 0);
+ ElfSym::End1 = Add("end", -1);
+ ElfSym::End2 = Add("_end", -1);
+ ElfSym::Etext1 = Add("etext", -1);
+ ElfSym::Etext2 = Add("_etext", -1);
+ ElfSym::Edata1 = Add("edata", -1);
+ ElfSym::Edata2 = Add("_edata", -1);
}
// Sort input sections by section name suffixes for
// __attribute__((init_priority(N))).
-static void sortInitFini(OutputSection *S) {
- if (S)
- reinterpret_cast<OutputSection *>(S)->sortInitFini();
+static void sortInitFini(OutputSection *Cmd) {
+ if (Cmd)
+ Cmd->sortInitFini();
}
// Sort input sections by the special rule for .ctors and .dtors.
-static void sortCtorsDtors(OutputSection *S) {
- if (S)
- reinterpret_cast<OutputSection *>(S)->sortCtorsDtors();
+static void sortCtorsDtors(OutputSection *Cmd) {
+ if (Cmd)
+ Cmd->sortCtorsDtors();
}
// Sort input sections using the list provided by --symbol-ordering-file.
-template <class ELFT>
-static void sortBySymbolsOrder(ArrayRef<OutputSection *> OutputSections) {
+static void sortBySymbolsOrder() {
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<SectionBase *, int> SectionOrder;
- for (elf::ObjectFile<ELFT> *File : Symtab<ELFT>::X->getObjectFiles()) {
- for (SymbolBody *Body : File->getSymbols()) {
- auto *D = dyn_cast<DefinedRegular>(Body);
- if (!D || !D->Section)
- continue;
- int &Priority = SectionOrder[D->Section];
- Priority = std::min(Priority, SymbolOrder.lookup(D->getName()));
- }
- }
-
// Sort sections by priority.
- for (OutputSection *Base : OutputSections)
+ DenseMap<SectionBase *, int> SectionOrder = buildSectionOrder();
+ for (BaseCommand *Base : Script->SectionCommands)
if (auto *Sec = dyn_cast<OutputSection>(Base))
Sec->sort([&](InputSectionBase *S) { return SectionOrder.lookup(S); });
}
template <class ELFT>
void Writer<ELFT>::forEachRelSec(std::function<void(InputSectionBase &)> Fn) {
- for (InputSectionBase *IS : InputSections) {
- 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>(IS) || isa<EhInputSection>(IS))
+ // 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.
+ for (InputSectionBase *IS : InputSections)
+ if (IS->Live && isa<InputSection>(IS) && (IS->Flags & SHF_ALLOC))
Fn(*IS);
- }
-
- if (!Config->Relocatable) {
- for (EhInputSection *ES : In<ELFT>::EhFrame->Sections)
- Fn(*ES);
- }
+ for (EhInputSection *ES : In<ELFT>::EhFrame->Sections)
+ Fn(*ES);
}
template <class ELFT> void Writer<ELFT>::createSections() {
+ std::vector<OutputSection *> Vec;
for (InputSectionBase *IS : InputSections)
if (IS)
- Factory.addInputSec(IS, getOutputSectionName(IS->Name));
+ if (OutputSection *Sec =
+ Factory.addInputSec(IS, getOutputSectionName(IS->Name)))
+ Vec.push_back(Sec);
- sortBySymbolsOrder<ELFT>(OutputSections);
+ Script->SectionCommands.insert(Script->SectionCommands.begin(), Vec.begin(),
+ Vec.end());
+
+ Script->fabricateDefaultCommands();
+ sortBySymbolsOrder();
sortInitFini(findSection(".init_array"));
sortInitFini(findSection(".fini_array"));
sortCtorsDtors(findSection(".ctors"));
sortCtorsDtors(findSection(".dtors"));
}
+// This function generates assignments for predefined symbols (e.g. _end or
+// _etext) and inserts them into the commands sequence to be processed at the
+// appropriate time. This ensures that the value is going to be correct by the
+// time any references to these symbols are processed and is equivalent to
+// defining these symbols explicitly in the linker script.
+template <class ELFT> void Writer<ELFT>::setReservedSymbolSections() {
+ PhdrEntry *Last = nullptr;
+ PhdrEntry *LastRO = nullptr;
+ PhdrEntry *LastRW = nullptr;
+
+ for (PhdrEntry *P : Phdrs) {
+ if (P->p_type != PT_LOAD)
+ continue;
+ Last = P;
+ if (P->p_flags & PF_W)
+ LastRW = P;
+ else
+ LastRO = P;
+ }
+
+ // _end is the first location after the uninitialized data region.
+ if (Last) {
+ if (ElfSym::End1)
+ ElfSym::End1->Section = Last->LastSec;
+ if (ElfSym::End2)
+ ElfSym::End2->Section = Last->LastSec;
+ }
+
+ // _etext is the first location after the last read-only loadable segment.
+ if (LastRO) {
+ if (ElfSym::Etext1)
+ ElfSym::Etext1->Section = LastRO->LastSec;
+ if (ElfSym::Etext2)
+ ElfSym::Etext2->Section = LastRO->LastSec;
+ }
+
+ // _edata points to the end of the last non SHT_NOBITS section.
+ if (LastRW) {
+ size_t I = 0;
+ for (; I < OutputSections.size(); ++I)
+ if (OutputSections[I] == LastRW->FirstSec)
+ break;
+
+ for (; I < OutputSections.size(); ++I)
+ if (OutputSections[I]->Type == SHT_NOBITS)
+ break;
+
+ if (ElfSym::Edata1)
+ ElfSym::Edata1->Section = OutputSections[I - 1];
+ if (ElfSym::Edata2)
+ ElfSym::Edata2->Section = OutputSections[I - 1];
+ }
+
+ if (ElfSym::Bss)
+ ElfSym::Bss->Section = findSection(".bss");
+
+ // Setup MIPS _gp_disp/__gnu_local_gp symbols which should
+ // be equal to the _gp symbol's value.
+ if (ElfSym::MipsGp) {
+ // Find GP-relative section with the lowest address
+ // and use this address to calculate default _gp value.
+ for (OutputSection *OS : OutputSections) {
+ if (OS->Flags & SHF_MIPS_GPREL) {
+ ElfSym::MipsGp->Section = OS;
+ ElfSym::MipsGp->Value = 0x7ff0;
+ break;
+ }
+ }
+ }
+}
+
// We want to find how similar two ranks are.
// The more branches in getSectionRank that match, the more similar they are.
// Since each branch corresponds to a bit flag, we can just use
// countLeadingZeros.
-static int getRankProximity(OutputSection *A, OutputSection *B) {
+static int getRankProximityAux(OutputSection *A, OutputSection *B) {
return countLeadingZeros(A->SortRank ^ B->SortRank);
}
static int getRankProximity(OutputSection *A, BaseCommand *B) {
- if (auto *Cmd = dyn_cast<OutputSectionCommand>(B))
- if (Cmd->Sec)
- return getRankProximity(A, Cmd->Sec);
+ if (auto *Sec = dyn_cast<OutputSection>(B))
+ if (Sec->Live)
+ return getRankProximityAux(A, Sec);
return -1;
}
@@ -975,7 +977,7 @@ static int getRankProximity(OutputSection *A, BaseCommand *B) {
// rw_sec : { *(rw_sec) }
// would mean that the RW PT_LOAD would become unaligned.
static bool shouldSkip(BaseCommand *Cmd) {
- if (isa<OutputSectionCommand>(Cmd))
+ if (isa<OutputSection>(Cmd))
return false;
if (auto *Assign = dyn_cast<SymbolAssignment>(Cmd))
return Assign->Name != ".";
@@ -989,7 +991,7 @@ template <typename ELFT>
static std::vector<BaseCommand *>::iterator
findOrphanPos(std::vector<BaseCommand *>::iterator B,
std::vector<BaseCommand *>::iterator E) {
- OutputSection *Sec = cast<OutputSectionCommand>(*E)->Sec;
+ OutputSection *Sec = cast<OutputSection>(*E);
// Find the first element that has as close a rank as possible.
auto I = std::max_element(B, E, [=](BaseCommand *A, BaseCommand *B) {
@@ -1001,44 +1003,53 @@ findOrphanPos(std::vector<BaseCommand *>::iterator B,
// Consider all existing sections with the same proximity.
int Proximity = getRankProximity(Sec, *I);
for (; I != E; ++I) {
- auto *Cmd = dyn_cast<OutputSectionCommand>(*I);
- if (!Cmd || !Cmd->Sec)
+ auto *CurSec = dyn_cast<OutputSection>(*I);
+ if (!CurSec || !CurSec->Live)
continue;
- if (getRankProximity(Sec, Cmd->Sec) != Proximity ||
- Sec->SortRank < Cmd->Sec->SortRank)
+ if (getRankProximity(Sec, CurSec) != Proximity ||
+ Sec->SortRank < CurSec->SortRank)
break;
}
- auto J = std::find_if(
- llvm::make_reverse_iterator(I), llvm::make_reverse_iterator(B),
- [](BaseCommand *Cmd) { return isa<OutputSectionCommand>(Cmd); });
+ auto J = std::find_if(llvm::make_reverse_iterator(I),
+ llvm::make_reverse_iterator(B), [](BaseCommand *Cmd) {
+ auto *OS = dyn_cast<OutputSection>(Cmd);
+ return OS && OS->Live;
+ });
I = J.base();
+
+ // As a special case, if the orphan section is the last section, put
+ // it at the very end, past any other commands.
+ // This matches bfd's behavior and is convenient when the linker script fully
+ // specifies the start of the file, but doesn't care about the end (the non
+ // alloc sections for example).
+ auto NextSec = std::find_if(
+ I, E, [](BaseCommand *Cmd) { return isa<OutputSection>(Cmd); });
+ if (NextSec == E)
+ return E;
+
while (I != E && shouldSkip(*I))
++I;
return I;
}
template <class ELFT> void Writer<ELFT>::sortSections() {
+ Script->adjustSectionsBeforeSorting();
+
// 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 (Script->Opt.HasSections)
- Script->adjustSectionsBeforeSorting();
-
- for (BaseCommand *Base : Script->Opt.Commands)
- if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base))
- if (OutputSection *Sec = Cmd->Sec)
- Sec->SortRank = getSectionRank(Sec);
+ for (BaseCommand *Base : Script->SectionCommands)
+ if (auto *Sec = dyn_cast<OutputSection>(Base))
+ Sec->SortRank = getSectionRank(Sec);
- if (!Script->Opt.HasSections) {
- // We know that all the OutputSectionCommands are contiguous in
+ if (!Script->HasSectionsCommand) {
+ // We know that all the OutputSections are contiguous in
// this case.
- auto E = Script->Opt.Commands.end();
- auto I = Script->Opt.Commands.begin();
- auto IsSection = [](BaseCommand *Base) {
- return isa<OutputSectionCommand>(Base);
- };
+ auto E = Script->SectionCommands.end();
+ auto I = Script->SectionCommands.begin();
+ auto IsSection = [](BaseCommand *Base) { return isa<OutputSection>(Base); };
I = std::find_if(I, E, IsSection);
E = std::find_if(llvm::make_reverse_iterator(E),
llvm::make_reverse_iterator(I), IsSection)
@@ -1078,7 +1089,7 @@ template <class ELFT> void Writer<ELFT>::sortSections() {
// a PT_LOAD.
//
// There is some ambiguity as to where exactly a new entry should be
- // inserted, because Opt.Commands contains not only output section
+ // inserted, because Commands contains not only output section
// commands but also other types of commands such as symbol assignment
// expressions. There's no correct answer here due to the lack of the
// formal specification of the linker script. We use heuristics to
@@ -1086,11 +1097,11 @@ template <class ELFT> void Writer<ELFT>::sortSections() {
// after another commands. For the details, look at shouldSkip
// function.
- auto I = Script->Opt.Commands.begin();
- auto E = Script->Opt.Commands.end();
+ auto I = Script->SectionCommands.begin();
+ auto E = Script->SectionCommands.end();
auto NonScriptI = std::find_if(I, E, [](BaseCommand *Base) {
- if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base))
- return Cmd->Sec && Cmd->Sec->SectionIndex == INT_MAX;
+ if (auto *Sec = dyn_cast<OutputSection>(Base))
+ return Sec->Live && Sec->SectionIndex == INT_MAX;
return false;
});
@@ -1110,13 +1121,13 @@ template <class ELFT> void Writer<ELFT>::sortSections() {
while (NonScriptI != E) {
auto Pos = findOrphanPos<ELFT>(I, NonScriptI);
- OutputSection *Orphan = cast<OutputSectionCommand>(*NonScriptI)->Sec;
+ OutputSection *Orphan = cast<OutputSection>(*NonScriptI);
// As an optimization, find all sections with the same sort rank
// and insert them with one rotate.
unsigned Rank = Orphan->SortRank;
auto End = std::find_if(NonScriptI + 1, E, [=](BaseCommand *Cmd) {
- return cast<OutputSectionCommand>(Cmd)->Sec->SortRank != Rank;
+ return cast<OutputSection>(Cmd)->SortRank != Rank;
});
std::rotate(Pos, NonScriptI, End);
NonScriptI = End;
@@ -1132,11 +1143,19 @@ static void applySynthetic(const std::vector<SyntheticSection *> &Sections,
Fn(SS);
}
-// 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 the output.
-static void removeUnusedSyntheticSections(std::vector<OutputSection *> &V) {
+// In order to allow users to manipulate linker-synthesized sections,
+// we had to add synthetic sections to the input section list early,
+// even before we make decisions whether they are needed. This allows
+// users to write scripts like this: ".mygot : { .got }".
+//
+// Doing it has an unintended side effects. If it turns out that we
+// don't need a .got (for example) at all because there's no
+// relocation that needs a .got, we don't want to emit .got.
+//
+// To deal with the above problem, this function is called after
+// scanRelocations is called to remove synthetic sections that turn
+// out to be empty.
+static void removeUnusedSyntheticSections() {
// All input synthetic sections that can be empty are placed after
// all regular ones. We iterate over them all and exit at first
// non-synthetic.
@@ -1147,15 +1166,58 @@ static void removeUnusedSyntheticSections(std::vector<OutputSection *> &V) {
OutputSection *OS = SS->getParent();
if (!SS->empty() || !OS)
continue;
- OS->Sections.erase(std::find(OS->Sections.begin(), OS->Sections.end(), SS));
- SS->Live = false;
+
+ std::vector<BaseCommand *>::iterator Empty = OS->SectionCommands.end();
+ for (auto I = OS->SectionCommands.begin(), E = OS->SectionCommands.end();
+ I != E; ++I) {
+ BaseCommand *B = *I;
+ if (auto *ISD = dyn_cast<InputSectionDescription>(B)) {
+ llvm::erase_if(ISD->Sections,
+ [=](InputSection *IS) { return IS == SS; });
+ if (ISD->Sections.empty())
+ Empty = I;
+ }
+ }
+ if (Empty != OS->SectionCommands.end())
+ OS->SectionCommands.erase(Empty);
+
// If there are no other sections in the output section, remove it from the
// output.
- if (OS->Sections.empty())
- V.erase(std::find(V.begin(), V.end(), OS));
+ if (OS->SectionCommands.empty())
+ OS->Live = false;
}
}
+// 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.
+static bool computeIsPreemptible(const SymbolBody &B) {
+ assert(!B.isLocal());
+ // Only symbols that appear in dynsym can be preempted.
+ if (!B.symbol()->includeInDynsym())
+ return false;
+
+ // Only default visibility symbols can be preempted.
+ if (B.symbol()->Visibility != STV_DEFAULT)
+ return false;
+
+ // At this point copy relocations have not been created yet, so any
+ // symbol that is not defined locally is preemptible.
+ if (!B.isInCurrentDSO())
+ return true;
+
+ // If we have a dynamic list it specifies which local symbols are preemptible.
+ if (Config->HasDynamicList)
+ return false;
+
+ if (!Config->Shared)
+ return false;
+
+ // -Bsymbolic means that definitions are not preempted.
+ if (Config->Bsymbolic || (Config->BsymbolicFunctions && B.isFunc()))
+ return false;
+ return true;
+}
+
// Create output section objects and add them to OutputSections.
template <class ELFT> void Writer<ELFT>::finalizeSections() {
Out::DebugInfo = findSection(".debug_info");
@@ -1168,8 +1230,9 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
// addresses of each section by section name. Add such symbols.
if (!Config->Relocatable) {
addStartEndSymbols();
- for (OutputSection *Sec : OutputSections)
- addStartStopSymbols(Sec);
+ for (BaseCommand *Base : Script->SectionCommands)
+ if (auto *Sec = dyn_cast<OutputSection>(Base))
+ addStartStopSymbols(Sec);
}
// Add _DYNAMIC symbol. Unlike GNU gold, our _DYNAMIC symbol has no type.
@@ -1177,7 +1240,9 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
// Even the author of gold doesn't remember why gold behaves that way.
// https://sourceware.org/ml/binutils/2002-03/msg00360.html
if (InX::DynSymTab)
- addRegular<ELFT>("_DYNAMIC", InX::Dynamic, 0);
+ Symtab->addRegular<ELFT>("_DYNAMIC", STV_HIDDEN, STT_NOTYPE, 0 /*Value*/,
+ /*Size=*/0, STB_WEAK, InX::Dynamic,
+ /*File=*/nullptr);
// Define __rel[a]_iplt_{start,end} symbols if needed.
addRelIpltSymbols();
@@ -1188,9 +1253,13 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
applySynthetic({In<ELFT>::EhFrame},
[](SyntheticSection *SS) { SS->finalizeContents(); });
+ for (Symbol *S : Symtab->getSymbols())
+ S->body()->IsPreemptible |= computeIsPreemptible(*S->body());
+
// Scan relocations. This must be done after every symbol is declared so that
// we can correctly decide if a dynamic relocation is needed.
- forEachRelSec(scanRelocations<ELFT>);
+ if (!Config->Relocatable)
+ forEachRelSec(scanRelocations<ELFT>);
if (InX::Plt && !InX::Plt->empty())
InX::Plt->addSymbols();
@@ -1199,7 +1268,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
// Now that we have defined all possible global symbols including linker-
// synthesized ones. Visit all symbols to give the finishing touches.
- for (Symbol *S : Symtab<ELFT>::X->getSymbols()) {
+ for (Symbol *S : Symtab->getSymbols()) {
SymbolBody *Body = S->body();
if (!includeInSymtab(*Body))
@@ -1210,7 +1279,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
if (InX::DynSymTab && S->includeInDynsym()) {
InX::DynSymTab->addSymbol(Body);
if (auto *SS = dyn_cast<SharedSymbol>(Body))
- if (cast<SharedFile<ELFT>>(SS->File)->isNeeded())
+ if (cast<SharedFile<ELFT>>(S->File)->isNeeded())
In<ELFT>::VerNeed->addSymbol(SS);
}
}
@@ -1220,16 +1289,23 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
return;
addPredefinedSections();
- removeUnusedSyntheticSections(OutputSections);
+ removeUnusedSyntheticSections();
- clearOutputSections();
sortSections();
+ Script->removeEmptyCommands();
// Now that we have the final list, create a list of all the
- // OutputSectionCommands for convenience.
- for (BaseCommand *Base : Script->Opt.Commands)
- if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base))
- OutputSectionCommands.push_back(Cmd);
+ // OutputSections for convenience.
+ for (BaseCommand *Base : Script->SectionCommands)
+ if (auto *Sec = dyn_cast<OutputSection>(Base))
+ OutputSections.push_back(Sec);
+
+ // Prefer command line supplied address over other constraints.
+ for (OutputSection *Sec : OutputSections) {
+ auto I = Config->SectionStartMap.find(Sec->Name);
+ if (I != Config->SectionStartMap.end())
+ Sec->AddrExpr = [=] { return I->second; };
+ }
// This is a bit of a hack. A value of 0 means undef, so we set it
// to 1 t make __ehdr_start defined. The section number is not
@@ -1237,8 +1313,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
Out::ElfHeader->SectionIndex = 1;
unsigned I = 1;
- for (OutputSectionCommand *Cmd : OutputSectionCommands) {
- OutputSection *Sec = Cmd->Sec;
+ for (OutputSection *Sec : OutputSections) {
Sec->SectionIndex = I++;
Sec->ShName = InX::ShStrTab->addString(Sec->Name);
}
@@ -1252,24 +1327,29 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
Out::ProgramHeaders->Size = sizeof(Elf_Phdr) * Phdrs.size();
}
- // Compute the size of .rela.dyn and .rela.plt early since we need
- // them to populate .dynamic.
- for (SyntheticSection *SS : {In<ELFT>::RelaDyn, In<ELFT>::RelaPlt})
- if (SS->getParent() && !SS->empty())
- SS->getParent()->assignOffsets();
+ // Some symbols are defined in term of program headers. Now that we
+ // have the headers, we can find out which sections they point to.
+ setReservedSymbolSections();
// Dynamic section must be the last one in this list and dynamic
// symbol table section (DynSymTab) must be the first one.
- applySynthetic({InX::DynSymTab, InX::Bss, InX::BssRelRo,
- InX::GnuHashTab, In<ELFT>::HashTab, InX::SymTab,
- InX::ShStrTab, InX::StrTab, In<ELFT>::VerDef,
- InX::DynStrTab, InX::GdbIndex, InX::Got,
- InX::MipsGot, InX::IgotPlt, InX::GotPlt,
- In<ELFT>::RelaDyn, In<ELFT>::RelaIplt, In<ELFT>::RelaPlt,
- InX::Plt, InX::Iplt, In<ELFT>::EhFrameHdr,
- In<ELFT>::VerSym, In<ELFT>::VerNeed, InX::Dynamic},
+ applySynthetic({InX::DynSymTab, InX::Bss,
+ InX::BssRelRo, InX::GnuHashTab,
+ InX::HashTab, InX::SymTab,
+ InX::ShStrTab, InX::StrTab,
+ In<ELFT>::VerDef, InX::DynStrTab,
+ InX::Got, InX::MipsGot,
+ InX::IgotPlt, InX::GotPlt,
+ In<ELFT>::RelaDyn, In<ELFT>::RelaIplt,
+ In<ELFT>::RelaPlt, InX::Plt,
+ InX::Iplt, In<ELFT>::EhFrameHdr,
+ In<ELFT>::VerSym, In<ELFT>::VerNeed,
+ InX::Dynamic},
[](SyntheticSection *SS) { SS->finalizeContents(); });
+ if (!Script->HasSectionsCommand && !Config->Relocatable)
+ fixSectionAlignments();
+
// 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.
@@ -1281,10 +1361,11 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
// are out of range. This will need to turn into a loop that converges
// when no more Thunks are added
ThunkCreator TC;
- if (TC.createThunks(OutputSectionCommands)) {
+ Script->assignAddresses();
+ if (TC.createThunks(OutputSections)) {
applySynthetic({InX::MipsGot},
[](SyntheticSection *SS) { SS->updateAllocSize(); });
- if (TC.createThunks(OutputSectionCommands))
+ if (TC.createThunks(OutputSections))
fatal("All non-range thunks should be created in first call");
}
}
@@ -1292,8 +1373,8 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
// Fill other section headers. The dynamic table is finalized
// at the end because some tags like RELSZ depend on result
// of finalizing other sections.
- for (OutputSectionCommand *Cmd : OutputSectionCommands)
- Cmd->finalize<ELFT>();
+ for (OutputSection *Sec : OutputSections)
+ Sec->finalize<ELFT>();
// createThunks may have added local symbols to the static symbol table
applySynthetic({InX::SymTab, InX::ShStrTab, InX::StrTab},
@@ -1303,21 +1384,12 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
template <class ELFT> void Writer<ELFT>::addPredefinedSections() {
// ARM ABI requires .ARM.exidx to be terminated by some piece of data.
// We have the terminater synthetic section class. Add that at the end.
- auto *OS = dyn_cast_or_null<OutputSection>(findSection(".ARM.exidx"));
- if (!OS || OS->Sections.empty() || Config->Relocatable)
+ OutputSection *Cmd = findSection(".ARM.exidx");
+ if (!Cmd || !Cmd->Live || Config->Relocatable)
return;
auto *Sentinel = make<ARMExidxSentinelSection>();
- OS->addSection(Sentinel);
- // If there are linker script commands existing at this point then add the
- // sentinel to the last of these too.
- if (OutputSectionCommand *C = Script->getCmd(OS)) {
- auto ISD = std::find_if(C->Commands.rbegin(), C->Commands.rend(),
- [](const BaseCommand *Base) {
- return isa<InputSectionDescription>(Base);
- });
- cast<InputSectionDescription>(*ISD)->Sections.push_back(Sentinel);
- }
+ Cmd->addSection(Sentinel);
}
// The linker is expected to define SECNAME_start and SECNAME_end
@@ -1359,24 +1431,11 @@ void Writer<ELFT>::addStartStopSymbols(OutputSection *Sec) {
addOptionalRegular<ELFT>(Saver.save("__stop_" + S), Sec, -1, STV_DEFAULT);
}
-template <class ELFT>
-OutputSectionCommand *Writer<ELFT>::findSectionCommand(StringRef Name) {
- for (OutputSectionCommand *Cmd : OutputSectionCommands)
- if (Cmd->Name == Name)
- return Cmd;
- return nullptr;
-}
-
-template <class ELFT> OutputSection *Writer<ELFT>::findSectionInScript(StringRef Name) {
- if (OutputSectionCommand *Cmd = findSectionCommand(Name))
- return Cmd->Sec;
- return nullptr;
-}
-
template <class ELFT> OutputSection *Writer<ELFT>::findSection(StringRef Name) {
- for (OutputSection *Sec : OutputSections)
- if (Sec->Name == Name)
- return Sec;
+ for (BaseCommand *Base : Script->SectionCommands)
+ if (auto *Sec = dyn_cast<OutputSection>(Base))
+ if (Sec->Name == Name)
+ return Sec;
return nullptr;
}
@@ -1406,19 +1465,19 @@ static uint64_t computeFlags(uint64_t Flags) {
// Decide which program headers to create and which sections to include in each
// one.
-template <class ELFT> std::vector<PhdrEntry> Writer<ELFT>::createPhdrs() {
- std::vector<PhdrEntry> Ret;
+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();
+ Ret.push_back(make<PhdrEntry>(Type, Flags));
+ return Ret.back();
};
// The first phdr entry is PT_PHDR which describes the program header itself.
AddHdr(PT_PHDR, PF_R)->add(Out::ProgramHeaders);
// PT_INTERP must be the second entry if exists.
- if (OutputSection *Sec = findSectionInScript(".interp"))
- AddHdr(PT_INTERP, Sec->getPhdrFlags())->add(Sec);
+ if (OutputSection *Cmd = findSection(".interp"))
+ AddHdr(PT_INTERP, Cmd->getPhdrFlags())->add(Cmd);
// Add the first PT_LOAD segment for regular output sections.
uint64_t Flags = computeFlags(PF_R);
@@ -1428,8 +1487,7 @@ template <class ELFT> std::vector<PhdrEntry> Writer<ELFT>::createPhdrs() {
Load->add(Out::ElfHeader);
Load->add(Out::ProgramHeaders);
- for (OutputSectionCommand *Cmd : OutputSectionCommands) {
- OutputSection *Sec = Cmd->Sec;
+ for (OutputSection *Sec : OutputSections) {
if (!(Sec->Flags & SHF_ALLOC))
break;
if (!needsPtLoad(Sec))
@@ -1441,7 +1499,7 @@ template <class ELFT> std::vector<PhdrEntry> Writer<ELFT>::createPhdrs() {
// different flags or is loaded at a discontiguous address using AT linker
// script command.
uint64_t NewFlags = computeFlags(Sec->getPhdrFlags());
- if (Script->hasLMA(Sec) || Flags != NewFlags) {
+ if (Sec->LMAExpr || Flags != NewFlags) {
Load = AddHdr(PT_LOAD, NewFlags);
Flags = NewFlags;
}
@@ -1450,14 +1508,12 @@ template <class ELFT> std::vector<PhdrEntry> Writer<ELFT>::createPhdrs() {
}
// Add a TLS segment if any.
- PhdrEntry TlsHdr(PT_TLS, PF_R);
- for (OutputSectionCommand *Cmd : OutputSectionCommands) {
- OutputSection *Sec = Cmd->Sec;
+ PhdrEntry *TlsHdr = make<PhdrEntry>(PT_TLS, PF_R);
+ for (OutputSection *Sec : OutputSections)
if (Sec->Flags & SHF_TLS)
- TlsHdr.add(Sec);
- }
- if (TlsHdr.First)
- Ret.push_back(std::move(TlsHdr));
+ TlsHdr->add(Sec);
+ if (TlsHdr->FirstSec)
+ Ret.push_back(TlsHdr);
// Add an entry for .dynamic.
if (InX::DynSymTab)
@@ -1466,14 +1522,12 @@ template <class ELFT> std::vector<PhdrEntry> Writer<ELFT>::createPhdrs() {
// PT_GNU_RELRO includes all sections that should be marked as
// read-only by dynamic linker after proccessing relocations.
- PhdrEntry RelRo(PT_GNU_RELRO, PF_R);
- for (OutputSectionCommand *Cmd : OutputSectionCommands) {
- OutputSection *Sec = Cmd->Sec;
+ PhdrEntry *RelRo = make<PhdrEntry>(PT_GNU_RELRO, PF_R);
+ for (OutputSection *Sec : OutputSections)
if (needsPtLoad(Sec) && isRelroSection(Sec))
- RelRo.add(Sec);
- }
- if (RelRo.First)
- Ret.push_back(std::move(RelRo));
+ RelRo->add(Sec);
+ if (RelRo->FirstSec)
+ Ret.push_back(RelRo);
// PT_GNU_EH_FRAME is a special section pointing on .eh_frame_hdr.
if (!In<ELFT>::EhFrame->empty() && In<ELFT>::EhFrameHdr &&
@@ -1483,8 +1537,8 @@ template <class ELFT> std::vector<PhdrEntry> Writer<ELFT>::createPhdrs() {
// PT_OPENBSD_RANDOMIZE is an OpenBSD-specific feature. That makes
// the dynamic linker fill the segment with random data.
- if (OutputSection *Sec = findSectionInScript(".openbsd.randomdata"))
- AddHdr(PT_OPENBSD_RANDOMIZE, Sec->getPhdrFlags())->add(Sec);
+ if (OutputSection *Cmd = findSection(".openbsd.randomdata"))
+ AddHdr(PT_OPENBSD_RANDOMIZE, Cmd->getPhdrFlags())->add(Cmd);
// PT_GNU_STACK is a special section to tell the loader to make the
// pages for the stack non-executable. If you really want an executable
@@ -1506,10 +1560,9 @@ template <class ELFT> std::vector<PhdrEntry> Writer<ELFT>::createPhdrs() {
// Create one PT_NOTE per a group of contiguous .note sections.
PhdrEntry *Note = nullptr;
- for (OutputSectionCommand *Cmd : OutputSectionCommands) {
- OutputSection *Sec = Cmd->Sec;
+ for (OutputSection *Sec : OutputSections) {
if (Sec->Type == SHT_NOTE) {
- if (!Note || Script->hasLMA(Sec))
+ if (!Note || Sec->LMAExpr)
Note = AddHdr(PT_NOTE, PF_R);
Note->add(Sec);
} else {
@@ -1520,20 +1573,18 @@ template <class ELFT> std::vector<PhdrEntry> Writer<ELFT>::createPhdrs() {
}
template <class ELFT>
-void Writer<ELFT>::addPtArmExid(std::vector<PhdrEntry> &Phdrs) {
+void Writer<ELFT>::addPtArmExid(std::vector<PhdrEntry *> &Phdrs) {
if (Config->EMachine != EM_ARM)
return;
- auto I =
- std::find_if(OutputSectionCommands.begin(), OutputSectionCommands.end(),
- [](OutputSectionCommand *Cmd) {
- return Cmd->Sec->Type == SHT_ARM_EXIDX;
- });
- if (I == OutputSectionCommands.end())
+ auto I = llvm::find_if(OutputSections, [](OutputSection *Cmd) {
+ return Cmd->Type == SHT_ARM_EXIDX;
+ });
+ if (I == OutputSections.end())
return;
// PT_ARM_EXIDX is the ARM EHABI equivalent of PT_GNU_EH_FRAME
- PhdrEntry ARMExidx(PT_ARM_EXIDX, PF_R);
- ARMExidx.add((*I)->Sec);
+ PhdrEntry *ARMExidx = make<PhdrEntry>(PT_ARM_EXIDX, PF_R);
+ ARMExidx->add(*I);
Phdrs.push_back(ARMExidx);
}
@@ -1541,33 +1592,31 @@ void Writer<ELFT>::addPtArmExid(std::vector<PhdrEntry> &Phdrs) {
// 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() {
- auto PageAlign = [](OutputSection *Sec) {
- OutputSectionCommand *Cmd = Script->getCmd(Sec);
+ auto PageAlign = [](OutputSection *Cmd) {
if (Cmd && !Cmd->AddrExpr)
Cmd->AddrExpr = [=] {
return alignTo(Script->getDot(), Config->MaxPageSize);
};
};
- for (const PhdrEntry &P : Phdrs)
- if (P.p_type == PT_LOAD && P.First)
- PageAlign(P.First);
+ for (const PhdrEntry *P : Phdrs)
+ if (P->p_type == PT_LOAD && P->FirstSec)
+ PageAlign(P->FirstSec);
- for (const PhdrEntry &P : Phdrs) {
- if (P.p_type != PT_GNU_RELRO)
+ for (const PhdrEntry *P : Phdrs) {
+ if (P->p_type != PT_GNU_RELRO)
continue;
- if (P.First)
- PageAlign(P.First);
+ if (P->FirstSec)
+ PageAlign(P->FirstSec);
// Find the first section after PT_GNU_RELRO. If it is in a PT_LOAD we
// have to align it to a page.
- auto End = OutputSectionCommands.end();
- auto I =
- std::find(OutputSectionCommands.begin(), End, Script->getCmd(P.Last));
+ auto End = OutputSections.end();
+ auto I = std::find(OutputSections.begin(), End, P->LastSec);
if (I == End || (I + 1) == End)
continue;
- OutputSection *Sec = (*(I + 1))->Sec;
- if (needsPtLoad(Sec))
- PageAlign(Sec);
+ OutputSection *Cmd = (*(I + 1));
+ if (needsPtLoad(Cmd))
+ PageAlign(Cmd);
}
}
@@ -1575,40 +1624,39 @@ template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
// its new file offset. The file offset must be the same with its
// virtual address (modulo the page size) so that the loader can load
// executables without any address adjustment.
-static uint64_t getFileAlignment(uint64_t Off, OutputSection *Sec) {
- OutputSection *First = Sec->FirstInPtLoad;
+static uint64_t getFileAlignment(uint64_t Off, OutputSection *Cmd) {
// If the section is not in a PT_LOAD, we just have to align it.
- if (!First)
- return alignTo(Off, Sec->Alignment);
+ if (!Cmd->PtLoad)
+ return alignTo(Off, Cmd->Alignment);
+ OutputSection *First = Cmd->PtLoad->FirstSec;
// 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 (Cmd == First)
+ return alignTo(Off, std::max<uint64_t>(Cmd->Alignment, Config->MaxPageSize),
+ Cmd->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;
+ return First->Offset + Cmd->Addr - First->Addr;
}
-static uint64_t setOffset(OutputSection *Sec, uint64_t Off) {
- if (Sec->Type == SHT_NOBITS) {
- Sec->Offset = Off;
+static uint64_t setOffset(OutputSection *Cmd, uint64_t Off) {
+ if (Cmd->Type == SHT_NOBITS) {
+ Cmd->Offset = Off;
return Off;
}
- Off = getFileAlignment(Off, Sec);
- Sec->Offset = Off;
- return Off + Sec->Size;
+ Off = getFileAlignment(Off, Cmd);
+ Cmd->Offset = Off;
+ return Off + Cmd->Size;
}
template <class ELFT> void Writer<ELFT>::assignFileOffsetsBinary() {
uint64_t Off = 0;
- for (OutputSectionCommand *Cmd : OutputSectionCommands) {
- OutputSection *Sec = Cmd->Sec;
+ for (OutputSection *Sec : OutputSections)
if (Sec->Flags & SHF_ALLOC)
Off = setOffset(Sec, Off);
- }
FileSize = alignTo(Off, Config->Wordsize);
}
@@ -1618,46 +1666,58 @@ template <class ELFT> void Writer<ELFT>::assignFileOffsets() {
Off = setOffset(Out::ElfHeader, Off);
Off = setOffset(Out::ProgramHeaders, Off);
- for (OutputSectionCommand *Cmd : OutputSectionCommands)
- Off = setOffset(Cmd->Sec, Off);
+ PhdrEntry *LastRX = nullptr;
+ for (PhdrEntry *P : Phdrs)
+ if (P->p_type == PT_LOAD && (P->p_flags & PF_X))
+ LastRX = P;
+
+ for (OutputSection *Sec : OutputSections) {
+ Off = setOffset(Sec, Off);
+ if (Script->HasSectionsCommand)
+ continue;
+ // If this is a last section of the last executable segment and that
+ // segment is the last loadable segment, align the offset of the
+ // following section to avoid loading non-segments parts of the file.
+ if (LastRX && LastRX->LastSec == Sec)
+ Off = alignTo(Off, Target->PageSize);
+ }
SectionHeaderOff = alignTo(Off, Config->Wordsize);
- FileSize =
- SectionHeaderOff + (OutputSectionCommands.size() + 1) * sizeof(Elf_Shdr);
+ FileSize = SectionHeaderOff + (OutputSections.size() + 1) * sizeof(Elf_Shdr);
}
// 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 (PhdrEntry &P : Phdrs) {
- OutputSection *First = P.First;
- OutputSection *Last = P.Last;
+ for (PhdrEntry *P : Phdrs) {
+ OutputSection *First = P->FirstSec;
+ OutputSection *Last = P->LastSec;
if (First) {
- P.p_filesz = Last->Offset - First->Offset;
+ 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();
+ 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 (P.p_type == PT_LOAD)
- P.p_align = Config->MaxPageSize;
- else if (P.p_type == PT_GNU_RELRO) {
- P.p_align = 1;
+ if (P->p_type == PT_LOAD)
+ P->p_align = std::max<uint64_t>(P->p_align, Config->MaxPageSize);
+ else if (P->p_type == PT_GNU_RELRO) {
+ P->p_align = 1;
// The glibc dynamic loader rounds the size down, so we need to round up
// to protect the last page. This is a no-op on FreeBSD which always
// rounds up.
- P.p_memsz = alignTo(P.p_memsz, Target->PageSize);
+ P->p_memsz = alignTo(P->p_memsz, Target->PageSize);
}
// 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 (P.p_type == PT_TLS) {
- Out::TlsPhdr = &P;
- if (P.p_memsz)
- P.p_memsz = alignTo(P.p_memsz, P.p_align);
+ if (P->p_type == PT_TLS) {
+ Out::TlsPhdr = P;
+ if (P->p_memsz)
+ P->p_memsz = alignTo(P->p_memsz, P->p_align);
}
}
}
@@ -1672,14 +1732,14 @@ template <class ELFT> void Writer<ELFT>::setPhdrs() {
template <class ELFT> uint64_t 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))
+ if (SymbolBody *B = Symtab->find(Config->Entry))
return B->getVA();
uint64_t Addr;
if (to_integer(Config->Entry, Addr))
return Addr;
// Case 4
- if (OutputSection *Sec = findSectionInScript(".text")) {
+ if (OutputSection *Sec = findSection(".text")) {
if (Config->WarnMissingEntry)
warn("cannot find entry symbol " + Config->Entry + "; defaulting to 0x" +
utohexstr(Sec->Addr));
@@ -1701,64 +1761,6 @@ static uint16_t getELFType() {
return ET_EXEC;
}
-// This function is called after we have assigned address and size
-// to each section. This function fixes some predefined
-// symbol values that depend on section address and size.
-template <class ELFT> void Writer<ELFT>::fixPredefinedSymbols() {
- // _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.
- PhdrEntry *Last = nullptr;
- PhdrEntry *LastRO = nullptr;
- PhdrEntry *LastRW = nullptr;
- for (PhdrEntry &P : Phdrs) {
- if (P.p_type != PT_LOAD)
- continue;
- Last = &P;
- if (P.p_flags & PF_W)
- LastRW = &P;
- else
- LastRO = &P;
- }
-
- auto Set = [](DefinedRegular *S, OutputSection *Sec, uint64_t Value) {
- if (S) {
- S->Section = Sec;
- S->Value = Value;
- }
- };
-
- if (Last) {
- Set(ElfSym::End1, Last->First, Last->p_memsz);
- Set(ElfSym::End2, Last->First, Last->p_memsz);
- }
- if (LastRO) {
- Set(ElfSym::Etext1, LastRO->First, LastRO->p_filesz);
- Set(ElfSym::Etext2, LastRO->First, LastRO->p_filesz);
- }
- if (LastRW) {
- Set(ElfSym::Edata1, LastRW->First, LastRW->p_filesz);
- Set(ElfSym::Edata2, LastRW->First, LastRW->p_filesz);
- }
-
- if (ElfSym::Bss)
- ElfSym::Bss->Section = findSectionInScript(".bss");
-
- // Setup MIPS _gp_disp/__gnu_local_gp symbols which should
- // be equal to the _gp symbol's value.
- if (Config->EMachine == EM_MIPS && !ElfSym::MipsGp->Value) {
- // Find GP-relative section with the lowest address
- // and use this address to calculate default _gp value.
- for (const OutputSectionCommand *Cmd : OutputSectionCommands) {
- OutputSection *OS = Cmd->Sec;
- if (OS->Flags & SHF_MIPS_GPREL) {
- ElfSym::MipsGp->Value = OS->Addr + 0x7ff0;
- break;
- }
- }
- }
-}
-
template <class ELFT> void Writer<ELFT>::writeHeader() {
uint8_t *Buf = Buffer->getBufferStart();
memcpy(Buf, "\177ELF", 4);
@@ -1777,7 +1779,7 @@ template <class ELFT> void Writer<ELFT>::writeHeader() {
EHdr->e_ehsize = sizeof(Elf_Ehdr);
EHdr->e_phnum = Phdrs.size();
EHdr->e_shentsize = sizeof(Elf_Shdr);
- EHdr->e_shnum = OutputSectionCommands.size() + 1;
+ EHdr->e_shnum = OutputSections.size() + 1;
EHdr->e_shstrndx = InX::ShStrTab->getParent()->SectionIndex;
if (Config->EMachine == EM_ARM)
@@ -1786,7 +1788,7 @@ template <class ELFT> void Writer<ELFT>::writeHeader() {
// 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>();
+ EHdr->e_flags = Config->MipsEFlags;
if (!Config->Relocatable) {
EHdr->e_phoff = sizeof(Elf_Ehdr);
@@ -1795,22 +1797,22 @@ template <class ELFT> void Writer<ELFT>::writeHeader() {
// Write the program header table.
auto *HBuf = reinterpret_cast<Elf_Phdr *>(Buf + EHdr->e_phoff);
- 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;
+ 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 (OutputSectionCommand *Cmd : OutputSectionCommands)
- Cmd->Sec->writeHeaderTo<ELFT>(++SHdrs);
+ for (OutputSection *Sec : OutputSections)
+ Sec->writeHeaderTo<ELFT>(++SHdrs);
}
// Open a result file.
@@ -1833,11 +1835,48 @@ template <class ELFT> void Writer<ELFT>::openFile() {
template <class ELFT> void Writer<ELFT>::writeSectionsBinary() {
uint8_t *Buf = Buffer->getBufferStart();
- for (OutputSectionCommand *Cmd : OutputSectionCommands) {
- OutputSection *Sec = Cmd->Sec;
+ for (OutputSection *Sec : OutputSections)
if (Sec->Flags & SHF_ALLOC)
- Cmd->writeTo<ELFT>(Buf + Sec->Offset);
+ Sec->writeTo<ELFT>(Buf + Sec->Offset);
+}
+
+static void fillTrap(uint8_t *I, uint8_t *End) {
+ for (; I + 4 <= End; I += 4)
+ memcpy(I, &Target->TrapInstr, 4);
+}
+
+// Fill the last page of executable segments with trap instructions
+// instead of leaving them as zero. Even though it is not required by any
+// standard, it is in general a good thing to do for security reasons.
+//
+// We'll leave other pages in segments as-is because the rest will be
+// overwritten by output sections.
+template <class ELFT> void Writer<ELFT>::writeTrapInstr() {
+ if (Script->HasSectionsCommand)
+ return;
+
+ // Fill the last page.
+ uint8_t *Buf = Buffer->getBufferStart();
+ for (PhdrEntry *P : Phdrs)
+ if (P->p_type == PT_LOAD && (P->p_flags & PF_X))
+ fillTrap(Buf + alignDown(P->p_offset + P->p_filesz, Target->PageSize),
+ Buf + alignTo(P->p_offset + P->p_filesz, Target->PageSize));
+
+ // Round up the file size of the last segment to the page boundary iff it is
+ // an executable segment to ensure that other other tools don't accidentally
+ // trim the instruction padding (e.g. when stripping the file).
+ PhdrEntry *LastRX = nullptr;
+ for (PhdrEntry *P : Phdrs) {
+ if (P->p_type != PT_LOAD)
+ continue;
+ if (P->p_flags & PF_X)
+ LastRX = P;
+ else
+ LastRX = nullptr;
}
+ if (LastRX)
+ LastRX->p_memsz = LastRX->p_filesz =
+ alignTo(LastRX->p_filesz, Target->PageSize);
}
// Write section contents to a mmap'ed file.
@@ -1846,8 +1885,8 @@ template <class ELFT> void Writer<ELFT>::writeSections() {
// PPC64 needs to process relocations in the .opd section
// before processing relocations in code-containing sections.
- if (auto *OpdCmd = findSectionCommand(".opd")) {
- Out::Opd = OpdCmd->Sec;
+ if (auto *OpdCmd = findSection(".opd")) {
+ Out::Opd = OpdCmd;
Out::OpdBuf = Buf + Out::Opd->Offset;
OpdCmd->template writeTo<ELFT>(Buf + Out::Opd->Offset);
}
@@ -1860,25 +1899,19 @@ template <class ELFT> void Writer<ELFT>::writeSections() {
// In -r or -emit-relocs mode, write the relocation sections first as in
// ELf_Rel targets we might find out that we need to modify the relocated
// section while doing it.
- for (OutputSectionCommand *Cmd : OutputSectionCommands) {
- OutputSection *Sec = Cmd->Sec;
+ for (OutputSection *Sec : OutputSections)
if (Sec->Type == SHT_REL || Sec->Type == SHT_RELA)
- Cmd->writeTo<ELFT>(Buf + Sec->Offset);
- }
+ Sec->writeTo<ELFT>(Buf + Sec->Offset);
- for (OutputSectionCommand *Cmd : OutputSectionCommands) {
- OutputSection *Sec = Cmd->Sec;
+ for (OutputSection *Sec : OutputSections)
if (Sec != Out::Opd && Sec != EhFrameHdr && Sec->Type != SHT_REL &&
Sec->Type != SHT_RELA)
- Cmd->writeTo<ELFT>(Buf + Sec->Offset);
- }
+ Sec->writeTo<ELFT>(Buf + Sec->Offset);
// The .eh_frame_hdr depends on .eh_frame section contents, therefore
// it should be written after .eh_frame is written.
- if (EhFrameHdr) {
- OutputSectionCommand *Cmd = Script->getCmd(EhFrameHdr);
- Cmd->writeTo<ELFT>(Buf + EhFrameHdr->Offset);
- }
+ if (EhFrameHdr)
+ EhFrameHdr->writeTo<ELFT>(Buf + EhFrameHdr->Offset);
}
template <class ELFT> void Writer<ELFT>::writeBuildId() {
diff --git a/ELF/Writer.h b/ELF/Writer.h
index e935b6419..7354319d5 100644
--- a/ELF/Writer.h
+++ b/ELF/Writer.h
@@ -20,11 +20,10 @@ namespace elf {
class InputFile;
class OutputSection;
class InputSectionBase;
-template <class ELFT> class ObjectFile;
-template <class ELFT> class SymbolTable;
+template <class ELFT> class ObjFile;
+class SymbolTable;
template <class ELFT> void writeResult();
template <class ELFT> void markLive();
-bool isRelroSection(const OutputSection *Sec);
// This describes a program header entry.
// Each contains type, access flags and range of output sections that will be
@@ -42,20 +41,21 @@ struct PhdrEntry {
uint32_t p_type = 0;
uint32_t p_flags = 0;
- OutputSection *First = nullptr;
- OutputSection *Last = nullptr;
+ OutputSection *FirstSec = nullptr;
+ OutputSection *LastSec = nullptr;
bool HasLMA = false;
};
llvm::StringRef getOutputSectionName(llvm::StringRef Name);
-template <class ELFT> uint32_t getMipsEFlags();
+template <class ELFT> uint32_t calcMipsEFlags();
uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
llvm::StringRef FileName);
bool isMipsN32Abi(const InputFile *F);
-}
-}
+bool isMipsR6();
+} // namespace elf
+} // namespace lld
#endif
diff --git a/MinGW/CMakeLists.txt b/MinGW/CMakeLists.txt
new file mode 100644
index 000000000..538a1bba4
--- /dev/null
+++ b/MinGW/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(LLVM_TARGET_DEFINITIONS Options.td)
+tablegen(LLVM Options.inc -gen-opt-parser-defs)
+add_public_tablegen_target(MinGWOptionsTableGen)
+
+if(NOT LLD_BUILT_STANDALONE)
+ set(tablegen_deps intrinsics_gen)
+endif()
+
+add_lld_library(lldMinGW
+ Driver.cpp
+
+ LINK_COMPONENTS
+ Option
+ Support
+
+ LINK_LIBS
+ lldCOFF
+
+ DEPENDS
+ MinGWOptionsTableGen
+ ${tablegen_deps}
+)
diff --git a/MinGW/Driver.cpp b/MinGW/Driver.cpp
new file mode 100644
index 000000000..ebb66125f
--- /dev/null
+++ b/MinGW/Driver.cpp
@@ -0,0 +1,221 @@
+//===- MinGW/Driver.cpp ---------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// GNU ld style linker driver for COFF currently supporting mingw-w64.
+///
+//===----------------------------------------------------------------------===//
+
+#include "lld/Common/Driver.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+#if !defined(_MSC_VER) && !defined(__MINGW32__)
+#include <unistd.h>
+#endif
+
+using namespace lld;
+using namespace llvm;
+
+LLVM_ATTRIBUTE_NORETURN static void error(const Twine &Msg) {
+ errs() << Msg << "\n";
+ exit(1);
+}
+
+// Create OptTable
+enum {
+ OPT_INVALID = 0,
+#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID,
+#include "Options.inc"
+#undef OPTION
+};
+
+// Create prefix string literals used in Options.td
+#define PREFIX(NAME, VALUE) static const char *const NAME[] = VALUE;
+#include "Options.inc"
+#undef PREFIX
+
+// Create table mapping all options defined in Options.td
+static const opt::OptTable::Info InfoTable[] = {
+#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
+ {X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \
+ X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12},
+#include "Options.inc"
+#undef OPTION
+};
+
+namespace {
+class MinGWOptTable : public opt::OptTable {
+public:
+ MinGWOptTable() : OptTable(InfoTable, false) {}
+ opt::InputArgList parse(ArrayRef<const char *> Argv);
+};
+} // namespace
+
+opt::InputArgList MinGWOptTable::parse(ArrayRef<const char *> Argv) {
+ unsigned MissingIndex;
+ unsigned MissingCount;
+
+ SmallVector<const char *, 256> Vec(Argv.data(), Argv.data() + Argv.size());
+ opt::InputArgList Args = this->ParseArgs(Vec, MissingIndex, MissingCount);
+
+ if (MissingCount)
+ error(StringRef(Args.getArgString(MissingIndex)) + ": missing argument");
+ for (auto *Arg : Args.filtered(OPT_UNKNOWN))
+ error("unknown argument: " + Arg->getSpelling());
+ if (!Args.hasArg(OPT_INPUT) && !Args.hasArg(OPT_l))
+ error("no input files");
+ return Args;
+}
+
+// Find a file by concatenating given paths.
+static Optional<std::string> findFile(StringRef Path1, const Twine &Path2) {
+ SmallString<128> S;
+ sys::path::append(S, Path1, Path2);
+ if (sys::fs::exists(S))
+ return S.str().str();
+ return None;
+}
+
+// This is for -lfoo. We'll look for libfoo.dll.a or libfoo.a from search paths.
+static std::string
+searchLibrary(StringRef Name, ArrayRef<StringRef> SearchPaths, bool BStatic) {
+ if (Name.startswith(":")) {
+ for (StringRef Dir : SearchPaths)
+ if (Optional<std::string> S = findFile(Dir, Name.substr(1)))
+ return *S;
+ error("unable to find library -l" + Name);
+ }
+
+ for (StringRef Dir : SearchPaths) {
+ if (!BStatic)
+ if (Optional<std::string> S = findFile(Dir, "lib" + Name + ".dll.a"))
+ return *S;
+ if (Optional<std::string> S = findFile(Dir, "lib" + Name + ".a"))
+ return *S;
+ }
+ error("unable to find library -l" + Name);
+}
+
+// Convert Unix-ish command line arguments to Windows-ish ones and
+// then call coff::link.
+bool mingw::link(ArrayRef<const char *> ArgsArr, raw_ostream &Diag) {
+ MinGWOptTable Parser;
+ opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
+
+ std::vector<std::string> LinkArgs;
+ auto Add = [&](const Twine &S) { LinkArgs.push_back(S.str()); };
+
+ Add("lld-link");
+ Add("-lldmingw");
+
+ if (auto *A = Args.getLastArg(OPT_entry)) {
+ StringRef S = A->getValue();
+ if (Args.getLastArgValue(OPT_m) == "i386pe" && S.startswith("_"))
+ Add("-entry:" + S.substr(1));
+ else
+ Add("-entry:" + S);
+ }
+
+ if (auto *A = Args.getLastArg(OPT_subs))
+ Add("-subsystem:" + StringRef(A->getValue()));
+ if (auto *A = Args.getLastArg(OPT_out_implib))
+ Add("-implib:" + StringRef(A->getValue()));
+ if (auto *A = Args.getLastArg(OPT_stack))
+ Add("-stack:" + StringRef(A->getValue()));
+ if (auto *A = Args.getLastArg(OPT_output_def))
+ Add("-output-def:" + StringRef(A->getValue()));
+
+ if (auto *A = Args.getLastArg(OPT_o))
+ Add("-out:" + StringRef(A->getValue()));
+ else if (Args.hasArg(OPT_shared))
+ Add("-out:a.dll");
+ else
+ Add("-out:a.exe");
+
+ if (Args.hasArg(OPT_shared))
+ Add("-dll");
+ if (Args.hasArg(OPT_verbose))
+ Add("-verbose");
+ if (Args.hasArg(OPT_export_all_symbols))
+ Add("-export-all-symbols");
+
+ if (auto *A = Args.getLastArg(OPT_m)) {
+ StringRef S = A->getValue();
+ if (S == "i386pe")
+ Add("-machine:x86");
+ else if (S == "i386pep")
+ Add("-machine:x64");
+ else if (S == "thumb2pe")
+ Add("-machine:arm");
+ else if (S == "arm64pe")
+ Add("-machine:arm64");
+ else
+ error("unknown parameter: -m" + S);
+ }
+
+ for (auto *A : Args.filtered(OPT_mllvm))
+ Add("-mllvm:" + StringRef(A->getValue()));
+
+ if (Args.getLastArgValue(OPT_m) == "i386pe")
+ Add("-alternatename:__image_base__=___ImageBase");
+ else
+ Add("-alternatename:__image_base__=__ImageBase");
+
+ std::vector<StringRef> SearchPaths;
+ for (auto *A : Args.filtered(OPT_L))
+ SearchPaths.push_back(A->getValue());
+
+ StringRef Prefix = "";
+ bool Static = false;
+ for (auto *A : Args) {
+ switch (A->getOption().getUnaliasedOption().getID()) {
+ case OPT_INPUT:
+ if (StringRef(A->getValue()).endswith_lower(".def"))
+ Add("-def:" + StringRef(A->getValue()));
+ else
+ Add(Prefix + StringRef(A->getValue()));
+ break;
+ case OPT_l:
+ Add(Prefix + searchLibrary(A->getValue(), SearchPaths, Static));
+ break;
+ case OPT_whole_archive:
+ Prefix = "-wholearchive:";
+ break;
+ case OPT_no_whole_archive:
+ Prefix = "";
+ break;
+ case OPT_Bstatic:
+ Static = true;
+ break;
+ case OPT_Bdynamic:
+ Static = false;
+ break;
+ }
+ }
+
+ if (Args.hasArg(OPT_verbose) || Args.hasArg(OPT__HASH_HASH_HASH))
+ outs() << llvm::join(LinkArgs, " ") << "\n";
+
+ if (Args.hasArg(OPT__HASH_HASH_HASH))
+ return true;
+
+ // Repack vector of strings to vector of const char pointers for coff::link.
+ std::vector<const char *> Vec;
+ for (const std::string &S : LinkArgs)
+ Vec.push_back(S.c_str());
+ return coff::link(Vec);
+}
diff --git a/MinGW/Options.td b/MinGW/Options.td
new file mode 100644
index 000000000..1d5168882
--- /dev/null
+++ b/MinGW/Options.td
@@ -0,0 +1,47 @@
+include "llvm/Option/OptParser.td"
+
+class F<string name>: Flag<["--", "-"], name>;
+class J<string name>: Joined<["--", "-"], name>;
+class S<string name>: Separate<["--", "-"], name>;
+
+def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
+ HelpText<"Add a directory to the library search path">;
+def entry: S<"entry">, MetaVarName<"<entry>">,
+ HelpText<"Name of entry point symbol">;
+def export_all_symbols: F<"export-all-symbols">,
+ HelpText<"Export all symbols even if a def file or dllexport attributes are used">;
+def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">,
+ HelpText<"Root name of library to use">;
+def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
+def mllvm: S<"mllvm">;
+def no_whole_archive: F<"no-whole-archive">,
+ HelpText<"No longer include all object files for following archives">;
+def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">,
+ HelpText<"Path to file to write output">;
+def out_implib: Separate<["--"], "out-implib">, HelpText<"Import library name">;
+def output_def: S<"output-def">, HelpText<"Output def file">;
+def shared: F<"shared">, HelpText<"Build a shared object">;
+def subs: S<"subsystem">, HelpText<"Specify subsystem">;
+def stack: S<"stack">;
+def whole_archive: F<"whole-archive">,
+ HelpText<"Include all object files for following archives">;
+def verbose: F<"verbose">, HelpText<"Verbose mode">;
+
+// LLD specific options
+def _HASH_HASH_HASH : Flag<["-"], "###">,
+ HelpText<"Print (but do not run) the commands to run for this compilation">;
+
+// Currently stubs to avoid errors
+def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries">;
+def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">;
+def O: Joined<["-"], "O">, HelpText<"Optimize output file size">;
+def enable_auto_image_base: F<"enable-auto-image-base">;
+def full_shutdown: Flag<["--"], "full-shutdown">;
+def major_image_version: S<"major-image-version">;
+def minor_image_version: S<"minor-image-version">;
+def sysroot: J<"sysroot">, HelpText<"Sysroot">;
+def v: Flag<["-"], "v">, HelpText<"Display the version number">;
+def version: F<"version">, HelpText<"Display the version number and exit">;
+
+// Alias
+def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias<entry>;
diff --git a/README.md b/README.md
index dc05cdea0..93e2a2d3b 100644
--- a/README.md
+++ b/README.md
@@ -8,3 +8,13 @@ infrastructure project.
lld is open source software. You may freely distribute it under the terms of
the license agreement found in LICENSE.txt.
+
+Benchmarking
+============
+
+In order to make sure various developers can evaluate patches over the
+same tests, we create a collection of self contained programs.
+
+It is hosted at https://s3-us-west-2.amazonaws.com/linker-tests/lld-speed-test.tar.xz
+
+The current sha256 is 4818e8d38961503a04a73597b0da1242c39efc9df2ceb6dfca4ed36845239863.
diff --git a/docs/Driver.rst b/docs/Driver.rst
index 27b378712..4ee6ce0c9 100644
--- a/docs/Driver.rst
+++ b/docs/Driver.rst
@@ -65,7 +65,7 @@ Adding an Option to an existing Flavor
Adding a Flavor
===============
-#. Add an entry for the flavor in :file:`include/lld/Driver/Driver.h` to
+#. Add an entry for the flavor in :file:`include/lld/Common/Driver.h` to
:cpp:class:`lld::UniversalDriver::Flavor`.
#. Add an entry in :file:`lib/Driver/UniversalDriver.cpp` to
diff --git a/docs/NewLLD.rst b/docs/NewLLD.rst
index 67f6b368b..cb27cc59d 100644
--- a/docs/NewLLD.rst
+++ b/docs/NewLLD.rst
@@ -301,7 +301,7 @@ Glossary
they are merged by ICF. It is known as an effective technique,
and it usually reduces C++ program's size by a few percent or more.
- Note that this is not entirely sound optimization. C/C++ require
+ Note that this is not an entirely sound optimization. C/C++ require
different functions have different addresses. If a program depends on
that property, it would fail at runtime.
diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst
index 0d75311e4..dcd6ac060 100644
--- a/docs/ReleaseNotes.rst
+++ b/docs/ReleaseNotes.rst
@@ -1,19 +1,19 @@
=======================
-LLD 5.0.0 Release Notes
+LLD 6.0.0 Release Notes
=======================
.. contents::
:local:
.. warning::
- These are in-progress notes for the upcoming LLVM 5.0.0 release.
+ These are in-progress notes for the upcoming LLVM 6.0.0 release.
Release notes for previous releases can be found on
`the Download Page <http://releases.llvm.org/download.html>`_.
Introduction
============
-This document contains the release notes for the LLD linker, release 5.0.0.
+This document contains the release notes for the LLD linker, release 6.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/>`_.
diff --git a/docs/conf.py b/docs/conf.py
index 410b33e98..28e16f5b8 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 version.
-version = '5'
+version = '6'
# The full version, including alpha/beta/rc tags.
-release = '5'
+release = '6'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/index.rst b/docs/index.rst
index fbdade7da..624a73d94 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -71,30 +71,27 @@ Performance
-----------
This is a link time comparison on a 2-socket 20-core 40-thread Xeon
-E5-2680 2.80 GHz machine with an SSD drive.
-
-LLD is much faster than the GNU linkers for large programs. That's
-fast for small programs too, but because the link time is short
-anyway, the difference is not very noticeable in that case.
-
+E5-2680 2.80 GHz machine with an SSD drive. We ran gold and lld with
+or without multi-threading support. To disable multi-threading, we
+added ``-no-threads`` to the command lines.
+
+============ =========== ============ ==================== ================== =============== =============
+Program Output size GNU ld GNU gold w/o threads GNU gold w/threads lld w/o threads lld w/threads
+ffmpeg dbg 92 MiB 1.72s 1.16s 1.01s 0.60s 0.35s
+mysqld dbg 154 MiB 8.50s 2.96s 2.68s 1.06s 0.68s
+clang dbg 1.67 GiB 104.03s 34.18s 23.49s 14.82s 5.28s
+chromium dbg 1.14 GiB 209.05s [1]_ 64.70s 60.82s 27.60s 16.70s
+============ =========== ============ ==================== ================== =============== =============
+
+As you can see, lld is significantly faster than GNU linkers.
Note that this is just a benchmark result of our environment.
Depending on number of available cores, available amount of memory or
disk latency/throughput, your results may vary.
-============ =========== ============ ============= ======
-Program Output size GNU ld GNU gold [1]_ LLD
-ffmpeg dbg 91 MiB 1.59s 1.15s 0.78s
-mysqld dbg 157 MiB 7.09s 2.49s 1.31s
-clang dbg 1.45 GiB 86.76s 21.93s 8.38s
-chromium dbg 1.52 GiB 142.30s [2]_ 40.86s 12.69s
-============ =========== ============ ============= ======
-
-.. [1] With the ``--threads`` option to enable multi-threading support.
-
-.. [2] Since GNU ld doesn't support the ``-icf=all`` option, we
- removed that from the command line for GNU ld. GNU ld would be
- slower than this if it had that option support. For gold and
- LLD, we use ``-icf=all``.
+.. [1] Since GNU ld doesn't support the ``-icf=all`` and
+ ``-gdb-index`` options, we removed them from the command line
+ for GNU ld. GNU ld would have been slower than this if it had
+ these options.
Build
-----
@@ -110,7 +107,7 @@ build that tree. You need `cmake` and of course a C++ compiler.
.. code-block:: console
- $ git clone https://github.com/llvm-project/llvm-project/
+ $ git clone https://github.com/llvm-project/llvm-project-20170507 llvm-project
$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS=lld -DCMAKE_INSTALL_PREFIX=/usr/local ../llvm-project/llvm
diff --git a/docs/windows_support.rst b/docs/windows_support.rst
index 56df45b32..6b06d29af 100644
--- a/docs/windows_support.rst
+++ b/docs/windows_support.rst
@@ -29,8 +29,6 @@ Development status
Driver
:good:`Mostly done`. Some exotic command line options that are not usually
used for application develompent, such as ``/DRIVER``, are not supported.
- Options for Windows 8 app store are not recognized too
- (e.g. ``/APPCONTAINER``).
Linking against DLL
:good:`Done`. LLD can read import libraries needed to link against DLL. Both
@@ -44,8 +42,7 @@ Creating DLL
:good:`Done`. LLD creates a DLL if ``/DLL`` option is given. Exported
functions can be specified either via command line (``/EXPORT``) or via
module-definition file (.def). Both export-by-name and export-by-ordinal are
- supported. LLD uses Microsoft ``lib.exe`` tool to create an import library
- file.
+ supported.
Windows resource files support
:good:`Done`. If an ``.res`` file is given, LLD converts the file to a COFF
diff --git a/include/lld/Driver/Driver.h b/include/lld/Common/Driver.h
index 4ba0994e8..b3fa47128 100644
--- a/include/lld/Driver/Driver.h
+++ b/include/lld/Common/Driver.h
@@ -1,4 +1,4 @@
-//===- lld/Driver/Driver.h - Linker Driver Emulator -----------------------===//
+//===- lld/Common/Driver.h - Linker Driver Emulator -----------------------===//
//
// The LLVM Linker
//
@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLD_DRIVER_DRIVER_H
-#define LLD_DRIVER_DRIVER_H
+#ifndef LLD_COMMON_DRIVER_H
+#define LLD_COMMON_DRIVER_H
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/raw_ostream.h"
@@ -19,6 +19,11 @@ bool link(llvm::ArrayRef<const char *> Args,
llvm::raw_ostream &Diag = llvm::errs());
}
+namespace mingw {
+bool link(llvm::ArrayRef<const char *> Args,
+ llvm::raw_ostream &Diag = llvm::errs());
+}
+
namespace elf {
bool link(llvm::ArrayRef<const char *> Args, bool CanExitEarly,
llvm::raw_ostream &Diag = llvm::errs());
diff --git a/include/lld/Core/LLVM.h b/include/lld/Common/LLVM.h
index ccf08859f..b5d0e2bff 100644
--- a/include/lld/Core/LLVM.h
+++ b/include/lld/Common/LLVM.h
@@ -12,8 +12,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLD_CORE_LLVM_H
-#define LLD_CORE_LLVM_H
+#ifndef LLD_COMMON_LLVM_H
+#define LLD_COMMON_LLVM_H
// This should be the only #include, force #includes of all the others on
// clients.
diff --git a/include/lld/Core/Reproduce.h b/include/lld/Common/Reproduce.h
index 6e1d36a54..9cc430e51 100644
--- a/include/lld/Core/Reproduce.h
+++ b/include/lld/Common/Reproduce.h
@@ -7,10 +7,10 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLD_CORE_REPRODUCE_H
-#define LLD_CORE_REPRODUCE_H
+#ifndef LLD_COMMON_REPRODUCE_H
+#define LLD_COMMON_REPRODUCE_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
diff --git a/include/lld/Core/TargetOptionsCommandFlags.h b/include/lld/Common/TargetOptionsCommandFlags.h
index 9ba99d94b..9c4ff7cea 100644
--- a/include/lld/Core/TargetOptionsCommandFlags.h
+++ b/include/lld/Common/TargetOptionsCommandFlags.h
@@ -11,10 +11,11 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/ADT/Optional.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Target/TargetOptions.h"
namespace lld {
llvm::TargetOptions InitTargetOptionsFromCodeGenFlags();
-llvm::CodeModel::Model GetCodeModelFromCMModel();
+llvm::Optional<llvm::CodeModel::Model> GetCodeModelFromCMModel();
}
diff --git a/ELF/Threads.h b/include/lld/Common/Threads.h
index e01afd4d3..981946723 100644
--- a/ELF/Threads.h
+++ b/include/lld/Common/Threads.h
@@ -30,7 +30,7 @@
// - 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
+// For the cases such as the former, we can just use parallelForEach
// 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
@@ -56,33 +56,34 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLD_ELF_THREADS_H
-#define LLD_ELF_THREADS_H
-
-#include "Config.h"
+#ifndef LLD_COMMON_THREADS_H
+#define LLD_COMMON_THREADS_H
#include "llvm/Support/Parallel.h"
#include <functional>
namespace lld {
-namespace elf {
-template <class IterTy, class FuncTy>
-void parallelForEach(IterTy Begin, IterTy End, FuncTy Fn) {
- if (Config->Threads)
- for_each(llvm::parallel::par, Begin, End, Fn);
+extern bool ThreadsEnabled;
+
+template <typename R, class FuncTy> void parallelForEach(R &&Range, FuncTy Fn) {
+ if (ThreadsEnabled)
+ for_each(llvm::parallel::par, std::begin(Range), std::end(Range), Fn);
else
- for_each(llvm::parallel::seq, Begin, End, Fn);
+ for_each(llvm::parallel::seq, std::begin(Range), std::end(Range), Fn);
}
inline void parallelForEachN(size_t Begin, size_t End,
std::function<void(size_t)> Fn) {
- if (Config->Threads)
+ if (ThreadsEnabled)
for_each_n(llvm::parallel::par, Begin, End, Fn);
else
for_each_n(llvm::parallel::seq, Begin, End, Fn);
}
-}
-}
+
+void runBackground(std::function<void()> Fn);
+void waitForBackgroundThreads();
+
+} // namespace lld
#endif
diff --git a/include/lld/Config/Version.h b/include/lld/Common/Version.h
index 1cec3cc76..93de77df5 100644
--- a/include/lld/Config/Version.h
+++ b/include/lld/Common/Version.h
@@ -1,4 +1,4 @@
-//===- lld/Config/Version.h - LLD Version Number ----------------*- C++ -*-===//
+//===- lld/Common/Version.h - LLD Version Number ----------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -14,7 +14,7 @@
#ifndef LLD_VERSION_H
#define LLD_VERSION_H
-#include "lld/Config/Version.inc"
+#include "lld/Common/Version.inc"
#include "llvm/ADT/StringRef.h"
namespace lld {
diff --git a/include/lld/Config/Version.inc.in b/include/lld/Common/Version.inc.in
index 2789a5c46..2789a5c46 100644
--- a/include/lld/Config/Version.inc.in
+++ b/include/lld/Common/Version.inc.in
diff --git a/include/lld/Core/Atom.h b/include/lld/Core/Atom.h
index 156a5d4a7..149c3d5ee 100644
--- a/include/lld/Core/Atom.h
+++ b/include/lld/Core/Atom.h
@@ -10,7 +10,7 @@
#ifndef LLD_CORE_ATOM_H
#define LLD_CORE_ATOM_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/ADT/StringRef.h"
namespace lld {
diff --git a/include/lld/Core/DefinedAtom.h b/include/lld/Core/DefinedAtom.h
index 7f623d2ea..6229d67e2 100644
--- a/include/lld/Core/DefinedAtom.h
+++ b/include/lld/Core/DefinedAtom.h
@@ -10,9 +10,9 @@
#ifndef LLD_CORE_DEFINED_ATOM_H
#define LLD_CORE_DEFINED_ATOM_H
+#include "lld/Common/LLVM.h"
#include "lld/Core/Atom.h"
#include "lld/Core/Reference.h"
-#include "lld/Core/LLVM.h"
#include "llvm/Support/ErrorHandling.h"
namespace lld {
diff --git a/include/lld/Core/Error.h b/include/lld/Core/Error.h
index b0bf73b1c..36a367249 100644
--- a/include/lld/Core/Error.h
+++ b/include/lld/Core/Error.h
@@ -14,7 +14,7 @@
#ifndef LLD_CORE_ERROR_H
#define LLD_CORE_ERROR_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Error.h"
#include <system_error>
diff --git a/include/lld/Core/PassManager.h b/include/lld/Core/PassManager.h
index 09b417a29..2ea65ae13 100644
--- a/include/lld/Core/PassManager.h
+++ b/include/lld/Core/PassManager.h
@@ -10,7 +10,7 @@
#ifndef LLD_CORE_PASS_MANAGER_H
#define LLD_CORE_PASS_MANAGER_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/Pass.h"
#include "llvm/Support/Error.h"
#include <memory>
diff --git a/include/lld/Core/Reader.h b/include/lld/Core/Reader.h
index 32d04249f..c7baf86af 100644
--- a/include/lld/Core/Reader.h
+++ b/include/lld/Core/Reader.h
@@ -10,7 +10,7 @@
#ifndef LLD_CORE_READER_H
#define LLD_CORE_READER_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/Reference.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/Magic.h"
diff --git a/include/lld/Core/SymbolTable.h b/include/lld/Core/SymbolTable.h
index ba4951e5b..9c39a6ed5 100644
--- a/include/lld/Core/SymbolTable.h
+++ b/include/lld/Core/SymbolTable.h
@@ -10,7 +10,7 @@
#ifndef LLD_CORE_SYMBOL_TABLE_H
#define LLD_CORE_SYMBOL_TABLE_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/StringExtras.h"
#include <cstring>
diff --git a/include/lld/Core/Writer.h b/include/lld/Core/Writer.h
index 216f93491..1f0ca4cda 100644
--- a/include/lld/Core/Writer.h
+++ b/include/lld/Core/Writer.h
@@ -10,7 +10,7 @@
#ifndef LLD_CORE_WRITER_H
#define LLD_CORE_WRITER_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/Support/Error.h"
#include <memory>
#include <vector>
diff --git a/include/lld/ReaderWriter/YamlContext.h b/include/lld/ReaderWriter/YamlContext.h
index b26161a15..a44ec1d0d 100644
--- a/include/lld/ReaderWriter/YamlContext.h
+++ b/include/lld/ReaderWriter/YamlContext.h
@@ -10,7 +10,7 @@
#ifndef LLD_READER_WRITER_YAML_CONTEXT_H
#define LLD_READER_WRITER_YAML_CONTEXT_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include <functional>
#include <memory>
#include <vector>
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 699f5e93f..8884efcfe 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -1,4 +1,3 @@
-add_subdirectory(Config)
add_subdirectory(Core)
add_subdirectory(Driver)
add_subdirectory(ReaderWriter)
diff --git a/lib/Config/CMakeLists.txt b/lib/Config/CMakeLists.txt
deleted file mode 100644
index 3e142b66f..000000000
--- a/lib/Config/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-add_lld_library(lldConfig
- Version.cpp
-
- ADDITIONAL_HEADER_DIRS
- ${LLD_INCLUDE_DIR}/lld/Config
-
- LINK_COMPONENTS
- Support
- )
diff --git a/lib/Core/CMakeLists.txt b/lib/Core/CMakeLists.txt
index 85046b93f..2d4d9ded0 100644
--- a/lib/Core/CMakeLists.txt
+++ b/lib/Core/CMakeLists.txt
@@ -8,10 +8,8 @@ add_lld_library(lldCore
File.cpp
LinkingContext.cpp
Reader.cpp
- Reproduce.cpp
Resolver.cpp
SymbolTable.cpp
- TargetOptionsCommandFlags.cpp
Writer.cpp
ADDITIONAL_HEADER_DIRS
diff --git a/lib/Core/Resolver.cpp b/lib/Core/Resolver.cpp
index e7cfaaac7..9c51c6cdb 100644
--- a/lib/Core/Resolver.cpp
+++ b/lib/Core/Resolver.cpp
@@ -7,13 +7,13 @@
//
//===----------------------------------------------------------------------===//
-#include "lld/Core/Atom.h"
+#include "lld/Core/Resolver.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/ArchiveLibraryFile.h"
+#include "lld/Core/Atom.h"
#include "lld/Core/File.h"
#include "lld/Core/Instrumentation.h"
-#include "lld/Core/LLVM.h"
#include "lld/Core/LinkingContext.h"
-#include "lld/Core/Resolver.h"
#include "lld/Core/SharedLibraryFile.h"
#include "lld/Core/SymbolTable.h"
#include "lld/Core/UndefinedAtom.h"
diff --git a/lib/Core/SymbolTable.cpp b/lib/Core/SymbolTable.cpp
index 583c65acb..51ae8d171 100644
--- a/lib/Core/SymbolTable.cpp
+++ b/lib/Core/SymbolTable.cpp
@@ -8,11 +8,11 @@
//===----------------------------------------------------------------------===//
#include "lld/Core/SymbolTable.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/AbsoluteAtom.h"
#include "lld/Core/Atom.h"
#include "lld/Core/DefinedAtom.h"
#include "lld/Core/File.h"
-#include "lld/Core/LLVM.h"
#include "lld/Core/LinkingContext.h"
#include "lld/Core/Resolver.h"
#include "lld/Core/SharedLibraryAtom.h"
diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt
index be7587286..097a8177e 100644
--- a/lib/Driver/CMakeLists.txt
+++ b/lib/Driver/CMakeLists.txt
@@ -9,14 +9,12 @@ add_lld_library(lldDriver
${LLD_INCLUDE_DIR}/lld/Driver
LINK_COMPONENTS
- Object
Option
Support
LINK_LIBS
- lldConfig
- lldMachO
lldCore
+ lldMachO
lldReaderWriter
lldYAML
)
diff --git a/lib/Driver/DarwinLdDriver.cpp b/lib/Driver/DarwinLdDriver.cpp
index c859c9802..a019e9c32 100644
--- a/lib/Driver/DarwinLdDriver.cpp
+++ b/lib/Driver/DarwinLdDriver.cpp
@@ -13,11 +13,11 @@
///
//===----------------------------------------------------------------------===//
+#include "lld/Common/LLVM.h"
#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/LinkingContext.h"
#include "lld/Core/Node.h"
#include "lld/Core/PassManager.h"
@@ -61,9 +61,9 @@ namespace {
// Create enum with OPT_xxx values for each option in DarwinLdOptions.td
enum {
OPT_INVALID = 0,
-#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
- HELP, META) \
- OPT_##ID,
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELP, META, VALUES) \
+ OPT_##ID,
#include "DarwinLdOptions.inc"
#undef OPTION
};
@@ -74,11 +74,13 @@ enum {
#undef PREFIX
// Create table mapping all options defined in DarwinLdOptions.td
-static const llvm::opt::OptTable::Info infoTable[] = {
-#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
- HELPTEXT, METAVAR) \
- { PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
- PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS },
+static const llvm::opt::OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ {PREFIX, NAME, HELPTEXT, \
+ METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
+ PARAM, FLAGS, OPT_##GROUP, \
+ OPT_##ALIAS, ALIASARGS, VALUES},
#include "DarwinLdOptions.inc"
#undef OPTION
};
@@ -86,7 +88,7 @@ static const llvm::opt::OptTable::Info infoTable[] = {
// Create OptTable class for parsing actual command line arguments
class DarwinLdOptTable : public llvm::opt::OptTable {
public:
- DarwinLdOptTable() : OptTable(infoTable) {}
+ DarwinLdOptTable() : OptTable(InfoTable) {}
};
static std::vector<std::unique_ptr<File>>
diff --git a/lib/ReaderWriter/CMakeLists.txt b/lib/ReaderWriter/CMakeLists.txt
index 8751d569b..bedb836d2 100644
--- a/lib/ReaderWriter/CMakeLists.txt
+++ b/lib/ReaderWriter/CMakeLists.txt
@@ -17,5 +17,4 @@ add_lld_library(lldReaderWriter
LINK_LIBS
lldCore
- lldYAML
)
diff --git a/lib/ReaderWriter/FileArchive.cpp b/lib/ReaderWriter/FileArchive.cpp
index 762d0871d..04c0bee76 100644
--- a/lib/ReaderWriter/FileArchive.cpp
+++ b/lib/ReaderWriter/FileArchive.cpp
@@ -7,9 +7,9 @@
//
//===----------------------------------------------------------------------===//
+#include "lld/Common/LLVM.h"
#include "lld/Core/ArchiveLibraryFile.h"
#include "lld/Core/File.h"
-#include "lld/Core/LLVM.h"
#include "lld/Core/Reader.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
diff --git a/lib/ReaderWriter/MachO/ArchHandler.h b/lib/ReaderWriter/MachO/ArchHandler.h
index 70a63bd10..80840b561 100644
--- a/lib/ReaderWriter/MachO/ArchHandler.h
+++ b/lib/ReaderWriter/MachO/ArchHandler.h
@@ -13,7 +13,7 @@
#include "Atoms.h"
#include "File.h"
#include "MachONormalizedFile.h"
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/Error.h"
#include "lld/Core/Reference.h"
#include "lld/Core/Simple.h"
@@ -112,6 +112,10 @@ public:
/// info in final executables.
virtual bool isLazyPointer(const Reference &);
+ /// Reference from an __stub_helper entry to the required offset of the
+ /// lazy bind commands.
+ virtual Reference::KindValue lazyImmediateLocationKind() = 0;
+
/// Returns true if the specified relocation is paired to the next relocation.
virtual bool isPairedReloc(const normalized::Relocation &) = 0;
diff --git a/lib/ReaderWriter/MachO/ArchHandler_arm.cpp b/lib/ReaderWriter/MachO/ArchHandler_arm.cpp
index 7d1544854..2f663c660 100644
--- a/lib/ReaderWriter/MachO/ArchHandler_arm.cpp
+++ b/lib/ReaderWriter/MachO/ArchHandler_arm.cpp
@@ -67,6 +67,10 @@ public:
return invalid;
}
+ Reference::KindValue lazyImmediateLocationKind() override {
+ return lazyImmediateLocation;
+ }
+
Reference::KindValue pointerKind() override {
return invalid;
}
diff --git a/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp b/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp
index 10360b5c6..b9c815c5a 100644
--- a/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp
+++ b/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp
@@ -127,6 +127,10 @@ public:
return pointer64;
}
+ Reference::KindValue lazyImmediateLocationKind() override {
+ return lazyImmediateLocation;
+ }
+
uint32_t dwarfCompactUnwindType() override {
return 0x03000000;
}
diff --git a/lib/ReaderWriter/MachO/ArchHandler_x86.cpp b/lib/ReaderWriter/MachO/ArchHandler_x86.cpp
index 2272bff65..a2c680927 100644
--- a/lib/ReaderWriter/MachO/ArchHandler_x86.cpp
+++ b/lib/ReaderWriter/MachO/ArchHandler_x86.cpp
@@ -70,6 +70,10 @@ public:
return delta32;
}
+ Reference::KindValue lazyImmediateLocationKind() override {
+ return lazyImmediateLocation;
+ }
+
Reference::KindValue unwindRefToEhFrameKind() override {
return invalid;
}
diff --git a/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp b/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
index d687ca5de..aee9959ca 100644
--- a/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
+++ b/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
@@ -116,6 +116,10 @@ public:
return unwindFDEToFunction;
}
+ Reference::KindValue lazyImmediateLocationKind() override {
+ return lazyImmediateLocation;
+ }
+
Reference::KindValue unwindRefToEhFrameKind() override {
return unwindInfoToEhFrame;
}
diff --git a/lib/ReaderWriter/MachO/CMakeLists.txt b/lib/ReaderWriter/MachO/CMakeLists.txt
index 5a96d87f1..f2fc34772 100644
--- a/lib/ReaderWriter/MachO/CMakeLists.txt
+++ b/lib/ReaderWriter/MachO/CMakeLists.txt
@@ -21,9 +21,9 @@ add_lld_library(lldMachO
LINK_COMPONENTS
DebugInfoDWARF
+ Demangle
Object
Support
- Demangle
LINK_LIBS
lldCore
diff --git a/lib/ReaderWriter/MachO/CompactUnwindPass.cpp b/lib/ReaderWriter/MachO/CompactUnwindPass.cpp
index 49d518456..1e2104092 100644
--- a/lib/ReaderWriter/MachO/CompactUnwindPass.cpp
+++ b/lib/ReaderWriter/MachO/CompactUnwindPass.cpp
@@ -17,9 +17,9 @@
#include "File.h"
#include "MachONormalizedFileBinaryUtils.h"
#include "MachOPasses.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/DefinedAtom.h"
#include "lld/Core/File.h"
-#include "lld/Core/LLVM.h"
#include "lld/Core/Reference.h"
#include "lld/Core/Simple.h"
#include "llvm/ADT/DenseMap.h"
diff --git a/lib/ReaderWriter/MachO/GOTPass.cpp b/lib/ReaderWriter/MachO/GOTPass.cpp
index 8458a1c79..49e6f88d4 100644
--- a/lib/ReaderWriter/MachO/GOTPass.cpp
+++ b/lib/ReaderWriter/MachO/GOTPass.cpp
@@ -35,9 +35,9 @@
#include "ArchHandler.h"
#include "File.h"
#include "MachOPasses.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/DefinedAtom.h"
#include "lld/Core/File.h"
-#include "lld/Core/LLVM.h"
#include "lld/Core/Reference.h"
#include "lld/Core/Simple.h"
#include "llvm/ADT/DenseMap.h"
diff --git a/lib/ReaderWriter/MachO/MachOLinkingContext.cpp b/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
index 7e7b559b1..4ef7a62a8 100644
--- a/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
+++ b/lib/ReaderWriter/MachO/MachOLinkingContext.cpp
@@ -14,11 +14,11 @@
#include "MachONormalizedFile.h"
#include "MachOPasses.h"
#include "SectCreateFile.h"
+#include "lld/Common/Driver.h"
#include "lld/Core/ArchiveLibraryFile.h"
#include "lld/Core/PassManager.h"
#include "lld/Core/Reader.h"
#include "lld/Core/Writer.h"
-#include "lld/Driver/Driver.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Triple.h"
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFile.h b/lib/ReaderWriter/MachO/MachONormalizedFile.h
index 31b24dfd1..d58859425 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFile.h
+++ b/lib/ReaderWriter/MachO/MachONormalizedFile.h
@@ -43,8 +43,8 @@
#define LLD_READER_WRITER_MACHO_NORMALIZE_FILE_H
#include "DebugInfo.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/Error.h"
-#include "lld/Core/LLVM.h"
#include "lld/ReaderWriter/MachOLinkingContext.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
index edbe576f0..7c2e833c0 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
@@ -24,8 +24,8 @@
#include "ArchHandler.h"
#include "MachONormalizedFile.h"
#include "MachONormalizedFileBinaryUtils.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/Error.h"
-#include "lld/Core/LLVM.h"
#include "lld/Core/SharedLibraryFile.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
@@ -508,10 +508,11 @@ readBinary(std::unique_ptr<MemoryBuffer> &mb,
if (dyldInfo) {
// If any exports, extract and add to normalized exportInfo vector.
if (dyldInfo->export_size) {
- const uint8_t *trieStart = reinterpret_cast<const uint8_t*>(start +
- dyldInfo->export_off);
- ArrayRef<uint8_t> trie(trieStart, dyldInfo->export_size);
- for (const ExportEntry &trieExport : MachOObjectFile::exports(trie)) {
+ const uint8_t *trieStart = reinterpret_cast<const uint8_t *>(
+ start + read32(&dyldInfo->export_off, isBig));
+ ArrayRef<uint8_t> trie(trieStart, read32(&dyldInfo->export_size, isBig));
+ Error Err = Error::success();
+ for (const ExportEntry &trieExport : MachOObjectFile::exports(Err, trie)) {
Export normExport;
normExport.name = trieExport.name().copy(f->ownedAllocations);
normExport.offset = trieExport.address();
@@ -522,6 +523,8 @@ readBinary(std::unique_ptr<MemoryBuffer> &mb,
normExport.otherName = trieExport.otherName().copy(f->ownedAllocations);
f->exportInfo.push_back(normExport);
}
+ if (Err)
+ return std::move(Err);
}
}
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h
index b38f70592..407bd9b97 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h
@@ -11,8 +11,8 @@
#define LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H
#include "MachONormalizedFile.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/Error.h"
-#include "lld/Core/LLVM.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/Support/Casting.h"
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp
index bac41d2a5..ac7b61653 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp
@@ -23,8 +23,8 @@
#include "MachONormalizedFile.h"
#include "MachONormalizedFileBinaryUtils.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/Error.h"
-#include "lld/Core/LLVM.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
index e58e3d2e7..e93ca86c3 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
@@ -24,8 +24,8 @@
#include "DebugInfo.h"
#include "MachONormalizedFile.h"
#include "MachONormalizedFileBinaryUtils.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/Error.h"
-#include "lld/Core/LLVM.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/BinaryFormat/MachO.h"
@@ -172,6 +172,8 @@ private:
SymbolScope &symbolScope);
void appendSection(SectionInfo *si, NormalizedFile &file);
uint32_t sectionIndexForAtom(const Atom *atom);
+ void fixLazyReferenceImm(const DefinedAtom *atom, uint32_t offset,
+ NormalizedFile &file);
typedef llvm::DenseMap<const Atom*, uint32_t> AtomToIndex;
struct AtomAndIndex { const Atom *atom; uint32_t index; SymbolScope scope; };
@@ -1423,6 +1425,8 @@ void Util::addRebaseAndBindingInfo(const lld::File &atomFile,
uint8_t segmentIndex;
uint64_t segmentStartAddr;
+ uint32_t offsetInBindInfo = 0;
+
for (SectionInfo *sect : _sectionInfos) {
segIndexForSection(sect, segmentIndex, segmentStartAddr);
for (const AtomInfo &info : sect->atomsAndOffsets) {
@@ -1467,6 +1471,59 @@ void Util::addRebaseAndBindingInfo(const lld::File &atomFile,
bind.symbolName = targ->name();
bind.addend = ref->addend();
nFile.lazyBindingInfo.push_back(bind);
+
+ // Now that we know the segmentOffset and the ordinal attribute,
+ // we can fix the helper's code
+
+ fixLazyReferenceImm(atom, offsetInBindInfo, nFile);
+
+ // 5 bytes for opcodes + variable sizes (target name + \0 and offset
+ // encode's size)
+ offsetInBindInfo +=
+ 6 + targ->name().size() + llvm::getULEB128Size(bind.segOffset);
+ if (bind.ordinal > BIND_IMMEDIATE_MASK)
+ offsetInBindInfo += llvm::getULEB128Size(bind.ordinal);
+ }
+ }
+ }
+ }
+}
+
+void Util::fixLazyReferenceImm(const DefinedAtom *atom, uint32_t offset,
+ NormalizedFile &file) {
+ for (const auto &ref : *atom) {
+ const DefinedAtom *da = dyn_cast<DefinedAtom>(ref->target());
+ if (da == nullptr)
+ return;
+
+ const Reference *helperRef = nullptr;
+ for (const Reference *hr : *da) {
+ if (hr->kindValue() == _archHandler.lazyImmediateLocationKind()) {
+ helperRef = hr;
+ break;
+ }
+ }
+ if (helperRef == nullptr)
+ continue;
+
+ // TODO: maybe get the fixed atom content from _archHandler ?
+ for (SectionInfo *sectInfo : _sectionInfos) {
+ for (const AtomInfo &atomInfo : sectInfo->atomsAndOffsets) {
+ if (atomInfo.atom == helperRef->target()) {
+ auto sectionContent =
+ file.sections[sectInfo->normalizedSectionIndex].content;
+ uint8_t *rawb =
+ file.ownedAllocations.Allocate<uint8_t>(sectionContent.size());
+ llvm::MutableArrayRef<uint8_t> newContent{rawb,
+ sectionContent.size()};
+ std::copy(sectionContent.begin(), sectionContent.end(),
+ newContent.begin());
+ llvm::support::ulittle32_t *loc =
+ reinterpret_cast<llvm::support::ulittle32_t *>(
+ &newContent[atomInfo.offsetInSection +
+ helperRef->offsetInAtom()]);
+ *loc = offset;
+ file.sections[sectInfo->normalizedSectionIndex].content = newContent;
}
}
}
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
index 18fb71f16..3b07a40f9 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
@@ -25,8 +25,8 @@
#include "File.h"
#include "MachONormalizedFile.h"
#include "MachONormalizedFileBinaryUtils.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/Error.h"
-#include "lld/Core/LLVM.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
@@ -906,6 +906,7 @@ readCompUnit(const NormalizedFile &normalizedFile,
abbrevData.getU8(&abbrevOffset);
uint32_t name;
llvm::dwarf::Form form;
+ llvm::DWARFFormParams formParams = {version, addrSize, Format};
TranslationUnitSource tu;
while ((name = abbrevData.getULEB128(&abbrevOffset)) |
(form = static_cast<llvm::dwarf::Form>(
@@ -929,8 +930,7 @@ readCompUnit(const NormalizedFile &normalizedFile,
break;
}
default:
- llvm::DWARFFormValue::skipValue(form, infoData, &offset, version,
- addrSize, Format);
+ llvm::DWARFFormValue::skipValue(form, infoData, &offset, formParams);
}
}
return tu;
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp
index fe67fc88c..2cce18fd6 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp
@@ -16,8 +16,8 @@
/// +------------+ +------+
#include "MachONormalizedFile.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/Error.h"
-#include "lld/Core/LLVM.h"
#include "lld/ReaderWriter/YamlContext.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
@@ -44,7 +44,6 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(DependentDylib)
LLVM_YAML_IS_SEQUENCE_VECTOR(RebaseLocation)
LLVM_YAML_IS_SEQUENCE_VECTOR(BindLocation)
LLVM_YAML_IS_SEQUENCE_VECTOR(Export)
-LLVM_YAML_IS_SEQUENCE_VECTOR(StringRef)
LLVM_YAML_IS_SEQUENCE_VECTOR(DataInCode)
diff --git a/lib/ReaderWriter/MachO/ObjCPass.cpp b/lib/ReaderWriter/MachO/ObjCPass.cpp
index 4712d8ca9..23c71e0f5 100644
--- a/lib/ReaderWriter/MachO/ObjCPass.cpp
+++ b/lib/ReaderWriter/MachO/ObjCPass.cpp
@@ -11,10 +11,11 @@
#include "ArchHandler.h"
#include "File.h"
+#include "MachONormalizedFileBinaryUtils.h"
#include "MachOPasses.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/DefinedAtom.h"
#include "lld/Core/File.h"
-#include "lld/Core/LLVM.h"
#include "lld/Core/Reference.h"
#include "lld/Core/Simple.h"
#include "lld/ReaderWriter/MachOLinkingContext.h"
@@ -29,7 +30,7 @@ namespace mach_o {
///
class ObjCImageInfoAtom : public SimpleDefinedAtom {
public:
- ObjCImageInfoAtom(const File &file,
+ ObjCImageInfoAtom(const File &file, bool isBig,
MachOLinkingContext::ObjCConstraint objCConstraint,
uint32_t swiftVersion)
: SimpleDefinedAtom(file) {
@@ -54,6 +55,8 @@ public:
}
Data.info.flags |= (swiftVersion << 8);
+
+ normalized::write32(Data.bytes + 4, Data.info.flags, isBig);
}
~ObjCImageInfoAtom() override = default;
@@ -109,7 +112,8 @@ public:
private:
const DefinedAtom* getImageInfo() {
- return new (_file.allocator()) ObjCImageInfoAtom(_file,
+ bool IsBig = MachOLinkingContext::isBigEndian(_ctx.arch());
+ return new (_file.allocator()) ObjCImageInfoAtom(_file, IsBig,
_ctx.objcConstraint(),
_ctx.swiftVersion());
}
diff --git a/lib/ReaderWriter/MachO/ShimPass.cpp b/lib/ReaderWriter/MachO/ShimPass.cpp
index ff559d70e..8a2d2e910 100644
--- a/lib/ReaderWriter/MachO/ShimPass.cpp
+++ b/lib/ReaderWriter/MachO/ShimPass.cpp
@@ -26,9 +26,9 @@
#include "ArchHandler.h"
#include "File.h"
#include "MachOPasses.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/DefinedAtom.h"
#include "lld/Core/File.h"
-#include "lld/Core/LLVM.h"
#include "lld/Core/Reference.h"
#include "lld/Core/Simple.h"
#include "lld/ReaderWriter/MachOLinkingContext.h"
diff --git a/lib/ReaderWriter/MachO/StubsPass.cpp b/lib/ReaderWriter/MachO/StubsPass.cpp
index 19e2bc592..04c586df3 100644
--- a/lib/ReaderWriter/MachO/StubsPass.cpp
+++ b/lib/ReaderWriter/MachO/StubsPass.cpp
@@ -17,9 +17,9 @@
#include "ArchHandler.h"
#include "File.h"
#include "MachOPasses.h"
+#include "lld/Common/LLVM.h"
#include "lld/Core/DefinedAtom.h"
#include "lld/Core/File.h"
-#include "lld/Core/LLVM.h"
#include "lld/Core/Reference.h"
#include "lld/Core/Simple.h"
#include "lld/ReaderWriter/MachOLinkingContext.h"
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 1dbd1b769..eb8475881 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -17,18 +17,23 @@ llvm_canonicalize_cmake_booleans(
HAVE_LIBZ)
configure_lit_site_cfg(
- ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
- ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg)
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py
+ MAIN_CONFIG
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py
+ )
configure_lit_site_cfg(
- ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in
- ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg
+ ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.py.in
+ ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg.py
+ MAIN_CONFIG
+ ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.cfg.py
)
set(LLD_TEST_DEPS lld)
if (NOT LLD_BUILT_STANDALONE)
list(APPEND LLD_TEST_DEPS
FileCheck count not llvm-ar llvm-as llvm-dis llvm-dwarfdump llvm-nm
- llc llvm-config llvm-objdump llvm-readobj yaml2obj obj2yaml
+ llc llvm-config llvm-objdump llvm-readelf llvm-readobj yaml2obj obj2yaml
llvm-mc llvm-lib llvm-pdbutil opt
)
endif()
diff --git a/test/COFF/Inputs/alpha.ll b/test/COFF/Inputs/alpha.ll
new file mode 100644
index 000000000..53fc36bee
--- /dev/null
+++ b/test/COFF/Inputs/alpha.ll
@@ -0,0 +1,9 @@
+define void @_DllMainCRTStartup() {
+entry:
+ ret void
+}
+
+define dllexport void @f() local_unnamed_addr {
+entry:
+ ret void
+}
diff --git a/test/COFF/Inputs/beta.ll b/test/COFF/Inputs/beta.ll
new file mode 100644
index 000000000..ff98618d8
--- /dev/null
+++ b/test/COFF/Inputs/beta.ll
@@ -0,0 +1,7 @@
+declare dllimport void @f() local_unnamed_addr
+
+define void @g() local_unnamed_addr {
+entry:
+ tail call void @f()
+ ret void
+}
diff --git a/test/COFF/Inputs/combined-resources-2.rc b/test/COFF/Inputs/combined-resources-2.rc
new file mode 100644
index 000000000..081b3a77b
--- /dev/null
+++ b/test/COFF/Inputs/combined-resources-2.rc
@@ -0,0 +1,36 @@
+#include "windows.h"
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+randomdat RCDATA
+{
+ "this is a random bit of data that means nothing\0",
+ 0x23a9,
+ 0x140e,
+ 194292,
+}
+
+LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
+randomdat RCDATA
+{
+ "zhe4 shi4 yi1ge4 sui2ji1 de shu4ju4, zhe4 yi4wei4zhe shen2me\0",
+ 0x23a9,
+ 0x140e,
+ 194292,
+}
+
+LANGUAGE LANG_GERMAN, SUBLANG_GERMAN_LUXEMBOURG
+randomdat RCDATA
+{
+ "Dies ist ein zufälliges Bit von Daten, die nichts bedeutet\0",
+ 0x23a9,
+ 0x140e,
+ 194292,
+}
+
+LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
+myaccelerators ACCELERATORS
+{
+ "^C", 999, VIRTKEY, ALT
+ "D", 1100, VIRTKEY, CONTROL, SHIFT
+ "^R", 444, ASCII, NOINVERT
+}
diff --git a/test/COFF/Inputs/combined-resources-2.res b/test/COFF/Inputs/combined-resources-2.res
new file mode 100644
index 000000000..31da6166d
--- /dev/null
+++ b/test/COFF/Inputs/combined-resources-2.res
Binary files differ
diff --git a/test/COFF/Inputs/combined-resources-cursor.bmp b/test/COFF/Inputs/combined-resources-cursor.bmp
new file mode 100644
index 000000000..ce513261b
--- /dev/null
+++ b/test/COFF/Inputs/combined-resources-cursor.bmp
Binary files differ
diff --git a/test/COFF/Inputs/combined-resources-okay.bmp b/test/COFF/Inputs/combined-resources-okay.bmp
new file mode 100644
index 000000000..e4005bf5e
--- /dev/null
+++ b/test/COFF/Inputs/combined-resources-okay.bmp
Binary files differ
diff --git a/test/COFF/Inputs/combined-resources.rc b/test/COFF/Inputs/combined-resources.rc
new file mode 100644
index 000000000..08bfb94c4
--- /dev/null
+++ b/test/COFF/Inputs/combined-resources.rc
@@ -0,0 +1,50 @@
+#include "windows.h"
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+myaccelerators ACCELERATORS
+{
+ "^C", 999, VIRTKEY, ALT
+ "D", 1100, VIRTKEY, CONTROL, SHIFT
+ "^R", 444, ASCII, NOINVERT
+}
+
+cursor BITMAP "combined-resources-cursor.bmp"
+okay BITMAP "combined-resources-okay.bmp"
+
+14432 MENU
+LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
+{
+ MENUITEM "yu", 100
+ MENUITEM "shala", 101
+ MENUITEM "kaoya", 102
+}
+
+testdialog DIALOG 10, 10, 200, 300
+STYLE WS_POPUP | WS_BORDER
+CAPTION "Test"
+{
+ CTEXT "Continue:", 1, 10, 10, 230, 14
+ PUSHBUTTON "&OK", 2, 66, 134, 161, 13
+}
+
+12 ACCELERATORS
+{
+ "X", 164, VIRTKEY, ALT
+ "H", 5678, VIRTKEY, CONTROL, SHIFT
+ "^R", 444, ASCII, NOINVERT
+}
+
+"eat" MENU
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS
+{
+ MENUITEM "fish", 100
+ MENUITEM "salad", 101
+ MENUITEM "duck", 102
+}
+
+
+myresource stringarray {
+ "this is a user defined resource\0",
+ "it contains many strings\0",
+}
diff --git a/test/COFF/Inputs/combined-resources.res b/test/COFF/Inputs/combined-resources.res
new file mode 100644
index 000000000..d422bb490
--- /dev/null
+++ b/test/COFF/Inputs/combined-resources.res
Binary files differ
diff --git a/test/COFF/Inputs/default.def b/test/COFF/Inputs/default.def
new file mode 100644
index 000000000..1d59beebb
--- /dev/null
+++ b/test/COFF/Inputs/default.def
@@ -0,0 +1,2 @@
+EXPORTS
+ f
diff --git a/test/COFF/Inputs/extension.def b/test/COFF/Inputs/extension.def
new file mode 100644
index 000000000..d93f0dc1e
--- /dev/null
+++ b/test/COFF/Inputs/extension.def
@@ -0,0 +1,3 @@
+LIBRARY library.ext
+EXPORTS
+ f
diff --git a/test/COFF/Inputs/gamma.ll b/test/COFF/Inputs/gamma.ll
new file mode 100644
index 000000000..1c78fff29
--- /dev/null
+++ b/test/COFF/Inputs/gamma.ll
@@ -0,0 +1,14 @@
+
+declare void @f() local_unnamed_addr
+
+define void @__imp_f() local_unnamed_addr {
+entry:
+ ret void
+}
+
+define void @mainCRTStartup() local_unnamed_addr {
+entry:
+ tail call void @f()
+ ret void
+}
+
diff --git a/test/COFF/Inputs/library-arm64.lib b/test/COFF/Inputs/library-arm64.lib
new file mode 100644
index 000000000..04e193dd1
--- /dev/null
+++ b/test/COFF/Inputs/library-arm64.lib
Binary files differ
diff --git a/test/COFF/Inputs/library2-arm64.lib b/test/COFF/Inputs/library2-arm64.lib
new file mode 100644
index 000000000..6fedf06f7
--- /dev/null
+++ b/test/COFF/Inputs/library2-arm64.lib
Binary files differ
diff --git a/test/COFF/Inputs/library2.def b/test/COFF/Inputs/library2.def
new file mode 100644
index 000000000..d1906010c
--- /dev/null
+++ b/test/COFF/Inputs/library2.def
@@ -0,0 +1,3 @@
+LIBRARY library2
+EXPORTS
+ function2
diff --git a/test/COFF/Inputs/lto-cache.ll b/test/COFF/Inputs/lto-cache.ll
new file mode 100644
index 000000000..acbee7be0
--- /dev/null
+++ b/test/COFF/Inputs/lto-cache.ll
@@ -0,0 +1,10 @@
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc"
+
+define i32 @main() {
+entry:
+ call void (...) @globalfunc()
+ ret i32 0
+}
+
+declare void @globalfunc(...)
diff --git a/test/COFF/Inputs/named.def b/test/COFF/Inputs/named.def
new file mode 100644
index 000000000..07c862218
--- /dev/null
+++ b/test/COFF/Inputs/named.def
@@ -0,0 +1,3 @@
+LIBRARY library
+EXPORTS
+ f
diff --git a/test/COFF/Inputs/object.s b/test/COFF/Inputs/object.s
new file mode 100644
index 000000000..b70599385
--- /dev/null
+++ b/test/COFF/Inputs/object.s
@@ -0,0 +1,13 @@
+
+ .text
+
+ .def f
+ .scl 2
+ .type 32
+ .endef
+ .global f
+f:
+ retq $0
+
+ .section .drectve,"rd"
+ .ascii " /EXPORT:f"
diff --git a/test/COFF/Inputs/pdb-diff-cl.pdb b/test/COFF/Inputs/pdb-diff-cl.pdb
new file mode 100644
index 000000000..ba5d8380e
--- /dev/null
+++ b/test/COFF/Inputs/pdb-diff-cl.pdb
Binary files differ
diff --git a/test/COFF/Inputs/pdb-diff.cpp b/test/COFF/Inputs/pdb-diff.cpp
new file mode 100644
index 000000000..7c5d13795
--- /dev/null
+++ b/test/COFF/Inputs/pdb-diff.cpp
@@ -0,0 +1,10 @@
+// Build with cl:
+// cl.exe /Z7 pdb-diff.cpp /link /debug /pdb:pdb-diff-cl.pdb
+// /nodefaultlib /entry:main
+// Build with lld (after running the above cl command):
+// lld-link.exe /debug /pdb:pdb-diff-lld.pdb /nodefaultlib
+// /entry:main pdb-diff.obj
+
+void *__purecall = 0;
+
+int main() { return 42; }
diff --git a/test/COFF/Inputs/pdb-diff.obj b/test/COFF/Inputs/pdb-diff.obj
new file mode 100644
index 000000000..a8948bd0c
--- /dev/null
+++ b/test/COFF/Inputs/pdb-diff.obj
Binary files differ
diff --git a/test/COFF/Inputs/pdb-global-gc.s b/test/COFF/Inputs/pdb-global-gc.s
new file mode 100644
index 000000000..4c931dca0
--- /dev/null
+++ b/test/COFF/Inputs/pdb-global-gc.s
@@ -0,0 +1,4 @@
+.section .data,"dw",one_only,__wc_mb_cur
+.global __wc_mb_cur
+__wc_mb_cur:
+.long 42
diff --git a/test/COFF/Inputs/pdb-globals.yaml b/test/COFF/Inputs/pdb-globals.yaml
new file mode 100644
index 000000000..98ef914d9
--- /dev/null
+++ b/test/COFF/Inputs/pdb-globals.yaml
@@ -0,0 +1,593 @@
+# // YAML Generated from the following source code:
+# // Compile with clang-cl /Z7 /GS- /c t.obj pdb-globals.cpp
+#
+# void *__purecall = 0;
+#
+# struct HelloPoint {
+# int X = 3;
+# int Y = 4;
+# int Z = 5;
+# };
+#
+# // S_LPROCREF
+# static int LocalFunc() { return 42; }
+#
+# // S_PROCREF
+# int GlobalFunc() { return 43; }
+#
+# // S_LDATA32
+# const int ConstantVar = 17;
+#
+# // S_GDATA32
+# const int *GlobalVar = &ConstantVar;
+#
+# // S_CONSTANT
+# constexpr int ConstexprVar = 18;
+#
+# // S_UDT
+# typedef HelloPoint HelloPointTypedef;
+#
+# int main(int argc, char **argv) {
+# HelloPointTypedef P;
+# int N = P.X + P.Y + P.Z;
+# N += LocalFunc() + GlobalFunc();
+# N += *GlobalVar;
+# N += ConstexprVar;
+# }
+
+
+--- !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: 16
+ SectionData: 5589E5B82B0000005DC3660F1F4400005589E583EC208B450C8B4D088D55F4894DEC89D18945E8E8000000008B4DF4034DF8034DFC894DF08945E4E8000000008945E0E80000000031C98B55E001C20355F08955F0A1000000008B000345F08945F08B45F083C0128945F089C883C4205DC366666666662E0F1F8400000000005589E5B82A0000005DC3
+ Relocations:
+ - VirtualAddress: 40
+ SymbolName: '??0HelloPoint@@QAE@XZ'
+ Type: IMAGE_REL_I386_REL32
+ - VirtualAddress: 60
+ SymbolName: '?LocalFunc@@YAHXZ'
+ Type: IMAGE_REL_I386_REL32
+ - VirtualAddress: 68
+ SymbolName: '?GlobalFunc@@YAHXZ'
+ Type: IMAGE_REL_I386_REL32
+ - VirtualAddress: 86
+ SymbolName: '?GlobalVar@@3PBHB'
+ Type: IMAGE_REL_I386_DIR32
+ - Name: .data
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 4
+ SectionData: '00000000'
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: _ConstantVar
+ Type: IMAGE_REL_I386_DIR32
+ - Name: .bss
+ Characteristics: [ IMAGE_SCN_CNT_UNINITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 4
+ SectionData: ''
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 5589E550894DFC8B4DFCC70103000000C7410404000000C741080500000089C883C4045DC3
+ - Name: .rdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '11000000'
+ - Name: .drectve
+ Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
+ Alignment: 1
+ SectionData: 202F44454641554C544C49423A6C6962636D742E6C6962202F44454641554C544C49423A6F6C646E616D65732E6C6962
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: 04000000F10000002F0000002D003C1101000000070006000000000000007017000000000000636C616E672076657273696F6E20362E302E30200000F100000036000000300047110000000000000000000000000A00000000000000000000000210000000000000000000476C6F62616C46756E630002004F110000F20000002000000000000000000000000A0000000000000001000000140000000000000010000000F1000000B60000002A00471100000000000000000000000062000000000000000000000006100000000000000000006D61696E000D003E1174000000010061726763001200451116000000080000001F000000000053000D003E11031000000100617267760012004511160000000C0000001F000000000053000A003E1107100000000050001200451116000000F4FFFFFF1F000000000053000A003E117400000000004E001200451116000000F0FFFFFF1F0000000000530002004F110000F200000050000000000000000000000062000000000000000700000044000000000000001E0000000F0000001F0000001C000000200000002800000021000000450000002200000052000000230000005B00000024000000F1000000350000002F0046110000000000000000000000000A00000000000000000000000F100000000000000000004C6F63616C46756E630002004F11000000F20000002000000000000000000000000A000000000000000100000014000000000000000D000000F10000004B00000017000D11030400000000000000005F5F7075726563616C6C0016000D1111100000000000000000476C6F62616C5661720018000C1110100000000000000000436F6E7374616E745661720000F10000002D000000180008110710000048656C6C6F506F696E745479706564656600110008110910000048656C6C6F506F696E7400000000F4000000080000000100000000000000F30000003800000000643A5C7372635C6C6C766D2D6D6F6E6F5C6C6C645C746573745C636F66665C696E707574735C7064622D676C6F62616C732E6370700000
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_COMPILE3
+ Compile3Sym:
+ Flags: [ ]
+ Machine: Pentium3
+ FrontendMajor: 6
+ FrontendMinor: 0
+ FrontendBuild: 0
+ FrontendQFE: 0
+ BackendMajor: 6000
+ BackendMinor: 0
+ BackendBuild: 0
+ BackendQFE: 0
+ Version: 'clang version 6.0.0 '
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 10
+ DbgStart: 0
+ DbgEnd: 0
+ FunctionType: 4098
+ Flags: [ ]
+ DisplayName: GlobalFunc
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 98
+ DbgStart: 0
+ DbgEnd: 0
+ FunctionType: 4102
+ Flags: [ ]
+ DisplayName: main
+ - Kind: S_LOCAL
+ LocalSym:
+ Type: 116
+ Flags: [ IsParameter ]
+ VarName: argc
+ - Kind: S_DEFRANGE_REGISTER_REL
+ DefRangeRegisterRelSym:
+ - Kind: S_LOCAL
+ LocalSym:
+ Type: 4099
+ Flags: [ IsParameter ]
+ VarName: argv
+ - Kind: S_DEFRANGE_REGISTER_REL
+ DefRangeRegisterRelSym:
+ - Kind: S_LOCAL
+ LocalSym:
+ Type: 4103
+ Flags: [ ]
+ VarName: P
+ - Kind: S_DEFRANGE_REGISTER_REL
+ DefRangeRegisterRelSym:
+ - Kind: S_LOCAL
+ LocalSym:
+ Type: 116
+ Flags: [ ]
+ VarName: N
+ - Kind: S_DEFRANGE_REGISTER_REL
+ DefRangeRegisterRelSym:
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - Kind: S_LPROC32_ID
+ ProcSym:
+ CodeSize: 10
+ DbgStart: 0
+ DbgEnd: 0
+ FunctionType: 4111
+ Flags: [ ]
+ DisplayName: LocalFunc
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - Kind: S_GDATA32
+ DataSym:
+ Type: 1027
+ DisplayName: __purecall
+ - Kind: S_GDATA32
+ DataSym:
+ Type: 4113
+ DisplayName: GlobalVar
+ - Kind: S_LDATA32
+ DataSym:
+ Type: 4112
+ DisplayName: ConstantVar
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4103
+ UDTName: HelloPointTypedef
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4105
+ UDTName: HelloPoint
+ - !FileChecksums
+ Checksums:
+ - FileName: 'd:\src\llvm-mono\lld\test\coff\inputs\pdb-globals.cpp'
+ Kind: None
+ Checksum: ''
+ - !StringTable
+ Strings:
+ - 'd:\src\llvm-mono\lld\test\coff\inputs\pdb-globals.cpp'
+ - ''
+ Relocations:
+ - VirtualAddress: 100
+ SymbolName: '?GlobalFunc@@YAHXZ'
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 104
+ SymbolName: '?GlobalFunc@@YAHXZ'
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 132
+ SymbolName: '?GlobalFunc@@YAHXZ'
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 136
+ SymbolName: '?GlobalFunc@@YAHXZ'
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 204
+ SymbolName: _main
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 208
+ SymbolName: _main
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 243
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 247
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 278
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 282
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 310
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 314
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 342
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 346
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 364
+ SymbolName: _main
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 368
+ SymbolName: _main
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 484
+ SymbolName: '?LocalFunc@@YAHXZ'
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 488
+ SymbolName: '?LocalFunc@@YAHXZ'
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 516
+ SymbolName: '?LocalFunc@@YAHXZ'
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 520
+ SymbolName: '?LocalFunc@@YAHXZ'
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 564
+ SymbolName: '?__purecall@@3PAXA'
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 568
+ SymbolName: '?__purecall@@3PAXA'
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 589
+ SymbolName: '?GlobalVar@@3PBHB'
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 593
+ SymbolName: '?GlobalVar@@3PBHB'
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 613
+ SymbolName: _ConstantVar
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 617
+ SymbolName: _ConstantVar
+ Type: IMAGE_REL_I386_SECTION
+ - Name: '.debug$T'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: 0400000006000112000000000E000810740000000000000000100000160001160000000001100000476C6F62616C46756E6300F10A000210700400000A8000000E0001120200000074000000031000000E0008107400000000000200041000001200011600000000051000006D61696E00F3F2F13200051500008002000000000000000000000000000048656C6C6F506F696E74002E3F415548656C6C6F506F696E74404000F2F1260003120D15030074000000000058000D15030074000000040059000D1503007400000008005A0032000515030000020810000000000000000000000C0048656C6C6F506F696E74002E3F415548656C6C6F506F696E74404000F2F13E00051600000000643A5C7372635C6C6C766D2D6D6F6E6F5C6C6C645C746573745C636F66665C696E707574735C7064622D676C6F62616C732E63707000F2F10E000616091000000A100000060000000A000210071000000A8000001A00091003000000071000000C1000000B000000001000000000000016000216071000000D10000048656C6C6F506F696E7400F11600011600000000011000004C6F63616C46756E6300F2F10A000110740000000100F2F10A000210101000000A800000
+ Types:
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 116
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4096
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4097
+ Name: GlobalFunc
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 1136
+ Attrs: 32778
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 116, 4099 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 116
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 2
+ ArgumentList: 4100
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4101
+ Name: main
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: HelloPoint
+ UniqueName: '.?AUHelloPoint@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 0
+ Name: X
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 4
+ Name: Y
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 8
+ Name: Z
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 3
+ Options: [ None, HasUniqueName ]
+ FieldList: 4104
+ Name: HelloPoint
+ UniqueName: '.?AUHelloPoint@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 12
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'd:\src\llvm-mono\lld\test\coff\inputs\pdb-globals.cpp'
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4105
+ SourceFile: 4106
+ LineNumber: 6
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4103
+ Attrs: 32778
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4103
+ ThisType: 4108
+ CallConv: ThisCall
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4096
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNC_ID
+ MemberFuncId:
+ ClassType: 4103
+ FunctionType: 4109
+ Name: HelloPoint
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4097
+ Name: LocalFunc
+ - Kind: LF_MODIFIER
+ Modifier:
+ ModifiedType: 116
+ Modifiers: [ None, Const ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4112
+ Attrs: 32778
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: 04000000F1000000650000003C0047110000000000000000000000002500000000000000000000000E1000000000000000000048656C6C6F506F696E743A3A48656C6C6F506F696E74000D003E110C100000010074686973001200451116000000FCFFFFFF0A00000000001B0002004F11000000F20000004000000000000000000000002500000000000000050000003400000000000000060000000A00000007000000100000000800000017000000090000001E00000006000000
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 37
+ DbgStart: 0
+ DbgEnd: 0
+ FunctionType: 4110
+ Flags: [ ]
+ DisplayName: 'HelloPoint::HelloPoint'
+ - Kind: S_LOCAL
+ LocalSym:
+ Type: 4108
+ Flags: [ IsParameter ]
+ VarName: this
+ - Kind: S_DEFRANGE_REGISTER_REL
+ DefRangeRegisterRelSym:
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ Relocations:
+ - VirtualAddress: 44
+ SymbolName: '??0HelloPoint@@QAE@XZ'
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 48
+ SymbolName: '??0HelloPoint@@QAE@XZ'
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 101
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 105
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 124
+ SymbolName: '??0HelloPoint@@QAE@XZ'
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 128
+ SymbolName: '??0HelloPoint@@QAE@XZ'
+ Type: IMAGE_REL_I386_SECTION
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 138
+ NumberOfRelocations: 4
+ NumberOfLinenumbers: 0
+ CheckSum: 3215092891
+ 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: 4
+ NumberOfRelocations: 1
+ 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: 4
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 3
+ - Name: .text
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 37
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 77530982
+ Number: 4
+ Selection: IMAGE_COMDAT_SELECT_ANY
+ - Name: '??0HelloPoint@@QAE@XZ'
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: .rdata
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 4
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 3903140090
+ Number: 5
+ - Name: .drectve
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 48
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 149686238
+ Number: 6
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 7
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 768
+ NumberOfRelocations: 26
+ NumberOfLinenumbers: 0
+ CheckSum: 2940884584
+ Number: 7
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 9
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 188
+ NumberOfRelocations: 6
+ NumberOfLinenumbers: 0
+ CheckSum: 1246640575
+ Number: 4
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+ - Name: '.debug$T'
+ Value: 0
+ SectionNumber: 8
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 452
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 2561906059
+ Number: 8
+ - Name: '@feat.00'
+ Value: 1
+ SectionNumber: -1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: '?GlobalFunc@@YAHXZ'
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: _main
+ Value: 16
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '?LocalFunc@@YAHXZ'
+ Value: 128
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: '?GlobalVar@@3PBHB'
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '?__purecall@@3PAXA'
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: _ConstantVar
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/test/COFF/Inputs/pdb-import-gc.lib b/test/COFF/Inputs/pdb-import-gc.lib
new file mode 100644
index 000000000..f4682eddb
--- /dev/null
+++ b/test/COFF/Inputs/pdb-import-gc.lib
Binary files differ
diff --git a/test/COFF/Inputs/pdb-scopes-a.yaml b/test/COFF/Inputs/pdb-scopes-a.yaml
new file mode 100644
index 000000000..e9f4484c7
--- /dev/null
+++ b/test/COFF/Inputs/pdb-scopes-a.yaml
@@ -0,0 +1,425 @@
+--- !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
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_OBJNAME
+ ObjNameSym:
+ Signature: 0
+ ObjectName: 'C:\src\llvm-project\build\a.obj'
+ - Kind: S_COMPILE3
+ Compile3Sym:
+ Flags: [ SecurityChecks, HotPatch ]
+ Machine: X64
+ FrontendMajor: 19
+ FrontendMinor: 0
+ FrontendBuild: 24215
+ FrontendQFE: 1
+ BackendMajor: 19
+ BackendMinor: 0
+ BackendBuild: 24215
+ BackendQFE: 1
+ Version: 'Microsoft (R) Optimizing Compiler'
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 5
+ DbgStart: 4
+ DbgEnd: 4
+ FunctionType: 4099
+ Flags: [ ]
+ DisplayName: g
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 0
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_REGREL32
+ RegRelativeSym:
+ Offset: 8
+ Type: 116
+ Register: RSP
+ VarName: x
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 5
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\a.c'
+ Lines:
+ - Offset: 0
+ LineStart: 1
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 58
+ DbgStart: 8
+ DbgEnd: 53
+ FunctionType: 4101
+ Flags: [ ]
+ DisplayName: main
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 56
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_REGREL32
+ RegRelativeSym:
+ Offset: 64
+ Type: 116
+ Register: RSP
+ VarName: argc
+ - Kind: S_BLOCK32
+ BlockSym:
+ CodeSize: 17
+ Offset: 15
+ BlockName: ''
+ - Kind: S_REGREL32
+ RegRelativeSym:
+ Offset: 32
+ Type: 116
+ Register: RSP
+ VarName: x
+ - Kind: S_END
+ ScopeEndSym:
+ - Kind: S_BLOCK32
+ BlockSym:
+ CodeSize: 17
+ Offset: 34
+ BlockName: ''
+ - Kind: S_REGREL32
+ RegRelativeSym:
+ Offset: 36
+ Type: 116
+ Register: RSP
+ VarName: y
+ - Kind: S_END
+ ScopeEndSym:
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 58
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\a.c'
+ Lines:
+ - Offset: 0
+ LineStart: 3
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 8
+ LineStart: 4
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 15
+ LineStart: 5
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 23
+ LineStart: 6
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 32
+ LineStart: 7
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 34
+ LineStart: 8
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 42
+ LineStart: 9
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 51
+ LineStart: 11
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !FileChecksums
+ Checksums:
+ - FileName: 'c:\src\llvm-project\build\a.c'
+ Kind: MD5
+ Checksum: 7FA72225C3F5630316383BD8BCC3EF72
+ - !StringTable
+ Strings:
+ - 'c:\src\llvm-project\build\a.c'
+ - !Symbols
+ Records:
+ - Kind: S_BUILDINFO
+ BuildInfoSym:
+ BuildId: 4110
+ Relocations:
+ - VirtualAddress: 152
+ SymbolName: g
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 156
+ SymbolName: g
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 220
+ SymbolName: g
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 224
+ SymbolName: g
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 292
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 296
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 369
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 373
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 412
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 416
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 452
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 456
+ 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
+ Types:
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 116 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 3
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4096
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4097
+ Attrs: 65548
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4097
+ Name: g
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 116
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4096
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4100
+ Name: main
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4097
+ Name: f
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\PROGRA~2\MICROS~1.0\VC\Bin\amd64\cl.exe'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: '-c -Z7 -MT -IC:\PROGRA~2\MICROS~1.0\VC\include -IC:\PROGRA~2\MICROS~1.0\VC\atlmfc\include -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\ucrt -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\shared -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\um'
+ - Kind: LF_SUBSTR_LIST
+ StringList:
+ StringIndices: [ 4105 ]
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 4106
+ String: ' -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\winrt -TC -X'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: a.c
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build\vc140.pdb'
+ - Kind: LF_BUILDINFO
+ BuildInfo:
+ ArgIndices: [ 4103, 4104, 4108, 4109, 4107 ]
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 894C2408C3CCCCCCCCCCCCCCCCCCCCCC894C24084883EC38837C2440007413C74424202A0000008B4C2420E800000000EB11C74424240D0000008B4C2424E80000000033C04883C438C3
+ Relocations:
+ - VirtualAddress: 44
+ SymbolName: f
+ Type: IMAGE_REL_AMD64_REL32
+ - VirtualAddress: 63
+ SymbolName: f
+ Type: IMAGE_REL_AMD64_REL32
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '0108010008620000'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: 000000003A00000000000000
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: '$LN5'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 4
+ SymbolName: '$LN5'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 8
+ SymbolName: '$unwind$main'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+symbols:
+ - 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: 628
+ NumberOfRelocations: 12
+ 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: 624
+ 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: 74
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 2120072435
+ Number: 0
+ - Name: g
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: f
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: main
+ Value: 16
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '$LN5'
+ Value: 16
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_LABEL
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 3137252093
+ Number: 0
+ - Name: '$unwind$main'
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 12
+ NumberOfRelocations: 3
+ NumberOfLinenumbers: 0
+ CheckSum: 336416693
+ Number: 0
+ - Name: '$pdata$main'
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/test/COFF/Inputs/pdb-scopes-b.yaml b/test/COFF/Inputs/pdb-scopes-b.yaml
new file mode 100644
index 000000000..2839bf7e3
--- /dev/null
+++ b/test/COFF/Inputs/pdb-scopes-b.yaml
@@ -0,0 +1,365 @@
+--- !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
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_OBJNAME
+ ObjNameSym:
+ Signature: 0
+ ObjectName: 'C:\src\llvm-project\build\b.obj'
+ - Kind: S_COMPILE3
+ Compile3Sym:
+ Flags: [ SecurityChecks, HotPatch ]
+ Machine: X64
+ FrontendMajor: 19
+ FrontendMinor: 0
+ FrontendBuild: 24215
+ FrontendQFE: 1
+ BackendMajor: 19
+ BackendMinor: 0
+ BackendBuild: 24215
+ BackendQFE: 1
+ Version: 'Microsoft (R) Optimizing Compiler'
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 62
+ DbgStart: 8
+ DbgEnd: 57
+ FunctionType: 4101
+ Flags: [ ]
+ DisplayName: f
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 56
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_REGREL32
+ RegRelativeSym:
+ Offset: 64
+ Type: 116
+ Register: RSP
+ VarName: x
+ - Kind: S_BLOCK32
+ BlockSym:
+ CodeSize: 20
+ Offset: 15
+ BlockName: ''
+ - Kind: S_REGREL32
+ RegRelativeSym:
+ Offset: 32
+ Type: 116
+ Register: RSP
+ VarName: y
+ - Kind: S_END
+ ScopeEndSym:
+ - Kind: S_BLOCK32
+ BlockSym:
+ CodeSize: 20
+ Offset: 37
+ BlockName: ''
+ - Kind: S_REGREL32
+ RegRelativeSym:
+ Offset: 36
+ Type: 116
+ Register: RSP
+ VarName: w
+ - Kind: S_END
+ ScopeEndSym:
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 62
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\b.c'
+ Lines:
+ - Offset: 0
+ LineStart: 2
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 8
+ LineStart: 3
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 15
+ LineStart: 4
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 26
+ LineStart: 5
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 35
+ LineStart: 6
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 37
+ LineStart: 7
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 48
+ LineStart: 8
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 57
+ LineStart: 10
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !FileChecksums
+ Checksums:
+ - FileName: 'c:\src\llvm-project\build\b.c'
+ Kind: MD5
+ Checksum: 8E8C92DB46478902EBEAEBFCFF15A6E0
+ - !StringTable
+ Strings:
+ - 'c:\src\llvm-project\build\b.c'
+ - !Symbols
+ Records:
+ - Kind: S_BUILDINFO
+ BuildInfoSym:
+ BuildId: 4110
+ Relocations:
+ - VirtualAddress: 152
+ SymbolName: f
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 156
+ SymbolName: f
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 223
+ SymbolName: f
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 227
+ SymbolName: f
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 266
+ SymbolName: f
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 270
+ SymbolName: f
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 308
+ SymbolName: f
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 312
+ SymbolName: f
+ Type: IMAGE_REL_AMD64_SECTION
+ - Name: '.debug$T'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ Types:
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 0 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 3
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4096
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4097
+ Attrs: 65548
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 116 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 3
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4099
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4100
+ Name: f
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4097
+ Name: g
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\PROGRA~2\MICROS~1.0\VC\Bin\amd64\cl.exe'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: '-c -Z7 -MT -IC:\PROGRA~2\MICROS~1.0\VC\include -IC:\PROGRA~2\MICROS~1.0\VC\atlmfc\include -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\ucrt -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\shared -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\um'
+ - Kind: LF_SUBSTR_LIST
+ StringList:
+ StringIndices: [ 4105 ]
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 4106
+ String: ' -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\winrt -TC -X'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: b.c
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build\vc140.pdb'
+ - Kind: LF_BUILDINFO
+ BuildInfo:
+ ArgIndices: [ 4103, 4104, 4108, 4109, 4107 ]
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 894C24084883EC38837C24400074168B44244083C003894424208B4C2420E800000000EB148B44244083C004894424248B4C2424E8000000004883C438C3
+ Relocations:
+ - VirtualAddress: 31
+ SymbolName: g
+ Type: IMAGE_REL_AMD64_REL32
+ - VirtualAddress: 53
+ SymbolName: g
+ Type: IMAGE_REL_AMD64_REL32
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '0108010008620000'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '000000003E00000000000000'
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: '$LN5'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 4
+ SymbolName: '$LN5'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 8
+ SymbolName: '$unwind$f'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+symbols:
+ - 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: 484
+ NumberOfRelocations: 8
+ 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: 616
+ 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: 62
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 3841032836
+ Number: 0
+ - Name: g
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: f
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '$LN5'
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_LABEL
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 3137252093
+ Number: 0
+ - Name: '$unwind$f'
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 12
+ NumberOfRelocations: 3
+ NumberOfLinenumbers: 0
+ CheckSum: 2420588879
+ Number: 0
+ - Name: '$pdata$f'
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/test/COFF/Inputs/pdb-type-server-simple-a.yaml b/test/COFF/Inputs/pdb-type-server-simple-a.yaml
new file mode 100644
index 000000000..78c681681
--- /dev/null
+++ b/test/COFF/Inputs/pdb-type-server-simple-a.yaml
@@ -0,0 +1,255 @@
+--- !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
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_OBJNAME
+ ObjNameSym:
+ Signature: 0
+ ObjectName: 'C:\src\llvm-project\build\a.obj'
+ - Kind: S_COMPILE3
+ Compile3Sym:
+ Flags: [ SecurityChecks, HotPatch ]
+ Machine: X64
+ FrontendMajor: 19
+ FrontendMinor: 0
+ FrontendBuild: 24215
+ FrontendQFE: 1
+ BackendMajor: 19
+ BackendMinor: 0
+ BackendBuild: 24215
+ BackendQFE: 1
+ Version: 'Microsoft (R) Optimizing Compiler'
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 27
+ DbgStart: 4
+ DbgEnd: 22
+ FunctionType: 4098
+ Flags: [ ]
+ DisplayName: main
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 56
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_REGREL32
+ RegRelativeSym:
+ Offset: 32
+ Type: 4102
+ Register: RSP
+ VarName: f
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 27
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\a.c'
+ Lines:
+ - Offset: 0
+ LineStart: 3
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 4
+ LineStart: 4
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 12
+ LineStart: 5
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 22
+ LineStart: 6
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !Symbols
+ Records:
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4102
+ UDTName: Foo
+ - !FileChecksums
+ Checksums:
+ - FileName: 'c:\src\llvm-project\build\a.c'
+ Kind: MD5
+ Checksum: BF69E7E933074E1B7ED1FE8FB395965B
+ - !StringTable
+ Strings:
+ - 'c:\src\llvm-project\build\a.c'
+ - !Symbols
+ Records:
+ - Kind: S_BUILDINFO
+ BuildInfoSym:
+ BuildId: 4107
+ Relocations:
+ - VirtualAddress: 152
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 156
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 224
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 228
+ 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
+ Types:
+ - Kind: LF_TYPESERVER2
+ TypeServer2:
+ Guid: '{41414141-4141-4141-4141-414141414141}'
+ Age: 1
+ Name: 'C:\src\llvm-project\build\ts.pdb'
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 4883EC38C74424202A000000488D4C2420E8000000004883C438C3
+ Relocations:
+ - VirtualAddress: 18
+ SymbolName: g
+ Type: IMAGE_REL_AMD64_REL32
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '0104010004620000'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: 000000001B00000000000000
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: '$LN3'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 4
+ SymbolName: '$LN3'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 8
+ SymbolName: '$unwind$main'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+symbols:
+ - 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: 388
+ 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: 64
+ 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: 27
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 1939996292
+ Number: 0
+ - Name: g
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: main
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '$LN3'
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_LABEL
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 931692337
+ Number: 0
+ - Name: '$unwind$main'
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 12
+ NumberOfRelocations: 3
+ NumberOfLinenumbers: 0
+ CheckSum: 567356797
+ Number: 0
+ - Name: '$pdata$main'
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/test/COFF/Inputs/pdb-type-server-simple-b.yaml b/test/COFF/Inputs/pdb-type-server-simple-b.yaml
new file mode 100644
index 000000000..56e97d530
--- /dev/null
+++ b/test/COFF/Inputs/pdb-type-server-simple-b.yaml
@@ -0,0 +1,173 @@
+--- !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
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_OBJNAME
+ ObjNameSym:
+ Signature: 0
+ ObjectName: 'C:\src\llvm-project\build\b.obj'
+ - Kind: S_COMPILE3
+ Compile3Sym:
+ Flags: [ SecurityChecks, HotPatch ]
+ Machine: X64
+ FrontendMajor: 19
+ FrontendMinor: 0
+ FrontendBuild: 24215
+ FrontendQFE: 1
+ BackendMajor: 19
+ BackendMinor: 0
+ BackendBuild: 24215
+ BackendQFE: 1
+ Version: 'Microsoft (R) Optimizing Compiler'
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 13
+ DbgStart: 5
+ DbgEnd: 12
+ FunctionType: 4099
+ Flags: [ ]
+ DisplayName: g
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 0
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_REGREL32
+ RegRelativeSym:
+ Offset: 8
+ Type: 4097
+ Register: RSP
+ VarName: p
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 13
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\b.c'
+ Lines:
+ - Offset: 0
+ LineStart: 2
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !Symbols
+ Records:
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4102
+ UDTName: Foo
+ - !FileChecksums
+ Checksums:
+ - FileName: 'c:\src\llvm-project\build\b.c'
+ Kind: MD5
+ Checksum: DDF8FD35CD67990C5D4147516BE10D0C
+ - !StringTable
+ Strings:
+ - 'c:\src\llvm-project\build\b.c'
+ - !Symbols
+ Records:
+ - Kind: S_BUILDINFO
+ BuildInfoSym:
+ BuildId: 4111
+ Relocations:
+ - VirtualAddress: 152
+ SymbolName: g
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 156
+ SymbolName: g
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 220
+ SymbolName: g
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 224
+ SymbolName: g
+ Type: IMAGE_REL_AMD64_SECTION
+ - Name: '.debug$T'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ Types:
+ - Kind: LF_TYPESERVER2
+ TypeServer2:
+ Guid: '{41414141-4141-4141-4141-414141414141}'
+ Age: 1
+ Name: 'C:\src\llvm-project\build\ts.pdb'
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 48894C2408488B4424088B00C3
+symbols:
+ - 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: 360
+ 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: 64
+ 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: 13
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 3246683207
+ Number: 0
+ - Name: g
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/Inputs/pdb-type-server-simple-ts.yaml b/test/COFF/Inputs/pdb-type-server-simple-ts.yaml
new file mode 100644
index 000000000..73496571a
--- /dev/null
+++ b/test/COFF/Inputs/pdb-type-server-simple-ts.yaml
@@ -0,0 +1,147 @@
+---
+MSF:
+ SuperBlock:
+ BlockSize: 4096
+ FreeBlockMap: 1
+ NumBlocks: 19
+ NumDirectoryBytes: 64
+ Unknown1: 0
+ BlockMapAddr: 17
+ NumDirectoryBlocks: 1
+ DirectoryBlocks: [ 16 ]
+ NumStreams: 0
+ FileSize: 77824
+PdbStream:
+ Age: 1
+ Guid: '{41414141-4141-4141-4141-414141414141}'
+ Signature: 1500053944
+ Features: [ VC140 ]
+ Version: VC70
+TpiStream:
+ Version: VC80
+ Records:
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: Foo
+ UniqueName: '.?AUFoo@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4096
+ Attrs: 65548
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4097 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 116
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4098
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4099
+ Attrs: 65548
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 0
+ Name: x
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 1
+ Options: [ None, HasUniqueName ]
+ FieldList: 4101
+ Name: Foo
+ UniqueName: '.?AUFoo@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 4
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 0 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 116
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4103
+IpiStream:
+ Version: VC80
+ Records:
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'c:\src\llvm-project\build\a.c'
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4102
+ SourceFile: 4096
+ LineNumber: 1
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4104
+ Name: main
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4099
+ Name: g
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\PROGRA~2\MICROS~1.0\VC\Bin\amd64\cl.exe'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: '-c -Zi -MT -IC:\PROGRA~2\MICROS~1.0\VC\include -IC:\PROGRA~2\MICROS~1.0\VC\atlmfc\include -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\ucrt -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\shared -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\um'
+ - Kind: LF_SUBSTR_LIST
+ StringList:
+ StringIndices: [ 4102 ]
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 4103
+ String: ' -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\winrt -TC -X'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: a.c
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build\ts.pdb'
+ - Kind: LF_BUILDINFO
+ BuildInfo:
+ ArgIndices: [ 4100, 4101, 4105, 4106, 4104 ]
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'c:\src\llvm-project\build\b.c'
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4102
+ SourceFile: 4108
+ LineNumber: 1
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: b.c
+ - Kind: LF_BUILDINFO
+ BuildInfo:
+ ArgIndices: [ 4100, 4101, 4110, 4106, 4104 ]
+...
diff --git a/test/COFF/Inputs/pdb1.yaml b/test/COFF/Inputs/pdb1.yaml
index 566f2da00..90905ae19 100644
--- a/test/COFF/Inputs/pdb1.yaml
+++ b/test/COFF/Inputs/pdb1.yaml
@@ -34,14 +34,10 @@ sections:
Records:
- Kind: S_GPROC32_ID
ProcSym:
- PtrParent: 0
- PtrEnd: 0
- PtrNext: 0
CodeSize: 14
DbgStart: 4
DbgEnd: 9
FunctionType: 4101
- Segment: 0
Flags: [ ]
DisplayName: main
- Kind: S_FRAMEPROC
diff --git a/test/COFF/Inputs/pdb_comdat_bar.yaml b/test/COFF/Inputs/pdb_comdat_bar.yaml
new file mode 100644
index 000000000..71a9535c5
--- /dev/null
+++ b/test/COFF/Inputs/pdb_comdat_bar.yaml
@@ -0,0 +1,440 @@
+--- !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
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_OBJNAME
+ ObjNameSym:
+ Signature: 0
+ ObjectName: 'C:\src\llvm-project\build\pdb_comdat_bar.obj'
+ - Kind: S_COMPILE3
+ Compile3Sym:
+ Flags: [ SecurityChecks, HotPatch ]
+ Machine: X64
+ FrontendMajor: 19
+ FrontendMinor: 0
+ FrontendBuild: 24215
+ FrontendQFE: 1
+ BackendMajor: 19
+ BackendMinor: 0
+ BackendBuild: 24215
+ BackendQFE: 1
+ Version: 'Microsoft (R) Optimizing Compiler'
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ PtrParent: 0
+ PtrEnd: 0
+ PtrNext: 0
+ CodeSize: 14
+ DbgStart: 4
+ DbgEnd: 9
+ FunctionType: 4102
+ Segment: 0
+ Flags: [ ]
+ DisplayName: bar
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 40
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 14
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\pdb_comdat_bar.c'
+ Lines:
+ - Offset: 0
+ LineStart: 3
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 4
+ LineStart: 4
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 9
+ LineStart: 5
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !Symbols
+ Records:
+ - Kind: S_GDATA32
+ DataSym:
+ Type: 116
+ DisplayName: global
+ - !FileChecksums
+ Checksums:
+ - FileName: 'c:\src\llvm-project\build\pdb_comdat_bar.c'
+ Kind: MD5
+ Checksum: 365279DB4FCBEDD721BBFC3B14A953C2
+ - FileName: 'c:\src\llvm-project\build\foo.h'
+ Kind: MD5
+ Checksum: D74D834EFAC3AE2B45E606A8320B1D5C
+ - !StringTable
+ Strings:
+ - 'c:\src\llvm-project\build\pdb_comdat_bar.c'
+ - 'c:\src\llvm-project\build\foo.h'
+ - !Symbols
+ Records:
+ - Kind: S_BUILDINFO
+ BuildInfoSym:
+ BuildId: 4110
+ Relocations:
+ - VirtualAddress: 168
+ SymbolName: bar
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 172
+ SymbolName: bar
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 224
+ SymbolName: bar
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 228
+ SymbolName: bar
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 288
+ SymbolName: global
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 292
+ SymbolName: global
+ Type: IMAGE_REL_AMD64_SECTION
+ - Name: '.debug$T'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ Types:
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 0 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 3
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4096
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4097
+ Attrs: 65548
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4097
+ Name: foo
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 3
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4101
+ Name: bar
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\PROGRA~2\MICROS~1.0\VC\Bin\amd64\cl.exe'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: '-c -Z7 -MT -IC:\PROGRA~2\MICROS~1.0\VC\include -IC:\PROGRA~2\MICROS~1.0\VC\atlmfc\include -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\ucrt -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\shared -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\um'
+ - Kind: LF_SUBSTR_LIST
+ StringList:
+ StringIndices: [ 4105 ]
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 4106
+ String: ' -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\winrt -TC -X'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: pdb_comdat_bar.c
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build\vc140.pdb'
+ - Kind: LF_BUILDINFO
+ BuildInfo:
+ ArgIndices: [ 4103, 4104, 4108, 4109, 4107 ]
+ - Name: .bss
+ Characteristics: [ IMAGE_SCN_CNT_UNINITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 4
+ SectionData: ''
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 4883EC28E8000000004883C428C3
+ Relocations:
+ - VirtualAddress: 5
+ SymbolName: foo
+ Type: IMAGE_REL_AMD64_REL32
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 8B0500000000FFC0890500000000C3
+ Relocations:
+ - VirtualAddress: 2
+ SymbolName: global
+ Type: IMAGE_REL_AMD64_REL32
+ - VirtualAddress: 10
+ SymbolName: global
+ Type: IMAGE_REL_AMD64_REL32
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ PtrParent: 0
+ PtrEnd: 0
+ PtrNext: 0
+ CodeSize: 15
+ DbgStart: 0
+ DbgEnd: 14
+ FunctionType: 4099
+ Segment: 0
+ Flags: [ ]
+ DisplayName: foo
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 0
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ MarkedInline, AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 15
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\foo.h'
+ Lines:
+ - Offset: 0
+ LineStart: 2
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 0
+ LineStart: 3
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 14
+ LineStart: 4
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ Relocations:
+ - VirtualAddress: 44
+ SymbolName: foo
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 48
+ SymbolName: foo
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 100
+ SymbolName: foo
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 104
+ SymbolName: foo
+ Type: IMAGE_REL_AMD64_SECTION
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '0104010004420000'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '000000000E00000000000000'
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: '$LN3'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 4
+ SymbolName: '$LN3'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 8
+ SymbolName: '$unwind$bar'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+symbols:
+ - 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: 460
+ NumberOfRelocations: 6
+ 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: 628
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: .bss
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 4
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: global
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 14
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 1682752513
+ Number: 0
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 15
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 1746394828
+ Number: 0
+ Selection: IMAGE_COMDAT_SELECT_ANY
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 7
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 148
+ NumberOfRelocations: 4
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 6
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+ - Name: foo
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: bar
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '$LN3'
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_LABEL
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 8
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 264583633
+ Number: 0
+ - Name: '$unwind$bar'
+ Value: 0
+ SectionNumber: 8
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 9
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 12
+ NumberOfRelocations: 3
+ NumberOfLinenumbers: 0
+ CheckSum: 361370162
+ Number: 0
+ - Name: '$pdata$bar'
+ Value: 0
+ SectionNumber: 9
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/test/COFF/Inputs/pdb_comdat_main.yaml b/test/COFF/Inputs/pdb_comdat_main.yaml
new file mode 100644
index 000000000..d9019d633
--- /dev/null
+++ b/test/COFF/Inputs/pdb_comdat_main.yaml
@@ -0,0 +1,446 @@
+--- !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
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_OBJNAME
+ ObjNameSym:
+ Signature: 0
+ ObjectName: 'C:\src\llvm-project\build\pdb_comdat_main.obj'
+ - Kind: S_COMPILE3
+ Compile3Sym:
+ Flags: [ SecurityChecks, HotPatch ]
+ Machine: X64
+ FrontendMajor: 19
+ FrontendMinor: 0
+ FrontendBuild: 24215
+ FrontendQFE: 1
+ BackendMajor: 19
+ BackendMinor: 0
+ BackendBuild: 24215
+ BackendQFE: 1
+ Version: 'Microsoft (R) Optimizing Compiler'
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ PtrParent: 0
+ PtrEnd: 0
+ PtrNext: 0
+ CodeSize: 24
+ DbgStart: 4
+ DbgEnd: 19
+ FunctionType: 4102
+ Segment: 0
+ Flags: [ ]
+ DisplayName: main
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 40
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 24
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\pdb_comdat_main.c'
+ Lines:
+ - Offset: 0
+ LineStart: 2
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 4
+ LineStart: 3
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 9
+ LineStart: 4
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 14
+ LineStart: 5
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 19
+ LineStart: 6
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !Symbols
+ Records:
+ - Kind: S_GDATA32
+ DataSym:
+ Type: 116
+ DisplayName: global
+ - !FileChecksums
+ Checksums:
+ - FileName: 'c:\src\llvm-project\build\pdb_comdat_main.c'
+ Kind: MD5
+ Checksum: F969E51BBE373436D81492EB61387F36
+ - FileName: 'c:\src\llvm-project\build\foo.h'
+ Kind: MD5
+ Checksum: D74D834EFAC3AE2B45E606A8320B1D5C
+ - !StringTable
+ Strings:
+ - 'c:\src\llvm-project\build\pdb_comdat_main.c'
+ - 'c:\src\llvm-project\build\foo.h'
+ - !Symbols
+ Records:
+ - Kind: S_BUILDINFO
+ BuildInfoSym:
+ BuildId: 4111
+ Relocations:
+ - VirtualAddress: 168
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 172
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 224
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 228
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 304
+ SymbolName: global
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 308
+ SymbolName: global
+ Type: IMAGE_REL_AMD64_SECTION
+ - Name: '.debug$T'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ Types:
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 0 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 3
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4096
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4097
+ Attrs: 65548
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4097
+ Name: foo
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 116
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4101
+ Name: main
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4097
+ Name: bar
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\PROGRA~2\MICROS~1.0\VC\Bin\amd64\cl.exe'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: '-c -Z7 -MT -IC:\PROGRA~2\MICROS~1.0\VC\include -IC:\PROGRA~2\MICROS~1.0\VC\atlmfc\include -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\ucrt -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\shared -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\um'
+ - Kind: LF_SUBSTR_LIST
+ StringList:
+ StringIndices: [ 4106 ]
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 4107
+ String: ' -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\winrt -TC -X'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: pdb_comdat_main.c
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build\vc140.pdb'
+ - Kind: LF_BUILDINFO
+ BuildInfo:
+ ArgIndices: [ 4104, 4105, 4109, 4110, 4108 ]
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 4883EC28E800000000E800000000B82A0000004883C428C3
+ Relocations:
+ - VirtualAddress: 5
+ SymbolName: foo
+ Type: IMAGE_REL_AMD64_REL32
+ - VirtualAddress: 10
+ SymbolName: bar
+ Type: IMAGE_REL_AMD64_REL32
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 8B0500000000FFC0890500000000C3
+ Relocations:
+ - VirtualAddress: 2
+ SymbolName: global
+ Type: IMAGE_REL_AMD64_REL32
+ - VirtualAddress: 10
+ SymbolName: global
+ Type: IMAGE_REL_AMD64_REL32
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ PtrParent: 0
+ PtrEnd: 0
+ PtrNext: 0
+ CodeSize: 15
+ DbgStart: 0
+ DbgEnd: 14
+ FunctionType: 4099
+ Segment: 0
+ Flags: [ ]
+ DisplayName: foo
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 0
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ MarkedInline, AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 15
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\foo.h'
+ Lines:
+ - Offset: 0
+ LineStart: 2
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 0
+ LineStart: 3
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 14
+ LineStart: 4
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ Relocations:
+ - VirtualAddress: 44
+ SymbolName: foo
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 48
+ SymbolName: foo
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 100
+ SymbolName: foo
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 104
+ SymbolName: foo
+ Type: IMAGE_REL_AMD64_SECTION
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '0104010004420000'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '000000001800000000000000'
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: '$LN3'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 4
+ SymbolName: '$LN3'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 8
+ SymbolName: '$unwind$main'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+symbols:
+ - 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: 480
+ NumberOfRelocations: 6
+ 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: 648
+ 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: 24
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 492663294
+ Number: 0
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 15
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 1746394828
+ Number: 0
+ Selection: IMAGE_COMDAT_SELECT_ANY
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 148
+ NumberOfRelocations: 4
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 5
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+ - Name: foo
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: bar
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: main
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '$LN3'
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_LABEL
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 7
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 264583633
+ Number: 0
+ - Name: '$unwind$main'
+ Value: 0
+ SectionNumber: 7
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 8
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 12
+ NumberOfRelocations: 3
+ NumberOfLinenumbers: 0
+ CheckSum: 2942184094
+ Number: 0
+ - Name: '$pdata$main'
+ Value: 0
+ SectionNumber: 8
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: global
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/Inputs/pdb_lines_1.yaml b/test/COFF/Inputs/pdb_lines_1.yaml
new file mode 100644
index 000000000..3fbb2a94d
--- /dev/null
+++ b/test/COFF/Inputs/pdb_lines_1.yaml
@@ -0,0 +1,480 @@
+--- !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
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_OBJNAME
+ ObjNameSym:
+ Signature: 0
+ ObjectName: 'C:\src\llvm-project\build\pdb_lines_1.obj'
+ - Kind: S_COMPILE3
+ Compile3Sym:
+ Flags: [ SecurityChecks, HotPatch ]
+ Machine: X64
+ FrontendMajor: 19
+ FrontendMinor: 0
+ FrontendBuild: 24215
+ FrontendQFE: 1
+ BackendMajor: 19
+ BackendMinor: 0
+ BackendBuild: 24215
+ BackendQFE: 1
+ Version: 'Microsoft (R) Optimizing Compiler'
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ PtrParent: 0
+ PtrEnd: 0
+ PtrNext: 0
+ CodeSize: 19
+ DbgStart: 4
+ DbgEnd: 14
+ FunctionType: 4102
+ Segment: 0
+ Flags: [ ]
+ DisplayName: main
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 40
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 19
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\pdb_lines_1.c'
+ Lines:
+ - Offset: 0
+ LineStart: 2
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 4
+ LineStart: 3
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 9
+ LineStart: 4
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 14
+ LineStart: 5
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !FileChecksums
+ Checksums:
+ - FileName: 'c:\src\llvm-project\build\pdb_lines_1.c'
+ Kind: MD5
+ Checksum: 4EB19DCD86C3BA2238A255C718572E7B
+ - FileName: 'c:\src\llvm-project\build\foo.h'
+ Kind: MD5
+ Checksum: 061EB73ABB642532857A4F1D9CBAC323
+ - !StringTable
+ Strings:
+ - 'c:\src\llvm-project\build\pdb_lines_1.c'
+ - 'c:\src\llvm-project\build\foo.h'
+ - !Symbols
+ Records:
+ - Kind: S_BUILDINFO
+ BuildInfoSym:
+ BuildId: 4111
+ Relocations:
+ - VirtualAddress: 164
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 168
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 220
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 224
+ 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
+ Types:
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 3
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4096
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4097
+ Attrs: 65548
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4097
+ Name: foo
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 0 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 116
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4101
+ Name: main
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4097
+ Name: bar
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\PROGRA~2\MICROS~1.0\VC\Bin\amd64\cl.exe'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: '-c -Z7 -MT -IC:\PROGRA~2\MICROS~1.0\VC\include -IC:\PROGRA~2\MICROS~1.0\VC\atlmfc\include -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\ucrt -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\shared -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\um'
+ - Kind: LF_SUBSTR_LIST
+ StringList:
+ StringIndices: [ 4106 ]
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 4107
+ String: ' -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\winrt -TC -X'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: pdb_lines_1.c
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build\vc140.pdb'
+ - Kind: LF_BUILDINFO
+ BuildInfo:
+ ArgIndices: [ 4104, 4105, 4109, 4110, 4108 ]
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 4883EC28E800000000B82A0000004883C428C3
+ Relocations:
+ - VirtualAddress: 5
+ SymbolName: foo
+ Type: IMAGE_REL_AMD64_REL32
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 4883EC28E8000000004883C428C3
+ Relocations:
+ - VirtualAddress: 5
+ SymbolName: bar
+ Type: IMAGE_REL_AMD64_REL32
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ PtrParent: 0
+ PtrEnd: 0
+ PtrNext: 0
+ CodeSize: 14
+ DbgStart: 4
+ DbgEnd: 9
+ FunctionType: 4099
+ Segment: 0
+ Flags: [ ]
+ DisplayName: foo
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 40
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ MarkedInline, AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 14
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\foo.h'
+ Lines:
+ - Offset: 0
+ LineStart: 2
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 4
+ LineStart: 3
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 9
+ LineStart: 4
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ Relocations:
+ - VirtualAddress: 44
+ SymbolName: foo
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 48
+ SymbolName: foo
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 100
+ SymbolName: foo
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 104
+ SymbolName: foo
+ Type: IMAGE_REL_AMD64_SECTION
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '0104010004420000'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '000000000E00000000000000'
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: '$LN3'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 4
+ SymbolName: '$LN3'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 8
+ SymbolName: '$unwind$foo'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '0104010004420000'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '000000001300000000000000'
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: '$LN3'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 4
+ SymbolName: '$LN3'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 8
+ SymbolName: '$unwind$main'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+symbols:
+ - 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: 432
+ 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: 644
+ 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: 19
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 791570821
+ Number: 0
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 14
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 1682752513
+ Number: 0
+ Selection: IMAGE_COMDAT_SELECT_ANY
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 148
+ NumberOfRelocations: 4
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 5
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+ - Name: bar
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: foo
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: main
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '$LN3'
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_LABEL
+ - Name: '$LN3'
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_LABEL
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 7
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 264583633
+ Number: 5
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+ - Name: '$unwind$foo'
+ Value: 0
+ SectionNumber: 7
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 8
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 12
+ NumberOfRelocations: 3
+ NumberOfLinenumbers: 0
+ CheckSum: 361370162
+ Number: 5
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+ - Name: '$pdata$foo'
+ Value: 0
+ SectionNumber: 8
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 9
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 264583633
+ Number: 0
+ - Name: '$unwind$main'
+ Value: 0
+ SectionNumber: 9
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 10
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 12
+ NumberOfRelocations: 3
+ NumberOfLinenumbers: 0
+ CheckSum: 4063508168
+ Number: 0
+ - Name: '$pdata$main'
+ Value: 0
+ SectionNumber: 10
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+...
diff --git a/test/COFF/Inputs/pdb_lines_2.yaml b/test/COFF/Inputs/pdb_lines_2.yaml
new file mode 100644
index 000000000..8ad8d062d
--- /dev/null
+++ b/test/COFF/Inputs/pdb_lines_2.yaml
@@ -0,0 +1,209 @@
+--- !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
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_OBJNAME
+ ObjNameSym:
+ Signature: 0
+ ObjectName: 'C:\src\llvm-project\build\pdb_lines_2.obj'
+ - Kind: S_COMPILE3
+ Compile3Sym:
+ Flags: [ SecurityChecks, HotPatch ]
+ Machine: X64
+ FrontendMajor: 19
+ FrontendMinor: 0
+ FrontendBuild: 24215
+ FrontendQFE: 1
+ BackendMajor: 19
+ BackendMinor: 0
+ BackendBuild: 24215
+ BackendQFE: 1
+ Version: 'Microsoft (R) Optimizing Compiler'
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ PtrParent: 0
+ PtrEnd: 0
+ PtrNext: 0
+ CodeSize: 1
+ DbgStart: 0
+ DbgEnd: 0
+ FunctionType: 4098
+ Segment: 0
+ Flags: [ ]
+ DisplayName: bar
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 0
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 1
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\pdb_lines_2.c'
+ Lines:
+ - Offset: 0
+ LineStart: 1
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 0
+ LineStart: 2
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !FileChecksums
+ Checksums:
+ - FileName: 'c:\src\llvm-project\build\pdb_lines_2.c'
+ Kind: MD5
+ Checksum: DF91CB3A2B8D917486574BB50CAC4CC7
+ - !StringTable
+ Strings:
+ - 'c:\src\llvm-project\build\pdb_lines_2.c'
+ - !Symbols
+ Records:
+ - Kind: S_BUILDINFO
+ BuildInfoSym:
+ BuildId: 4106
+ Relocations:
+ - VirtualAddress: 164
+ SymbolName: bar
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 168
+ SymbolName: bar
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 220
+ SymbolName: bar
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 224
+ SymbolName: bar
+ Type: IMAGE_REL_AMD64_SECTION
+ - Name: '.debug$T'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ Types:
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 3
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4096
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4097
+ Name: bar
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\PROGRA~2\MICROS~1.0\VC\Bin\amd64\cl.exe'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: '-c -Z7 -MT -IC:\PROGRA~2\MICROS~1.0\VC\include -IC:\PROGRA~2\MICROS~1.0\VC\atlmfc\include -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\ucrt -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\shared -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\um'
+ - Kind: LF_SUBSTR_LIST
+ StringList:
+ StringIndices: [ 4101 ]
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 4102
+ String: ' -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\winrt -TC -X'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: pdb_lines_2.c
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build\vc140.pdb'
+ - Kind: LF_BUILDINFO
+ BuildInfo:
+ ArgIndices: [ 4099, 4100, 4104, 4105, 4103 ]
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: C3
+symbols:
+ - 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: 360
+ 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: 568
+ 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: 1
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 40735498
+ Number: 0
+ - Name: bar
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/arm64-import2.test b/test/COFF/arm64-import2.test
new file mode 100644
index 000000000..3811f3ed7
--- /dev/null
+++ b/test/COFF/arm64-import2.test
@@ -0,0 +1,85 @@
+# REQUIRES: aarch64
+
+# RUN: yaml2obj < %s > %t.obj
+# RUN: llvm-objdump -d %t.obj | FileCheck %s -check-prefix BEFORE
+# RUN: lld-link /entry:main /subsystem:console /out:%t.exe %t.obj %p/Inputs/library-arm64.lib %p/Inputs/library2-arm64.lib
+# RUN: llvm-objdump -d %t.exe | FileCheck %s -check-prefix AFTER
+# RUN: llvm-readobj -coff-imports %t.exe | FileCheck %s -check-prefix IMPORTS
+
+# BEFORE: Disassembly of section .text:
+# BEFORE: 0: 00 00 00 94 bl #0
+# BEFORE: 4: 00 00 00 94 bl #0
+# BEFORE: 8: c0 03 5f d6 ret
+
+# AFTER: Disassembly of section .text:
+# AFTER: 140001000: 03 00 00 94 bl #12
+# AFTER: 140001004: 05 00 00 94 bl #20
+# AFTER: 140001008: c0 03 5f d6 ret
+# AFTER: 14000100c: 10 00 00 b0 adrp x16, #4096
+# AFTER: 140001010: 10 32 40 f9 ldr x16, [x16, #96]
+# AFTER: 140001014: 00 02 1f d6 br x16
+# AFTER: 140001018: 10 00 00 b0 adrp x16, #4096
+# AFTER: 14000101c: 10 3a 40 f9 ldr x16, [x16, #112]
+# AFTER: 140001020: 00 02 1f d6 br x16
+
+# IMPORTS: Import {
+# IMPORTS: Name: library.dll
+# IMPORTS: ImportLookupTableRVA: 0x2040
+# IMPORTS: ImportAddressTableRVA: 0x2060
+# IMPORTS: Symbol: function (2)
+# IMPORTS: }
+# IMPORTS: Import {
+# IMPORTS: Name: library2.dll
+# IMPORTS: ImportLookupTableRVA: 0x2050
+# IMPORTS: ImportAddressTableRVA: 0x2070
+# IMPORTS: Symbol: function2 (0)
+# IMPORTS: }
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_ARM64
+ Characteristics: [ ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: 0000009400000094C0035FD6
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: function
+ Type: 3
+ - VirtualAddress: 4
+ SymbolName: function2
+ Type: 3
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 12
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 1438860354
+ Number: 1
+ - Name: main
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: function
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: function2
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/arm64-magic.yaml b/test/COFF/arm64-magic.yaml
new file mode 100644
index 000000000..a35eeca48
--- /dev/null
+++ b/test/COFF/arm64-magic.yaml
@@ -0,0 +1,46 @@
+# RUN: yaml2obj < %s > %t.obj
+# RUN: lld-link /out:%t.exe /entry:mainCRTStartup /subsystem:console %t.obj
+# RUN: llvm-readobj -file-headers %t.exe | FileCheck %s
+
+# CHECK: Format: COFF-ARM64
+# CHECK: Arch: aarch64
+# CHECK: AddressSize: 64bit
+# CHECK: ImageFileHeader {
+# CHECK: Machine: IMAGE_FILE_MACHINE_ARM64 (0xAA64)
+# CHECK: Characteristics [ (0x22)
+# CHECK: IMAGE_FILE_EXECUTABLE_IMAGE (0x2)
+# CHECK: IMAGE_FILE_LARGE_ADDRESS_AWARE (0x20)
+# CHECK: ]
+# CHECK: }
+# CHECK: ImageOptionalHeader {
+# CHECK: Magic: 0x20B
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_ARM64
+ Characteristics: []
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_PURGEABLE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: 'e0031f2ac0035fd6'
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 1
+ - Name: mainCRTStartup
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/arm64-relocs-imports.test b/test/COFF/arm64-relocs-imports.test
new file mode 100644
index 000000000..e31b172aa
--- /dev/null
+++ b/test/COFF/arm64-relocs-imports.test
@@ -0,0 +1,238 @@
+# REQUIRES: aarch64
+
+# RUN: yaml2obj < %s > %t.obj
+# RUN: llvm-objdump -d %t.obj | FileCheck %s -check-prefix BEFORE
+# RUN: lld-link /entry:main /subsystem:console /out:%t.exe %t.obj %p/Inputs/library-arm64.lib
+# RUN: llvm-objdump -d %t.exe | FileCheck %s -check-prefix AFTER
+
+# BEFORE: Disassembly of section .text:
+# BEFORE: 0: fe 0f 1f f8 str x30, [sp, #-16]!
+# BEFORE: 4: 00 00 00 90 adrp x0, #0
+# BEFORE: 8: 00 08 00 91 add x0, x0, #2
+# BEFORE: c: 00 00 00 94 bl #0
+# BEFORE: 10: 00 01 40 39 ldrb w0, [x8]
+# BEFORE: 14: 00 01 40 79 ldrh w0, [x8]
+# BEFORE: 18: 00 01 40 b9 ldr w0, [x8]
+# BEFORE: 1c: 00 01 40 f9 ldr x0, [x8]
+# BEFORE: 20: 00 01 00 39 strb w0, [x8]
+# BEFORE: 24: 00 01 00 79 strh w0, [x8]
+# BEFORE: 28: 00 01 00 b9 str w0, [x8]
+# BEFORE: 2c: 00 01 00 f9 str x0, [x8]
+# BEFORE: 30: 00 01 40 3d ldr b0, [x8]
+# BEFORE: 34: 00 01 40 7d ldr h0, [x8]
+# BEFORE: 38: 00 01 40 bd ldr s0, [x8]
+# BEFORE: 3c: 00 01 40 fd ldr d0, [x8]
+# BEFORE: 40: 00 01 c0 3d ldr q0, [x8]
+# BEFORE: 44: 00 01 00 3d str b0, [x8]
+# BEFORE: 48: 00 01 00 7d str h0, [x8]
+# BEFORE: 4c: 00 01 00 bd str s0, [x8]
+# BEFORE: 50: 00 01 00 fd str d0, [x8]
+# BEFORE: 54: 00 01 80 3d str q0, [x8]
+# BEFORE: 58: 00 05 40 f9 ldr x0, [x8, #8]
+# BEFORE: 5c: 20 1a 01 b0 adrp x0, #36982784
+# BEFORE: 60: 00 fc 4f f9 ldr x0, [x0, #8184]
+# BEFORE: 64: e0 03 1f 2a mov w0, wzr
+# BEFORE: 68: fe 07 41 f8 ldr x30, [sp], #16
+# BEFORE: 6c: c0 03 5f d6 ret
+# BEFORE: 70: 08 00 00 00 <unknown>
+# BEFORE: 74: 00 00 00 00 <unknown>
+# BEFORE: 78: 01 00 00 00 <unknown>
+
+# AFTER: Disassembly of section .text:
+# AFTER: 140002000: fe 0f 1f f8 str x30, [sp, #-16]!
+# AFTER: 140002004: e0 ff ff f0 adrp x0, #-4096
+# AFTER: 140002008: 00 18 00 91 add x0, x0, #6
+# AFTER: 14000200c: 1c 00 00 94 bl #112
+# AFTER: 140002010: 00 21 40 39 ldrb w0, [x8, #8]
+# AFTER: 140002014: 00 11 40 79 ldrh w0, [x8, #8]
+# AFTER: 140002018: 00 09 40 b9 ldr w0, [x8, #8]
+# AFTER: 14000201c: 00 05 40 f9 ldr x0, [x8, #8]
+# AFTER: 140002020: 00 21 00 39 strb w0, [x8, #8]
+# AFTER: 140002024: 00 11 00 79 strh w0, [x8, #8]
+# AFTER: 140002028: 00 09 00 b9 str w0, [x8, #8]
+# AFTER: 14000202c: 00 05 00 f9 str x0, [x8, #8]
+# AFTER: 140002030: 00 41 40 3d ldr b0, [x8, #16]
+# AFTER: 140002034: 00 21 40 7d ldr h0, [x8, #16]
+# AFTER: 140002038: 00 11 40 bd ldr s0, [x8, #16]
+# AFTER: 14000203c: 00 09 40 fd ldr d0, [x8, #16]
+# AFTER: 140002040: 00 05 c0 3d ldr q0, [x8, #16]
+# AFTER: 140002044: 00 41 00 3d str b0, [x8, #16]
+# AFTER: 140002048: 00 21 00 7d str h0, [x8, #16]
+# AFTER: 14000204c: 00 11 00 bd str s0, [x8, #16]
+# AFTER: 140002050: 00 09 00 fd str d0, [x8, #16]
+# AFTER: 140002054: 00 05 80 3d str q0, [x8, #16]
+# AFTER: 140002058: 00 09 40 f9 ldr x0, [x8, #16]
+# AFTER: 14000205c: 00 00 00 b0 adrp x0, #4096
+# AFTER: 140002060: 00 fc 47 f9 ldr x0, [x0, #4088]
+# AFTER: 140002064: e0 03 1f 2a mov w0, wzr
+# AFTER: 140002068: fe 07 41 f8 ldr x30, [sp], #16
+# AFTER: 14000206c: c0 03 5f d6 ret
+# AFTER: 140002070: 10 10 00 40 <unknown>
+# AFTER: 140002074: 01 00 00 00 <unknown>
+# AFTER: 140002078: 09 10 00 00 <unknown>
+# AFTER: 14000207c: 10 00 00 b0 adrp x16, #4096
+# AFTER: 140002080: 10 1e 40 f9 ldr x16, [x16, #56]
+# AFTER: 140002084: 00 02 1f d6 br x16
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_ARM64
+ Characteristics: [ ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: FE0F1FF80000009000080091000000940001403900014079000140B9000140F90001003900010079000100B9000100F90001403D0001407D000140BD000140FD0001C03D0001003D0001007D000100BD000100FD0001803D000540F9201A01B000FC4FF9E0031F2AFE0741F8C0035FD6080000000000000001000000
+ Relocations:
+ - VirtualAddress: 4
+ SymbolName: .Lstr
+ Type: 4
+ - VirtualAddress: 8
+ SymbolName: .Lstr
+ Type: 6
+ - VirtualAddress: 12
+ SymbolName: function
+ Type: 3
+ - VirtualAddress: 16
+ SymbolName: .Lglobal
+ Type: 7
+ - VirtualAddress: 20
+ SymbolName: .Lglobal
+ Type: 7
+ - VirtualAddress: 24
+ SymbolName: .Lglobal
+ Type: 7
+ - VirtualAddress: 28
+ SymbolName: .Lglobal
+ Type: 7
+ - VirtualAddress: 32
+ SymbolName: .Lglobal
+ Type: 7
+ - VirtualAddress: 36
+ SymbolName: .Lglobal
+ Type: 7
+ - VirtualAddress: 40
+ SymbolName: .Lglobal
+ Type: 7
+ - VirtualAddress: 44
+ SymbolName: .Lglobal
+ Type: 7
+ - VirtualAddress: 48
+ SymbolName: .Lglobal16
+ Type: 7
+ - VirtualAddress: 52
+ SymbolName: .Lglobal16
+ Type: 7
+ - VirtualAddress: 56
+ SymbolName: .Lglobal16
+ Type: 7
+ - VirtualAddress: 60
+ SymbolName: .Lglobal16
+ Type: 7
+ - VirtualAddress: 64
+ SymbolName: .Lglobal16
+ Type: 7
+ - VirtualAddress: 68
+ SymbolName: .Lglobal16
+ Type: 7
+ - VirtualAddress: 72
+ SymbolName: .Lglobal16
+ Type: 7
+ - VirtualAddress: 76
+ SymbolName: .Lglobal16
+ Type: 7
+ - VirtualAddress: 80
+ SymbolName: .Lglobal16
+ Type: 7
+ - VirtualAddress: 84
+ SymbolName: .Lglobal16
+ Type: 7
+ - VirtualAddress: 88
+ SymbolName: .Lglobal
+ Type: 7
+ - VirtualAddress: 92
+ SymbolName: .Lglobal16
+ Type: 4
+ - VirtualAddress: 96
+ SymbolName: .Lglobal0
+ Type: 7
+ - VirtualAddress: 112
+ SymbolName: .Lglobal
+ Type: 14
+ - VirtualAddress: 120
+ SymbolName: .Lglobal
+ Type: 2
+ - 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: ''
+ - Name: .rdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ SectionData: 00000000202068656C6C6F20776F726C6400
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 28
+ NumberOfRelocations: 3
+ NumberOfLinenumbers: 0
+ CheckSum: 1438860354
+ Number: 1
+ - Name: .rdata
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 12
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 872944732
+ Number: 4
+ - Name: main
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: .Lstr
+ Value: 4
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .Lglobal
+ Value: 8
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .Lglobal16
+ Value: 16
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .Lglobal0
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: function
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/combined-resources.test b/test/COFF/combined-resources.test
new file mode 100644
index 000000000..e8d5d6500
--- /dev/null
+++ b/test/COFF/combined-resources.test
@@ -0,0 +1,213 @@
+// Check that lld properly handles merging multiple .res files.
+// The inputs were generated with the following commands, using the original
+// Windows rc.exe
+// > rc /fo combined-resources.res /nologo combined-resources.rc
+// > rc /fo combined-resources-2.res /nologo combined-resources-2.rc
+
+# RUN: yaml2obj < %p/Inputs/ret42.yaml > %t.obj
+# RUN: lld-link /out:%t.exe /entry:main %t.obj %p/Inputs/resource.res \
+# RUN: %p/Inputs/combined-resources.res %p/Inputs/combined-resources-2.res
+
+# RUN: llvm-readobj -coff-resources -file-headers -section-data %t.exe | \
+# RUN: FileCheck %s
+
+CHECK: ResourceTableRVA: 0x1000
+CHECK-NEXT: ResourceTableSize: 0xC1C
+CHECK-DAG: Resources [
+CHECK-NEXT: Total Number of Resources: 13
+CHECK-DAG: .rsrc Data (
+CHECK-NEXT: 0000: 00000000 00000000 00000000 01000600 |................|
+CHECK-NEXT: 0010: 38030080 48000080 02000000 60000080 |8...H.......`...|
+CHECK-NEXT: 0020: 04000000 80000080 05000000 A0000080 |................|
+CHECK-NEXT: 0030: 06000000 B8000080 09000000 D0000080 |................|
+CHECK-NEXT: 0040: 0A000000 F0000080 00000000 00000000 |................|
+CHECK-NEXT: 0050: 00000000 01000000 50030080 08010080 |........P.......|
+CHECK-NEXT: 0060: 00000000 00000000 00000000 02000000 |................|
+CHECK-NEXT: 0070: FE020080 20010080 0C030080 38010080 |.... .......8...|
+CHECK-NEXT: 0080: 00000000 00000000 00000000 01000100 |................|
+CHECK-NEXT: 0090: 2C030080 50010080 60380000 68010080 |,...P...`8..h...|
+CHECK-NEXT: 00A0: 00000000 00000000 00000000 01000000 |................|
+CHECK-NEXT: 00B0: 16030080 80010080 00000000 00000000 |................|
+CHECK-NEXT: 00C0: 00000000 00000100 01000000 98010080 |................|
+CHECK-NEXT: 00D0: 00000000 00000000 00000000 01000100 |................|
+CHECK-NEXT: 00E0: E0020080 B0010080 0C000000 D0010080 |................|
+CHECK-NEXT: 00F0: 00000000 00000000 00000000 01000000 |................|
+CHECK-NEXT: 0100: 66030080 E8010080 00000000 00000000 |f...............|
+CHECK-NEXT: 0110: 00000000 00000100 09040000 10020000 |................|
+CHECK-NEXT: 0120: 00000000 00000000 00000000 00000100 |................|
+CHECK-NEXT: 0130: 09040000 20020000 00000000 00000000 |.... ...........|
+CHECK-NEXT: 0140: 00000000 00000100 09040000 30020000 |............0...|
+CHECK-NEXT: 0150: 00000000 00000000 00000000 00000100 |................|
+CHECK-NEXT: 0160: 090C0000 40020000 00000000 00000000 |....@...........|
+CHECK-NEXT: 0170: 00000000 00000100 04080000 50020000 |............P...|
+CHECK-NEXT: 0180: 00000000 00000000 00000000 00000100 |................|
+CHECK-NEXT: 0190: 09040000 60020000 00000000 00000000 |....`...........|
+CHECK-NEXT: 01A0: 00000000 00000100 09040000 70020000 |............p...|
+CHECK-NEXT: 01B0: 00000000 00000000 00000000 00000200 |................|
+CHECK-NEXT: 01C0: 09040000 80020000 04080000 90020000 |................|
+CHECK-NEXT: 01D0: 00000000 00000000 00000000 00000100 |................|
+CHECK-NEXT: 01E0: 09040000 A0020000 00000000 00000000 |................|
+CHECK-NEXT: 01F0: 00000000 00000300 09040000 B0020000 |................|
+CHECK-NEXT: 0200: 04080000 C0020000 07100000 D0020000 |................|
+CHECK-NEXT: 0210: FC1A0000 39000000 00000000 00000000 |....9...........|
+CHECK-NEXT: 0220: C4130000 28030000 00000000 00000000 |....(...........|
+CHECK-NEXT: 0230: EC160000 28030000 00000000 00000000 |....(...........|
+CHECK-NEXT: 0240: CC1A0000 30000000 00000000 00000000 |....0...........|
+CHECK-NEXT: 0250: 141A0000 2E000000 00000000 00000000 |................|
+CHECK-NEXT: 0260: 441A0000 6C000000 00000000 00000000 |D...l...........|
+CHECK-NEXT: 0270: 7C130000 2A000000 00000000 00000000 ||...*...........|
+CHECK-NEXT: 0280: AC130000 18000000 00000000 00000000 |................|
+CHECK-NEXT: 0290: 041C0000 18000000 00000000 00000000 |................|
+CHECK-NEXT: 02A0: B41A0000 18000000 00000000 00000000 |................|
+CHECK-NEXT: 02B0: 3C1B0000 36000000 00000000 00000000 |<...6...........|
+CHECK-NEXT: 02C0: 741B0000 43000000 00000000 00000000 |t...C...........|
+CHECK-NEXT: 02D0: BC1B0000 42000000 00000000 00000000 |....B...........|
+CHECK-NEXT: 02E0: 0E004D00 59004100 43004300 45004C00 |..M.Y.A.C.C.E.L.|
+CHECK-NEXT: 02F0: 45005200 41005400 4F005200 53000600 |E.R.A.T.O.R.S...|
+CHECK-NEXT: 0300: 43005500 52005300 4F005200 04004F00 |C.U.R.S.O.R...O.|
+CHECK-NEXT: 0310: 4B004100 59000A00 54004500 53005400 |K.A.Y...T.E.S.T.|
+CHECK-NEXT: 0320: 44004900 41004C00 4F004700 05002200 |D.I.A.L.O.G...".|
+CHECK-NEXT: 0330: 45004100 54002200 0B005300 54005200 |E.A.T."...S.T.R.|
+CHECK-NEXT: 0340: 49004E00 47004100 52005200 41005900 |I.N.G.A.R.R.A.Y.|
+CHECK-NEXT: 0350: 0A004D00 59005200 45005300 4F005500 |..M.Y.R.E.S.O.U.|
+CHECK-NEXT: 0360: 52004300 45000900 52004100 4E004400 |R.C.E...R.A.N.D.|
+CHECK-NEXT: 0370: 4F004D00 44004100 54000000 00000500 |O.M.D.A.T.......|
+CHECK-NEXT: 0380: 48006500 6C006C00 6F000000 00000000 |H.e.l.l.o.......|
+CHECK-NEXT: 0390: 00000000 00000000 00000000 00000000 |................|
+CHECK-NEXT: 03A0: 00000000 00000000 00000000 11000300 |................|
+CHECK-NEXT: 03B0: E7030000 0D004400 4C040000 82001200 |......D.L.......|
+CHECK-NEXT: 03C0: BC010000 28000000 10000000 10000000 |....(...........|
+CHECK-NEXT: 03D0: 01001800 00000000 00030000 C40E0000 |................|
+CHECK-NEXT: 03E0: C40E0000 00000000 00000000 FFFFFFFF |................|
+CHECK-NEXT: 03F0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0400: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0410: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0420: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0430: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0440: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0450: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0460: FF7F7F7F 7C7C7C78 78787575 75FFFFFF |....|||xxxuuu...|
+CHECK-NEXT: 0470: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0480: FFFFFFFF FFFFFFFF 979797FF FFFFFFFF |................|
+CHECK-NEXT: 0490: FF838383 AAAAAADB DBDB7979 79757575 |..........yyyuuu|
+CHECK-NEXT: 04A0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 04B0: FFFFFFFF FFFFFFFF 9C9C9C98 9898FFFF |................|
+CHECK-NEXT: 04C0: FF888888 DBDBDBB7 B7B77D7D 7DFFFFFF |..........}}}...|
+CHECK-NEXT: 04D0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 04E0: FFFFFFFF FFFFFFFF A0A0A09C 9C9C9393 |................|
+CHECK-NEXT: 04F0: 93ADADAD F2F2F284 84848181 81FFFFFF |................|
+CHECK-NEXT: 0500: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0510: FFFFFFFF FFFFFFFF A4A4A4D7 D7D79D9D |................|
+CHECK-NEXT: 0520: 9DD0D0D0 EEEEEE91 91918D8D 8DFFFFFF |................|
+CHECK-NEXT: 0530: FFFFFF81 81817E7E 7EFFFFFF FFFFFFFF |......~~~.......|
+CHECK-NEXT: 0540: FFFFFFFF FFFFFFFF A9A9A9F2 F2F2E5E5 |................|
+CHECK-NEXT: 0550: E5E2E2E2 95959591 91918D8D 8D898989 |................|
+CHECK-NEXT: 0560: 868686FF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0570: FFFFFFFF FFFFFFFF ADADADF2 F2F2E1E1 |................|
+CHECK-NEXT: 0580: E1DFDFDF E7E7E7E4 E4E4BBBB BB8E8E8E |................|
+CHECK-NEXT: 0590: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 05A0: FFFFFFFF FFFFFFFF B5B5B5F2 F2F2E8E8 |................|
+CHECK-NEXT: 05B0: E8E7E7E7 EAEAEAC6 C6C69E9E 9EFFFFFF |................|
+CHECK-NEXT: 05C0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 05D0: FFFFFFFF FFFFFFFF B9B9B9F4 F4F4ECEC |................|
+CHECK-NEXT: 05E0: ECEDEDED CBCBCBA7 A7A7FFFF FFFFFFFF |................|
+CHECK-NEXT: 05F0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0600: FFFFFFFF FFFFFFFF BDBDBDF7 F7F7EFEF |................|
+CHECK-NEXT: 0610: EFD0D0D0 AFAFAFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0620: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0630: FFFFFFFF FFFFFFFF C1C1C1F7 F7F7D5D5 |................|
+CHECK-NEXT: 0640: D5B6B6B6 FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0650: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0660: FFFFFFFF FFFFFFFF C4C4C4D9 D9D9BEBE |................|
+CHECK-NEXT: 0670: BEFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0680: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0690: FFFFFFFF FFFFFFFF C8C8C8C5 C5C5FFFF |................|
+CHECK-NEXT: 06A0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 06B0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 06C0: FFFFFFFF FFFFFFFF CBCBCBFF FFFFFFFF |................|
+CHECK-NEXT: 06D0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 06E0: FFFFFFFF FFFFFFFF FFFFFFFF 28000000 |............(...|
+CHECK-NEXT: 06F0: 10000000 10000000 01001800 00000000 |................|
+CHECK-NEXT: 0700: 00030000 C40E0000 C40E0000 00000000 |................|
+CHECK-NEXT: 0710: 00000000 FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0720: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0730: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0740: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0750: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0760: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0770: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0780: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0790: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 07A0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 07B0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 07C0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 07D0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 07E0: A0E3A901 B31801B3 1801B318 01B31801 |................|
+CHECK-NEXT: 07F0: B31801B3 1861D06F FFFFFFFF FFFFFFFF |.....a.o........|
+CHECK-NEXT: 0800: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0810: 01B31800 D7331CDB 49DBF9E2 9BEFAF00 |.....3..I.......|
+CHECK-NEXT: 0820: D73300D7 3301B318 FFFFFFFF FFFFFFFF |.3..3...........|
+CHECK-NEXT: 0830: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0840: 01B31800 DE55F6FE F9DBFAE7 FEFFFE86 |.....U..........|
+CHECK-NEXT: 0850: EFAE00DE 5501B318 FFFFFFFF FFFFFFFF |....U...........|
+CHECK-NEXT: 0860: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0870: 01B31800 E676DBFB EC00E676 57EFA5FB |.....v.....vW...|
+CHECK-NEXT: 0880: FFFD55EE A401B318 FFFFFFFF FFFFFFFF |..U.............|
+CHECK-NEXT: 0890: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 08A0: 01B31800 ED9800ED 9800ED98 00ED9887 |................|
+CHECK-NEXT: 08B0: F7CFFEFF FF01B318 FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 08C0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 08D0: 01B31800 F4BA00F4 BA00F4BA 00F4BA00 |................|
+CHECK-NEXT: 08E0: F4BA9CFB E401B318 FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 08F0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0900: 01B31800 FBDB00FB DB00FBDB 00FBDB00 |................|
+CHECK-NEXT: 0910: FBDB00FB DB01B318 FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0920: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0930: 9FE2A801 B31801B3 1801B318 01B31801 |................|
+CHECK-NEXT: 0940: B31801B3 1861D06F FFFFFFFF FFFFFFFF |.....a.o........|
+CHECK-NEXT: 0950: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0960: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0970: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0980: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0990: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 09A0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 09B0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 09C0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 09D0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 09E0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 09F0: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0A00: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................|
+CHECK-NEXT: 0A10: FFFFFFFF 00000000 00006400 79007500 |..........d.y.u.|
+CHECK-NEXT: 0A20: 00000000 65007300 68006100 6C006100 |....e.s.h.a.l.a.|
+CHECK-NEXT: 0A30: 00008000 66006B00 61006F00 79006100 |....f.k.a.o.y.a.|
+CHECK-NEXT: 0A40: 00000000 0000C080 00000000 02000A00 |................|
+CHECK-NEXT: 0A50: 0A00C800 2C010000 00005400 65007300 |....,.....T.e.s.|
+CHECK-NEXT: 0A60: 74000000 01000250 00000000 0A000A00 |t......P........|
+CHECK-NEXT: 0A70: E6000E00 0100FFFF 82004300 6F006E00 |..........C.o.n.|
+CHECK-NEXT: 0A80: 74006900 6E007500 65003A00 00000000 |t.i.n.u.e.:.....|
+CHECK-NEXT: 0A90: 00000150 00000000 42008600 A1000D00 |...P....B.......|
+CHECK-NEXT: 0AA0: 0200FFFF 80002600 4F004B00 00000000 |......&.O.K.....|
+CHECK-NEXT: 0AB0: 00000000 11005800 A4000000 0D004800 |......X.......H.|
+CHECK-NEXT: 0AC0: 2E160000 82001200 BC010000 00000000 |................|
+CHECK-NEXT: 0AD0: 00006400 66006900 73006800 00000000 |..d.f.i.s.h.....|
+CHECK-NEXT: 0AE0: 65007300 61006C00 61006400 00008000 |e.s.a.l.a.d.....|
+CHECK-NEXT: 0AF0: 66006400 75006300 6B000000 74686973 |f.d.u.c.k...this|
+CHECK-NEXT: 0B00: 20697320 61207573 65722064 6566696E | is a user defin|
+CHECK-NEXT: 0B10: 65642072 65736F75 72636500 69742063 |ed resource.it c|
+CHECK-NEXT: 0B20: 6F6E7461 696E7320 6D616E79 20737472 |ontains many str|
+CHECK-NEXT: 0B30: 696E6773 00000000 00000000 74686973 |ings........this|
+CHECK-NEXT: 0B40: 20697320 61207261 6E646F6D 20626974 | is a random bit|
+CHECK-NEXT: 0B50: 206F6620 64617461 20746861 74206D65 | of data that me|
+CHECK-NEXT: 0B60: 616E7320 6E6F7468 696E6700 A9230E14 |ans nothing..#..|
+CHECK-NEXT: 0B70: F4F60000 7A686534 20736869 34207969 |....zhe4 shi4 yi|
+CHECK-NEXT: 0B80: 31676534 20737569 326A6931 20646520 |1ge4 sui2ji1 de |
+CHECK-NEXT: 0B90: 73687534 6A75342C 207A6865 34207969 |shu4ju4, zhe4 yi|
+CHECK-NEXT: 0BA0: 34776569 347A6865 20736865 6E326D65 |4wei4zhe shen2me|
+CHECK-NEXT: 0BB0: 00A9230E 14F4F600 00000000 44696573 |..#.........Dies|
+CHECK-NEXT: 0BC0: 20697374 2065696E 207A7566 C3A46C6C | ist ein zuf..ll|
+CHECK-NEXT: 0BD0: 69676573 20426974 20766F6E 20446174 |iges Bit von Dat|
+CHECK-NEXT: 0BE0: 656E2C20 64696520 6E696368 74732062 |en, die nichts b|
+CHECK-NEXT: 0BF0: 65646575 74657400 A9230E14 F4F60000 |edeutet..#......|
+CHECK-NEXT: 0C00: 00000000 11000300 E7030000 0D004400 |..............D.|
+CHECK-NEXT: 0C10: 4C040000 82001200 BC010000 |L...........|
+CHECK-NEXT: )
diff --git a/test/COFF/common-alignment.test b/test/COFF/common-alignment.test
new file mode 100644
index 000000000..a4ee15729
--- /dev/null
+++ b/test/COFF/common-alignment.test
@@ -0,0 +1,78 @@
+# REQUIRES: x86
+# RUN: yaml2obj %s > %t.obj
+# RUN: lld-link /out:%t.exe /entry:main %t.obj %t.obj
+# RUN: llvm-objdump -d %t.exe | FileCheck %s
+
+# Operands of B8 (MOV EAX) are common symbols
+# CHECK: 3000: b8 00 10 00 40
+# CHECK: 3005: b8 10 10 00 40
+
+--- !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: 4
+ SectionData: b800000000b800000000
+ Relocations:
+ - VirtualAddress: 1
+ SymbolName: bssdata4
+ Type: IMAGE_REL_AMD64_ADDR32
+ - VirtualAddress: 6
+ SymbolName: bssdata4_align16
+ Type: IMAGE_REL_AMD64_ADDR32
+ - Name: .data
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 4
+ SectionData: 03000000
+ - Name: .drectve
+ Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
+ Alignment: 1
+ SectionData: 202d616c69676e636f6d6d3a62737364617461345f616c69676e31362c340a # -aligncomm:bssdata4_align16,4
+
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 0
+ NumberOfRelocations: 5
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: .data
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 4
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: main
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: bssdata4
+ Value: 4
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: bssdata4_align16
+ Value: 4
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/common.test b/test/COFF/common.test
index 007cfcfb6..4a00153f3 100644
--- a/test/COFF/common.test
+++ b/test/COFF/common.test
@@ -1,3 +1,4 @@
+# REQUIRES: x86
# RUN: yaml2obj %s > %t.obj
# RUN: lld-link /out:%t.exe /entry:main %t.obj %t.obj
# RUN: llvm-objdump -d %t.exe | FileCheck %s
diff --git a/test/COFF/conflict.test b/test/COFF/conflict.test
index a634c7185..ae8e6c8ad 100644
--- a/test/COFF/conflict.test
+++ b/test/COFF/conflict.test
@@ -1,3 +1,4 @@
+# REQUIRES: x86
# RUN: yaml2obj < %s > %t1.obj
# RUN: yaml2obj < %s > %t2.obj
# RUN: not lld-link /out:%t.exe %t1.obj %t2.obj >& %t.log
diff --git a/test/COFF/constant.test b/test/COFF/constant.test
index 3c8956bea..02d6b3e2c 100644
--- a/test/COFF/constant.test
+++ b/test/COFF/constant.test
@@ -1,3 +1,4 @@
+REQUIRES: x86
RUN: mkdir -p %t
RUN: llvm-mc -triple i686-unknown-windows-msvc -filetype obj -o %t/import.o %S/Inputs/constant-import.s
RUN: llc -mtriple i686-unknown-windows-msvc -filetype obj -o %t/export.o %S/Inputs/constant-export.ll
diff --git a/test/COFF/def-export-stdcall.s b/test/COFF/def-export-stdcall.s
index cdca7ae76..17473f7f9 100644
--- a/test/COFF/def-export-stdcall.s
+++ b/test/COFF/def-export-stdcall.s
@@ -1,7 +1,9 @@
+# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=i686-windows-msvc %s -o %t.obj
# RUN: echo -e "LIBRARY foo\nEXPORTS\n stdcall" > %t.def
# RUN: lld-link -entry:dllmain -dll -def:%t.def %t.obj -out:%t.dll -implib:%t.lib
-# RUN: llvm-nm %t.lib | FileCheck %s
+# RUN: llvm-readobj %t.lib | FileCheck %s
+# CHECK: Name type: undecorate
# CHECK: __imp__stdcall@8
# CHECK: _stdcall@8
diff --git a/test/COFF/delayimports-armnt.yaml b/test/COFF/delayimports-armnt.yaml
new file mode 100644
index 000000000..a1625511f
--- /dev/null
+++ b/test/COFF/delayimports-armnt.yaml
@@ -0,0 +1,106 @@
+# REQUIRES: arm
+# RUN: yaml2obj < %s > %t.obj
+# RUN: lld-link %t.obj %p/Inputs/library.lib /subsystem:console \
+# RUN: /entry:mainCRTStartup /alternatename:__delayLoadHelper2=mainCRTStartup \
+# RUN: /delayload:library.dll /out:%t.exe
+# RUN: llvm-readobj -coff-imports %t.exe | FileCheck -check-prefix=IMPORT %s
+# RUN: llvm-readobj -coff-basereloc %t.exe | FileCheck -check-prefix=BASEREL %s
+# RUN: llvm-objdump -d %t.exe | FileCheck -check-prefix=DISASM %s
+
+# IMPORT: Format: COFF-ARM
+# IMPORT-NEXT: Arch: thumb
+# IMPORT-NEXT: AddressSize: 32bit
+# IMPORT-NEXT: DelayImport {
+# IMPORT-NEXT: Name: library.dll
+# IMPORT-NEXT: Attributes: 0x1
+# IMPORT-NEXT: ModuleHandle: 0x3000
+# IMPORT-NEXT: ImportAddressTable: 0x3008
+# IMPORT-NEXT: ImportNameTable: 0x2040
+# IMPORT-NEXT: BoundDelayImportTable: 0x0
+# IMPORT-NEXT: UnloadDelayImportTable: 0x0
+# IMPORT-NEXT: Import {
+# IMPORT-NEXT: Symbol: function (0)
+# IMPORT-NEXT: Address: 0x401019
+# IMPORT-NEXT: }
+# IMPORT-NEXT: }
+#
+# BASEREL: BaseReloc [
+# BASEREL-NEXT: Entry {
+# BASEREL-NEXT: Type: ARM_MOV32(T)
+# BASEREL-NEXT: Address: 0x1000
+# BASEREL-NEXT: }
+# BASEREL-NEXT: Entry {
+# BASEREL-NEXT: Type: ARM_MOV32(T)
+# BASEREL-NEXT: Address: 0x100C
+# BASEREL-NEXT: }
+# BASEREL-NEXT: Entry {
+# BASEREL-NEXT: Type: ARM_MOV32(T)
+# BASEREL-NEXT: Address: 0x1018
+# BASEREL-NEXT: }
+# BASEREL-NEXT: Entry {
+# BASEREL-NEXT: Type: ARM_MOV32(T)
+# BASEREL-NEXT: Address: 0x102E
+# BASEREL-NEXT: }
+# BASEREL-NEXT: Entry {
+# BASEREL-NEXT: Type: HIGHLOW
+# BASEREL-NEXT: Address: 0x3008
+# BASEREL-NEXT: }
+# BASEREL-NEXT: Entry {
+# BASEREL-NEXT: Type: ABSOLUTE
+# BASEREL-NEXT: Address: 0x3000
+# BASEREL-NEXT: }
+# BASEREL-NEXT: ]
+#
+# DISASM: 401018: 43 f2 08 0c movw r12, #12296
+# DISASM-NEXT: 40101c: c0 f2 40 0c movt r12, #64
+# DISASM-NEXT: 401020: 2d e9 0f 48 push.w {r0, r1, r2, r3, r11, lr}
+# DISASM-NEXT: 401024: 0d f2 10 0b addw r11, sp, #16
+# DISASM-NEXT: 401028: 2d ed 10 0b vpush {d0, d1, d2, d3, d4, d5, d6, d7}
+# DISASM-NEXT: 40102c: 61 46 mov r1, r12
+# DISASM-NEXT: 40102e: 42 f2 00 00 movw r0, #8192
+# DISASM-NEXT: 401032: c0 f2 40 00 movt r0, #64
+# DISASM-NEXT: 401036: ff f7 e3 ff bl #-58
+# DISASM-NEXT: 40103a: 84 46 mov r12, r0
+# DISASM-NEXT: 40103c: bd ec 10 0b vpop {d0, d1, d2, d3, d4, d5, d6, d7}
+# DISASM-NEXT: 401040: bd e8 0f 48 pop.w {r0, r1, r2, r3, r11, lr}
+# DISASM-NEXT: 401044: 60 47 bx r12
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_ARMNT
+ Characteristics: [ ]
+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: 4
+ SectionData: 40F20000C0F2000000680047
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: __imp_function
+ Type: 17
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 12
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 1
+ - Name: mainCRTStartup
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: __imp_function
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/delayimports32.test b/test/COFF/delayimports32.test
index 9c4fcae5b..53aadbb6a 100644
--- a/test/COFF/delayimports32.test
+++ b/test/COFF/delayimports32.test
@@ -1,3 +1,4 @@
+# REQUIRES: x86
# RUN: yaml2obj < %p/Inputs/hello32.yaml > %t.obj
# RUN: lld-link %t.obj %p/Inputs/std32.lib /subsystem:console \
# RUN: /entry:main@0 /alternatename:___delayLoadHelper2@8=_main@0 \
diff --git a/test/COFF/dllexport-mingw.s b/test/COFF/dllexport-mingw.s
new file mode 100644
index 000000000..06c2e2258
--- /dev/null
+++ b/test/COFF/dllexport-mingw.s
@@ -0,0 +1,19 @@
+# REQEUIRES: x86
+
+# RUN: llvm-mc -triple=i686-windows-gnu %s -filetype=obj -o %t.obj
+
+# RUN: lld-link -lldmingw -dll -out:%t.dll -entry:main %t.obj -implib:%t.lib
+# RUN: llvm-readobj %t.lib | FileCheck %s
+
+# CHECK: Symbol: __imp__func
+# CHECK: Symbol: _func
+
+.global _main
+.global _func
+.text
+_main:
+ ret
+_func:
+ ret
+.section .drectve
+.ascii "-export:func"
diff --git a/test/COFF/duplicate.test b/test/COFF/duplicate.test
new file mode 100644
index 000000000..c2f743ebc
--- /dev/null
+++ b/test/COFF/duplicate.test
@@ -0,0 +1,12 @@
+RUN: llc -mtriple x86_64-windows-msvc -filetype obj -o alpha.obj %S/Inputs/alpha.ll
+RUN: llc -mtriple x86_64-windows-msvc -filetype obj -o beta.obj %S/Inputs/beta.ll
+RUN: lld-link /out:alpha.dll /dll alpha.obj /implib:alpha.lib
+RUN: not lld-link /out:beta.dll /dll alpha.obj beta.obj alpha.lib 2>&1 | FileCheck %s -check-prefix CHECK-ALPHA
+
+CHECK-ALPHA: error: duplicate symbol: f in {{.*}}alpha.obj and in alpha.dll
+
+RUN: llc -mtriple x86_64-windows-msvc -filetype obj -o gamma.obj %S/Inputs/gamma.ll
+RUN: not lld-link /out:gamma.exe /subsystem:console /entry:mainCRTStartup gamma.obj alpha.lib 2>&1 | FileCheck %s -check-prefix CHECK-GAMMA
+
+CHECK-GAMMA: error: duplicate symbol: __imp_f in {{.*}}gamma.obj and in alpha.dll
+
diff --git a/test/COFF/entry-inference.test b/test/COFF/entry-inference.test
index 2eb9a6863..294870bf4 100644
--- a/test/COFF/entry-inference.test
+++ b/test/COFF/entry-inference.test
@@ -14,10 +14,10 @@
# RUN: not lld-link /out:%t.exe %t.obj > %t.log 2>&1
# RUN: FileCheck -check-prefix=WWINMAIN %s < %t.log
-# MAIN: <root>: undefined symbol: mainCRTStartup
-# WMAIN: <root>: undefined symbol: wmainCRTStartup
-# WINMAIN: <root>: undefined symbol: WinMainCRTStartup
-# WWINMAIN: <root>: undefined symbol: wWinMainCRTStartup
+# MAIN: error: <root>: undefined symbol: mainCRTStartup
+# WMAIN: error: <root>: undefined symbol: wmainCRTStartup
+# WINMAIN: error: <root>: undefined symbol: WinMainCRTStartup
+# WWINMAIN: error: <root>: undefined symbol: wWinMainCRTStartup
--- !COFF
header:
diff --git a/test/COFF/entry-mangled.test b/test/COFF/entry-mangled.test
index acf54ba07..1140e8298 100644
--- a/test/COFF/entry-mangled.test
+++ b/test/COFF/entry-mangled.test
@@ -1,3 +1,4 @@
+# REQUIRES: x86
# RUN: yaml2obj < %s > %t.obj
# RUN: lld-link /out:%t.exe /entry:main %t.obj
# RUN: llvm-as -o %t.lto.obj %S/Inputs/entry-mangled.ll
diff --git a/test/COFF/entrylib.ll b/test/COFF/entrylib.ll
index 4ffa42c44..602b4ff63 100644
--- a/test/COFF/entrylib.ll
+++ b/test/COFF/entrylib.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; RUN: llvm-as -o %t.obj %s
; RUN: rm -f %t.lib
; RUN: llvm-ar cru %t.lib %t.obj
diff --git a/test/COFF/export-all.s b/test/COFF/export-all.s
new file mode 100644
index 000000000..6d2a64094
--- /dev/null
+++ b/test/COFF/export-all.s
@@ -0,0 +1,56 @@
+# REQEUIRES: x86
+
+# RUN: llvm-mc -triple=i686-windows-gnu %s -filetype=obj -o %t.obj
+
+# RUN: lld-link -lldmingw -dll -out:%t.dll -entry:DllMainCRTStartup@12 %t.obj -implib:%t.lib
+# RUN: llvm-readobj -coff-exports %t.dll | FileCheck %s
+
+# CHECK-NOT: Name: DllMainCRTStartup
+# CHECK: Name: foobar
+
+.global _foobar
+.global _DllMainCRTStartup@12
+.text
+_DllMainCRTStartup@12:
+ ret
+_foobar:
+ ret
+
+# Test specifying -export-all-symbols, on an object file that contains
+# dllexport directive for some of the symbols.
+
+# RUN: yaml2obj < %p/Inputs/export.yaml > %t.obj
+#
+# RUN: lld-link -out:%t.dll -dll %t.obj -lldmingw -export-all-symbols -output-def:%t.def
+# RUN: llvm-readobj -coff-exports %t.dll | FileCheck -check-prefix=CHECK2 %s
+# RUN: cat %t.def | FileCheck -check-prefix=CHECK2-DEF %s
+
+# Note, this will actually export _DllMainCRTStartup as well, since
+# it uses the standard spelling in this object file, not the MinGW one.
+
+# CHECK2: Name: exportfn1
+# CHECK2: Name: exportfn2
+# CHECK2: Name: exportfn3
+
+# CHECK2-DEF: EXPORTS
+# CHECK2-DEF: exportfn1 @3
+# CHECK2-DEF: exportfn2 @4
+# CHECK2-DEF: exportfn3 @5
+
+# Test ignoring certain object files and libs.
+
+# RUN: echo -e ".global foobar\n.global DllMainCRTStartup\n.text\nDllMainCRTStartup:\nret\nfoobar:\ncall mingwfunc\ncall crtfunc\nret\n" > %t.main.s
+# RUN: llvm-mc -triple=x86_64-windows-gnu %t.main.s -filetype=obj -o %t.main.obj
+# RUN: mkdir -p %T/libs
+# RUN: echo -e ".global mingwfunc\n.text\nmingwfunc:\nret\n" > %T/libs/mingwfunc.s
+# RUN: llvm-mc -triple=x86_64-windows-gnu %T/libs/mingwfunc.s -filetype=obj -o %T/libs/mingwfunc.o
+# RUN: llvm-ar rcs %T/libs/libmingwex.a %T/libs/mingwfunc.o
+# RUN: echo -e ".global crtfunc\n.text\ncrtfunc:\nret\n" > %T/libs/crtfunc.s
+# RUN: llvm-mc -triple=x86_64-windows-gnu %T/libs/crtfunc.s -filetype=obj -o %T/libs/crt2.o
+# RUN: lld-link -out:%t.dll -dll -entry:DllMainCRTStartup %t.main.obj -lldmingw %T/libs/crt2.o %T/libs/libmingwex.a -output-def:%t.def
+# RUN: echo "EOF" >> %t.def
+# RUN: cat %t.def | FileCheck -check-prefix=CHECK-EXCLUDE %s
+
+# CHECK-EXCLUDE: EXPORTS
+# CHECK-EXCLUDE-NEXT: foobar @1
+# CHECK-EXCLUDE-NEXT: EOF
diff --git a/test/COFF/export-arm64.yaml b/test/COFF/export-arm64.yaml
new file mode 100644
index 000000000..ad9a96db4
--- /dev/null
+++ b/test/COFF/export-arm64.yaml
@@ -0,0 +1,70 @@
+# REQUIRES: aarch64
+
+# RUN: yaml2obj < %s > %t.obj
+# RUN: lld-link /out:%t.dll /dll %t.obj /export:exportfn1 /export:exportfn2 /implib:%t.lib
+# RUN: llvm-objdump -p %t.dll | FileCheck %s
+# RUN: llvm-objdump -r %t.lib | FileCheck %s -check-prefix=RELOCS
+
+# CHECK: Export Table:
+# CHECK: DLL name: export-arm64.yaml.tmp.dll
+# CHECK: Ordinal RVA Name
+# CHECK-NEXT: 0 0
+# CHECK-NEXT: 1 0x1008 exportfn1
+# CHECK-NEXT: 2 0x1010 exportfn2
+# CHECK-NEXT: 3 0x1018 exportfn3
+
+# RELOCS: IMAGE_REL_ARM64_ADDR32NB .idata$6
+# RELOCS: IMAGE_REL_ARM64_ADDR32NB .idata$4
+# RELOCS: IMAGE_REL_ARM64_ADDR32NB .idata$5
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_ARM64
+ Characteristics: []
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_PURGEABLE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: e0031f2ac0035fd6e0031f2ac0035fd6e0031f2ac0035fd6e0031f2ac0035fd6
+ - Name: .drectve
+ Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
+ Alignment: 1
+ SectionData: 2f6578706f72743a6578706f7274666e3300 # /export:exportfn3
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 32
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 1
+ - Name: _DllMainCRTStartup
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: exportfn1
+ Value: 8
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: exportfn2
+ Value: 16
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: exportfn3
+ Value: 24
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/export-armnt.yaml b/test/COFF/export-armnt.yaml
new file mode 100644
index 000000000..461d5a033
--- /dev/null
+++ b/test/COFF/export-armnt.yaml
@@ -0,0 +1,72 @@
+# REQUIRES: arm
+
+# RUN: yaml2obj < %s > %t.obj
+#
+# RUN: lld-link /out:%t.dll /dll %t.obj /export:exportfn1 /export:exportfn2
+# RUN: llvm-objdump -p %t.dll | FileCheck %s
+
+# CHECK: Export Table:
+# CHECK: DLL name: export-armnt.yaml.tmp.dll
+# CHECK: Ordinal RVA Name
+# CHECK-NEXT: 0 0
+# CHECK-NEXT: 1 0x1005 exportfn1
+# CHECK-NEXT: 2 0x1009 exportfn2
+# CHECK-NEXT: 3 0x1009 exportfn3
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_ARMNT
+ Characteristics: []
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: 704700bf704700bf704700bf
+ - Name: .drectve
+ Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
+ Alignment: 1
+ SectionData: 2f6578706f72743a6578706f7274666e3300 # /export:exportfn3
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 12
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: _DllMainCRTStartup
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: exportfn1
+ Value: 4
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: exportfn2
+ Value: 8
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: exportfn3
+ Value: 8
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '?mangled@@YAHXZ'
+ Value: 8
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/export.test b/test/COFF/export.test
index e4a094ce6..174f4ac55 100644
--- a/test/COFF/export.test
+++ b/test/COFF/export.test
@@ -39,8 +39,9 @@ CHECK3-NEXT: 4 0
CHECK3-NEXT: 5 0x1008
CHECK3-NEXT: 6 0x1010 exportfn2
-# RUN: lld-link /out:%t.dll /dll %t.obj /export:f1=exportfn1 /export:f2=exportfn2
+# RUN: lld-link /out:%t.dll /dll %t.obj /export:f1=exportfn1 /export:f2=exportfn2 /implib:%t.lib
# RUN: llvm-objdump -p %t.dll | FileCheck -check-prefix=CHECK4 %s
+# RUN: llvm-nm %t.lib | FileCheck -check-prefix=CHECK4-NM %s
CHECK4: Export Table:
CHECK4: DLL name: export.test.tmp.dll
@@ -49,6 +50,8 @@ CHECK4-NEXT: 0 0
CHECK4-NEXT: 1 0x1010 exportfn3
CHECK4-NEXT: 2 0x1008 f1
CHECK4-NEXT: 3 0x1010 f2
+CHECK4-NM: 00000000 T f1
+CHECK4-NM: 00000000 T f2
# RUN: echo "EXPORTS exportfn1 @3" > %t.def
# RUN: echo "fn2=exportfn2 @2" >> %t.def
diff --git a/test/COFF/force.test b/test/COFF/force.test
index 80bd27555..b96c1f84c 100644
--- a/test/COFF/force.test
+++ b/test/COFF/force.test
@@ -1,10 +1,11 @@
# RUN: yaml2obj < %s > %t.obj
# RUN: not lld-link /out:%t.exe /entry:main %t.obj >& %t.log
-# RUN: FileCheck %s < %t.log
+# RUN: FileCheck -check-prefix=ERROR %s < %t.log
# RUN: lld-link /out:%t.exe /entry:main %t.obj /force >& %t.log
-# RUN: FileCheck %s < %t.log
+# RUN: FileCheck -check-prefix=WARN %s < %t.log
-# CHECK: .obj: undefined symbol: foo
+# ERROR: error: {{.*}}.obj: undefined symbol: foo
+# WARN: warning: {{.*}}.obj: undefined symbol: foo
--- !COFF
header:
diff --git a/test/COFF/guardcf.test b/test/COFF/guardcf.test
new file mode 100644
index 000000000..4f99d7053
--- /dev/null
+++ b/test/COFF/guardcf.test
@@ -0,0 +1,74 @@
+# RUN: yaml2obj < %s > %t.obj
+# RUN: lld-link /entry:main /out:%t.exe %t.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: 4
+ 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: main
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: __guard_fids_count
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: __guard_fids_table
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: __guard_flags
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: __guard_iat_count
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: __guard_iat_table
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: __guard_longjmp_count
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: __guard_longjmp_table
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/hello32.test b/test/COFF/hello32.test
index 66b52378a..feb65f39d 100644
--- a/test/COFF/hello32.test
+++ b/test/COFF/hello32.test
@@ -21,7 +21,8 @@ HEADER-NEXT: IMAGE_FILE_EXECUTABLE_IMAGE (0x2)
HEADER-NEXT: ]
HEADER-NEXT: }
HEADER-NEXT: ImageOptionalHeader {
-HEADER-NEXT: MajorLinkerVersion: 0
+HEADER-NEXT: Magic: 0x10B
+HEADER-NEXT: MajorLinkerVersion: 14
HEADER-NEXT: MinorLinkerVersion: 0
HEADER-NEXT: SizeOfCode: 512
HEADER-NEXT: SizeOfInitializedData: 1536
@@ -41,10 +42,9 @@ HEADER-NEXT: MinorSubsystemVersion: 0
HEADER-NEXT: SizeOfImage: 16896
HEADER-NEXT: SizeOfHeaders: 512
HEADER-NEXT: Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI (0x3)
-HEADER-NEXT: Characteristics [ (0x9940)
+HEADER-NEXT: Characteristics [ (0x9140)
HEADER-NEXT: IMAGE_DLL_CHARACTERISTICS_APPCONTAINER (0x1000)
HEADER-NEXT: IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE (0x40)
-HEADER-NEXT: IMAGE_DLL_CHARACTERISTICS_NO_BIND (0x800)
HEADER-NEXT: IMAGE_DLL_CHARACTERISTICS_NX_COMPAT (0x100)
HEADER-NEXT: IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE (0x8000)
HEADER-NEXT: ]
diff --git a/test/COFF/icf-associative.test b/test/COFF/icf-associative.test
index 1c63b05d8..bfaeabb4d 100644
--- a/test/COFF/icf-associative.test
+++ b/test/COFF/icf-associative.test
@@ -16,7 +16,7 @@ sections:
Alignment: 16
SectionData: 4883EC28E8000000004883C428C3
- - Name: '.debug$S'
+ - Name: '.debug_blah'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
Alignment: 1
SectionData: 0000000000000000000000000000
@@ -26,7 +26,7 @@ sections:
Alignment: 16
SectionData: 4883EC28E8000000004883C428C3
- - Name: '.debug$S'
+ - Name: '.debug_blah'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
Alignment: 1
SectionData: FFFFFFFFFFFFFFFFFFFFFFFFFFFF
@@ -46,7 +46,7 @@ symbols:
Number: 0
Selection: IMAGE_COMDAT_SELECT_ANY
- - Name: '.debug$S'
+ - Name: '.debug_blah'
Value: 0
SectionNumber: 2
SimpleType: IMAGE_SYM_TYPE_NULL
@@ -74,7 +74,7 @@ symbols:
Number: 0
Selection: IMAGE_COMDAT_SELECT_ANY
- - Name: '.debug$S'
+ - Name: '.debug_blah'
Value: 0
SectionNumber: 4
SimpleType: IMAGE_SYM_TYPE_NULL
diff --git a/test/COFF/implib-name.test b/test/COFF/implib-name.test
new file mode 100644
index 000000000..81b5b2584
--- /dev/null
+++ b/test/COFF/implib-name.test
@@ -0,0 +1,71 @@
+# RUN: mkdir -p %T
+# RUN: llvm-mc -triple x86_64-unknown-windows-msvc -filetype obj -o %T/object.obj %S/Inputs/object.s
+
+# RUN: lld-link /dll /machine:x64 /def:%S/Inputs/named.def /out:%T/library.dll %T/object.obj /entry:f /subsystem:CONSOLE
+# RUN: llvm-ar t %T/library.lib | FileCheck %s -check-prefix CHECK-DEFAULT-DLL-EXT
+
+# RUN: lld-link /machine:x64 /def:%S/Inputs/named.def /out:%T/library.lib
+# RUN: llvm-ar t %T/library.lib | FileCheck %s -check-prefix CHECK-DEFAULT-DLL-EXT
+
+CHECK-DEFAULT-DLL-EXT: library.dll
+CHECK-DEFAULT-DLL-EXT: library.dll
+CHECK-DEFAULT-DLL-EXT: library.dll
+CHECK-DEFAULT-DLL-EXT: library.dll
+
+# RUN: lld-link /machine:x64 /def:%S/Inputs/named.def /out:%T/library.exe %T/object.obj /entry:f /subsystem:CONSOLE
+# RUN: llvm-ar t %T/library.lib | FileCheck %s -check-prefix CHECK-DEFAULT-EXE-EXT
+
+CHECK-DEFAULT-EXE-EXT: library.exe
+CHECK-DEFAULT-EXE-EXT: library.exe
+CHECK-DEFAULT-EXE-EXT: library.exe
+CHECK-DEFAULT-EXE-EXT: library.exe
+
+# RUN: lld-link /dll /machine:x64 /def:%S/Inputs/extension.def /out:%T/extension.dll /entry:f /subsystem:CONSOLE
+# RUN: llvm-ar t %T/extension.lib | FileCheck %s -check-prefix CHECK-EXTENSION
+
+# RUN: lld-link /machine:x64 /def:%S/Inputs/extension.def /out:%T/extension.exe /entry:f /subsystem:CONSOLE
+# RUN: llvm-ar t %T/extension.lib | FileCheck %s -check-prefix CHECK-EXTENSION
+
+# RUN: lld-link /machine:x64 /def:%S/Inputs/extension.def /out:%T/extension.lib
+# RUN: llvm-ar t %T/extension.lib | FileCheck %s -check-prefix CHECK-EXTENSION
+
+CHECK-EXTENSION: library.ext
+CHECK-EXTENSION: library.ext
+CHECK-EXTENSION: library.ext
+CHECK-EXTENSION: library.ext
+
+# RUN: lld-link /dll /machine:x64 /def:%S/Inputs/default.def /out:%T/default.dll /entry:f /subsystem:CONSOLE
+# RUN: llvm-ar t %T/default.lib | FileCheck %s -check-prefix CHECK-OUTPUT-NAME-DLL
+
+# RUN: lld-link /machine:x64 /def:%S/Inputs/default.def /out:%T/default.lib
+# RUN: llvm-ar t %T/default.lib | FileCheck %s -check-prefix CHECK-OUTPUT-NAME-DLL
+
+CHECK-OUTPUT-NAME-DLL: default.dll
+CHECK-OUTPUT-NAME-DLL: default.dll
+CHECK-OUTPUT-NAME-DLL: default.dll
+CHECK-OUTPUT-NAME-DLL: default.dll
+
+# RUN: lld-link /machine:x64 /def:%S/Inputs/default.def /out:%T/default.exe %T/object.obj /entry:f /subsystem:CONSOLE
+# RUN: llvm-ar t %T/default.lib | FileCheck %s -check-prefix CHECK-OUTPUT-NAME-EXE
+
+CHECK-OUTPUT-NAME-EXE: default.exe
+CHECK-OUTPUT-NAME-EXE: default.exe
+CHECK-OUTPUT-NAME-EXE: default.exe
+CHECK-OUTPUT-NAME-EXE: default.exe
+
+# RUN: lld-link /machine:x64 /out:%T/default.exe %T/object.obj /entry:f /subsystem:CONSOLE
+# RUN: llvm-ar t %T/default.lib | FileCheck %s -check-prefix CHECK-NODEF-EXE
+
+CHECK-NODEF-EXE: default.exe
+CHECK-NODEF-EXE: default.exe
+CHECK-NODEF-EXE: default.exe
+CHECK-NODEF-EXE: default.exe
+
+# RUN: lld-link /machine:x64 /dll /out:%T/default.dll %T/object.obj /entry:f /subsystem:CONSOLE
+# RUN: llvm-ar t %T/default.lib | FileCheck %s -check-prefix CHECK-NODEF-DLL
+
+CHECK-NODEF-DLL: default.dll
+CHECK-NODEF-DLL: default.dll
+CHECK-NODEF-DLL: default.dll
+CHECK-NODEF-DLL: default.dll
+
diff --git a/test/COFF/imports.test b/test/COFF/imports.test
index 584c24eb1..326bfbebb 100644
--- a/test/COFF/imports.test
+++ b/test/COFF/imports.test
@@ -1,3 +1,4 @@
+# REQUIRES: x86
# Verify that the lld can handle .lib files and emit .idata sections.
#
# RUN: lld-link /out:%t.exe /entry:main /subsystem:console \
diff --git a/test/COFF/include-lto.ll b/test/COFF/include-lto.ll
index 6ca32fa71..d5ae546ab 100644
--- a/test/COFF/include-lto.ll
+++ b/test/COFF/include-lto.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; RUN: llvm-as -o %t.obj %s
; RUN: lld-link /dll /out:%t.dll %t.obj
; RUN: llvm-objdump -d %t.dll | FileCheck %s
diff --git a/test/COFF/linkrepro-res.test b/test/COFF/linkrepro-res.test
new file mode 100644
index 000000000..cf0aa1636
--- /dev/null
+++ b/test/COFF/linkrepro-res.test
@@ -0,0 +1,12 @@
+# REQUIRES: x86, shell
+
+# RUN: rm -rf %t.dir
+# RUN: mkdir -p %t.dir/build
+# RUN: cd %t.dir/build
+# RUN: lld-link %p/Inputs/resource.res /subsystem:console /machine:x64 \
+# RUN: /entry:__ImageBase /linkrepro:. /out:%t.exe
+# RUN: tar xf repro.tar
+# RUN: diff %p/Inputs/resource.res repro/%:p/Inputs/resource.res
+# RUN: FileCheck %s --check-prefix=RSP < repro/response.txt
+
+# RSP: resource.res
diff --git a/test/COFF/lto-cache.ll b/test/COFF/lto-cache.ll
new file mode 100644
index 000000000..ad1a3b71f
--- /dev/null
+++ b/test/COFF/lto-cache.ll
@@ -0,0 +1,21 @@
+; REQUIRES: x86
+
+; RUN: opt -module-hash -module-summary %s -o %t.o
+; RUN: opt -module-hash -module-summary %p/Inputs/lto-cache.ll -o %t2.o
+
+; RUN: rm -Rf %t.cache && mkdir %t.cache
+; Create two files that would be removed by cache pruning due to age.
+; We should only remove files matching the pattern "llvmcache-*".
+; RUN: touch -t 197001011200 %t.cache/llvmcache-foo %t.cache/foo
+; RUN: lld-link /lldltocache:%t.cache /lldltocachepolicy:prune_after=1h /out:%t3 /entry:main %t2.o %t.o
+
+; Two cached objects, plus a timestamp file and "foo", minus the file we removed.
+; RUN: ls %t.cache | count 4
+
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc"
+
+define void @globalfunc() #0 {
+entry:
+ ret void
+}
diff --git a/test/COFF/lto-reloc-model.ll b/test/COFF/lto-reloc-model.ll
new file mode 100644
index 000000000..bea19e9ce
--- /dev/null
+++ b/test/COFF/lto-reloc-model.ll
@@ -0,0 +1,19 @@
+; RUN: llvm-as -o %t %s
+; RUN: lld-link /entry:main /subsystem:console /out:%t.exe %t
+; RUN: llvm-objdump -d %t.exe | FileCheck %s
+
+target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32"
+target triple = "i686-pc-windows-msvc"
+
+@foo = thread_local global i8 0
+
+module asm "__tls_index = 1"
+module asm "__tls_array = 2"
+
+define i8* @main() {
+ ; CHECK: movl 1, %eax
+ ; CHECK: movl %fs:2, %ecx
+ ; CHECK: movl (%ecx,%eax,4), %eax
+ ; CHECK: leal (%eax), %eax
+ ret i8* @foo
+}
diff --git a/test/COFF/manifest.test b/test/COFF/manifest.test
index 33e80e75a..31587b43e 100644
--- a/test/COFF/manifest.test
+++ b/test/COFF/manifest.test
@@ -1,6 +1,10 @@
# RUN: yaml2obj %p/Inputs/ret42.yaml > %t.obj
+# RUN: rm -f %t.exe.manifest
# RUN: lld-link /out:%t.exe /entry:main %t.obj
+# RUN: test ! -e %t.exe.manifest
+
+# RUN: lld-link /manifest /out:%t.exe /entry:main %t.obj
# RUN: FileCheck -check-prefix=MANIFEST %s < %t.exe.manifest
MANIFEST: <?xml version="1.0" standalone="yes"?>
@@ -15,7 +19,7 @@ MANIFEST: </security>
MANIFEST: </trustInfo>
MANIFEST: </assembly>
-# RUN: lld-link /out:%t.exe /entry:main \
+# RUN: lld-link /out:%t.exe /entry:main /manifest \
# RUN: /manifestuac:"level='requireAdministrator' uiAccess='true'" %t.obj
# RUN: FileCheck -check-prefix=UAC %s < %t.exe.manifest
@@ -31,6 +35,7 @@ UAC: </security>
UAC: </trustInfo>
UAC: </assembly>
+# /manifestdependency implies /manifest. (/manifestuac doesn't.)
# RUN: lld-link /out:%t.exe /entry:main \
# RUN: /manifestdependency:"foo='bar'" %t.obj
# RUN: FileCheck -check-prefix=DEPENDENCY %s < %t.exe.manifest
@@ -52,10 +57,24 @@ DEPENDENCY: </dependentAssembly>
DEPENDENCY: </dependency>
DEPENDENCY: </assembly>
-# RUN: lld-link /out:%t.exe /entry:main /manifestuac:no %t.obj
+# RUN: lld-link /manifest /out:%t.exe /entry:main /manifestuac:no \
+# RUN: /manifestdependency:"foo='bar'" %t.obj
# RUN: FileCheck -check-prefix=NOUAC %s < %t.exe.manifest
NOUAC: <?xml version="1.0" standalone="yes"?>
NOUAC: <assembly xmlns="urn:schemas-microsoft-com:asm.v1"
NOUAC: manifestVersion="1.0">
+NOUAC: <dependency>
+NOUAC: <dependentAssembly>
+NOUAC: <assemblyIdentity foo='bar' />
+NOUAC: </dependentAssembly>
+NOUAC: </dependency>
NOUAC: </assembly>
+
+# RUN: lld-link /manifest /out:%t.exe /entry:main /manifestuac:no %t.obj
+# RUN: FileCheck -check-prefix=NOUACNODEP %s < %t.exe.manifest
+
+NOUACNODEP: <?xml version="1.0" standalone="yes"?>
+NOUACNODEP: <assembly xmlns="urn:schemas-microsoft-com:asm.v1"
+NOUACNODEP: manifestVersion="1.0">
+NOUACNODEP: </assembly>
diff --git a/test/COFF/manifestinput-error.test b/test/COFF/manifestinput-error.test
new file mode 100644
index 000000000..eca7d0d03
--- /dev/null
+++ b/test/COFF/manifestinput-error.test
@@ -0,0 +1,10 @@
+# UNSUPPORTED: manifest_tool
+# UNSUPPORTED: libxml2
+
+# RUN: yaml2obj %p/Inputs/ret42.yaml > %t.obj
+# RUN: not lld-link /out:%t.exe /entry:main \
+# RUN: /manifest:embed \
+# RUN: /manifestuac:"level='requireAdministrator'" \
+# RUN: /manifestinput:%p/Inputs/manifestinput.test %t.obj 2>&1 | FileCheck %s
+
+# CHECK: error: unable to find mt.exe in PATH: No such file or directory
diff --git a/test/COFF/manifestinput-nowarning.test b/test/COFF/manifestinput-nowarning.test
new file mode 100644
index 000000000..d8dd864d5
--- /dev/null
+++ b/test/COFF/manifestinput-nowarning.test
@@ -0,0 +1,11 @@
+# UNSUPPORTED: libxml2
+# REQUIRES: manifest_tool
+
+# RUN: yaml2obj %p/Inputs/ret42.yaml > %t.obj
+# RUN: lld-link /out:%t.exe /entry:main \
+# RUN: /manifest:embed \
+# RUN: /manifestuac:"level='requireAdministrator'" \
+# RUN: /manifestinput:%p/Inputs/manifestinput.test %t.obj | \
+# RUN: FileCheck -allow-empty %s
+
+# CHECK-NOT: warning: error with internal manifest tool: no libxml2
diff --git a/test/COFF/manifestinput.test b/test/COFF/manifestinput.test
index 7fc37c98f..024e7881e 100644
--- a/test/COFF/manifestinput.test
+++ b/test/COFF/manifestinput.test
@@ -1,10 +1,26 @@
-# REQUIRES: win_mt
+# REQUIRES: manifest_tool
# RUN: yaml2obj %p/Inputs/ret42.yaml > %t.obj
# RUN: lld-link /out:%t.exe /entry:main \
+# RUN: /manifest:embed \
# RUN: /manifestuac:"level='requireAdministrator'" \
# RUN: /manifestinput:%p/Inputs/manifestinput.test %t.obj
-# RUN: FileCheck %s < %t.exe.manifest
+# RUN: llvm-readobj -coff-resources -file-headers %t.exe | FileCheck %s \
+# RUN: -check-prefix TEST_EMBED
-CHECK: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-CHECK: <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"><dependency><dependentAssembly><assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity></dependentAssembly></dependency><trustInfo><security><requestedPrivileges><requestedExecutionLevel level="requireAdministrator" uiAccess="false"></requestedExecutionLevel></requestedPrivileges></security></trustInfo></assembly>
+TEST_EMBED: ResourceTableRVA: 0x1000
+TEST_EMBED-NEXT: ResourceTableSize: 0x298
+TEST_EMBED-DAG: Resources [
+TEST_EMBED-NEXT: Total Number of Resources: 1
+TEST_EMBED-DAG: Number of String Entries: 0
+TEST_EMBED-NEXT: Number of ID Entries: 1
+TEST_EMBED-NEXT: Type: kRT_MANIFEST (ID 24) [
+TEST_EMBED-NEXT: Table Offset: 0x18
+TEST_EMBED-NEXT: Number of String Entries: 0
+TEST_EMBED-NEXT: Number of ID Entries: 1
+TEST_EMBED-NEXT: Name: (ID 1) [
+TEST_EMBED-NEXT: Table Offset: 0x30
+TEST_EMBED-NEXT: Number of String Entries: 0
+TEST_EMBED-NEXT: Number of ID Entries: 1
+TEST_EMBED-NEXT: Language: (ID 1033) [
+TEST_EMBED-NEXT: Entry Offset: 0x48
diff --git a/test/COFF/msvclto-archive.ll b/test/COFF/msvclto-archive.ll
index 047b19e76..334565a1b 100644
--- a/test/COFF/msvclto-archive.ll
+++ b/test/COFF/msvclto-archive.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
;; Make sure we re-create archive files to strip bitcode files.
;; Do not create empty archives because the MSVC linker
diff --git a/test/COFF/msvclto-order.ll b/test/COFF/msvclto-order.ll
index 8991dce4a..6f569af4a 100644
--- a/test/COFF/msvclto-order.ll
+++ b/test/COFF/msvclto-order.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; RUN: opt -thinlto-bc %s -o %t.obj
; RUN: llc -filetype=obj %S/Inputs/msvclto-order-a.ll -o %T/msvclto-order-a.obj
; RUN: llvm-ar crs %T/msvclto-order-a.lib %T/msvclto-order-a.obj
diff --git a/test/COFF/msvclto.ll b/test/COFF/msvclto.ll
index 7fa9c5471..66fabeb80 100644
--- a/test/COFF/msvclto.ll
+++ b/test/COFF/msvclto.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; RUN: llvm-as -o %t.obj %s
; RUN: mkdir -p %t.dir
; RUN: llvm-mc -triple=x86_64-pc-windows-msvc -filetype=obj -o %t.dir/bitcode.obj %p/Inputs/msvclto.s
diff --git a/test/COFF/nodefaultlib.test b/test/COFF/nodefaultlib.test
index 867dc8f18..c0f8d50fc 100644
--- a/test/COFF/nodefaultlib.test
+++ b/test/COFF/nodefaultlib.test
@@ -19,9 +19,9 @@
# RUN: /nodefaultlib:std64.lib >& %t.log || true
# RUN: FileCheck -check-prefix=CHECK3 %s < %t.log
-CHECK1: hello64.obj: {{[Nn]}}o such file or directory
-CHECK2: hello64: {{[Nn]}}o such file or directory
-CHECK3: hello64.obj: undefined symbol: MessageBoxA
+CHECK1: error: could not open hello64.obj: {{[Nn]}}o such file or directory
+CHECK2: error: could not open hello64: {{[Nn]}}o such file or directory
+CHECK3: error: {{.*}}hello64.obj: undefined symbol: MessageBoxA
# RUN: lld-link /libpath:%T /out:%t.exe /entry:main \
# RUN: /subsystem:console hello64.obj /defaultlib:std64.lib
diff --git a/test/COFF/options.test b/test/COFF/options.test
index a23da1971..39f944bed 100644
--- a/test/COFF/options.test
+++ b/test/COFF/options.test
@@ -2,7 +2,13 @@
# RUN: lld-link /out:%t.exe /entry:main %t.obj
# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=BIND %s
-BIND: IMAGE_DLL_CHARACTERISTICS_NO_BIND
+# RUN: lld-link /allowbind /out:%t.exe /entry:main %t.obj
+# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=BIND %s
+BIND-NOT: IMAGE_DLL_CHARACTERISTICS_NO_BIND
+
+# RUN: lld-link /allowbind:no /out:%t.exe /entry:main %t.obj
+# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=NOBIND %s
+NOBIND: IMAGE_DLL_CHARACTERISTICS_NO_BIND
# RUN: lld-link /out:%t.exe /entry:main %t.obj
# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=ISO %s
diff --git a/test/COFF/pdb-comdat.test b/test/COFF/pdb-comdat.test
new file mode 100644
index 000000000..a3283d206
--- /dev/null
+++ b/test/COFF/pdb-comdat.test
@@ -0,0 +1,109 @@
+Consider this example program with an inline function "foo":
+
+==> foo.h <==
+extern int global;
+__inline void foo() {
+ ++global;
+}
+void bar();
+==> pdb_comdat_main.c <==
+#include "foo.h"
+int main(void) {
+ foo();
+ bar();
+ return 42;
+}
+==> pdb_comdat_bar.c <==
+#include "foo.h"
+void bar(void) {
+ foo();
+}
+
+Both object files will contain debug info for foo, but only the debug info from
+pdb_comdat_main.obj should be included in the PDB.
+
+RUN: rm -rf %t && mkdir -p %t && cd %t
+RUN: yaml2obj %S/Inputs/pdb_comdat_main.yaml -o pdb_comdat_main.obj
+RUN: yaml2obj %S/Inputs/pdb_comdat_bar.yaml -o pdb_comdat_bar.obj
+RUN: lld-link pdb_comdat_main.obj pdb_comdat_bar.obj -out:%t.exe -debug -pdb:%t.pdb -nodefaultlib -entry:main
+RUN: llvm-pdbutil dump -l -symbols -globals %t.pdb | FileCheck %s
+
+CHECK: Lines
+CHECK: ============================================================
+CHECK-LABEL: Mod 0000 | `{{.*}}pdb_comdat_main.obj`:
+CHECK: c:\src\llvm-project\build\pdb_comdat_main.c (MD5: F969E51BBE373436D81492EB61387F36)
+CHECK: c:\src\llvm-project\build\foo.h (MD5: D74D834EFAC3AE2B45E606A8320B1D5C)
+CHECK-LABEL: Mod 0001 | `{{.*}}pdb_comdat_bar.obj`:
+CHECK: c:\src\llvm-project\build\pdb_comdat_bar.c (MD5: 365279DB4FCBEDD721BBFC3B14A953C2)
+CHECK-NOT: c:\src\llvm-project\build\foo.h
+CHECK-LABEL: Mod 0002 | `* Linker *`:
+
+CHECK-LABEL: Global Symbols
+CHECK-NEXT: ============================================================
+CHECK-NEXT: Records
+CHECK-NEXT: 84 | S_PROCREF [size = 20] `main`
+CHECK-NEXT: module = 1, sum name = 0, offset = 120
+CHECK-NEXT: 128 | S_PROCREF [size = 20] `foo`
+CHECK-NEXT: module = 1, sum name = 0, offset = 208
+CHECK-NEXT: 148 | S_PROCREF [size = 20] `bar`
+CHECK-NEXT: module = 2, sum name = 0, offset = 120
+CHECK-NEXT: 104 | S_GDATA32 [size = 24] `global`
+CHECK-NEXT: type = 0x0074 (int), addr = 0000:0000
+CHECK-NEXT: 168 | S_GDATA32 [size = 24] `global`
+CHECK-NEXT: type = 0x0074 (int), addr = 0000:0000
+
+CHECK: Symbols
+CHECK: ============================================================
+CHECK-LABEL: Mod 0000 | `{{.*}}pdb_comdat_main.obj`:
+CHECK: 4 | S_OBJNAME [size = 56] sig=0, `C:\src\llvm-project\build\pdb_comdat_main.obj`
+CHECK: 60 | S_COMPILE3 [size = 60]
+CHECK: machine = intel x86-x64, Ver = Microsoft (R) Optimizing Compiler, language = c
+CHECK: frontend = 19.0.24215.1, backend = 19.0.24215.1
+CHECK: flags = security checks | hot patchable
+CHECK: 120 | S_GPROC32 [size = 44] `main`
+CHECK: parent = 0, end = 196, addr = 0002:0000, code size = 24
+CHECK: debug start = 4, debug end = 19, flags = none
+CHECK: 164 | S_FRAMEPROC [size = 32]
+CHECK: size = 40, padding size = 0, offset to padding = 0
+CHECK: bytes of callee saved registers = 0, exception handler addr = 0000:0000
+CHECK: flags = has async eh | opt speed
+CHECK: 196 | S_END [size = 4]
+CHECK: 200 | S_BUILDINFO [size = 8] BuildId = `0x100A`
+CHECK: 208 | S_GPROC32 [size = 44] `foo`
+CHECK: parent = 0, end = 284, addr = 0002:0032, code size = 15
+CHECK: debug start = 0, debug end = 14, flags = none
+CHECK: 252 | S_FRAMEPROC [size = 32]
+CHECK: size = 0, padding size = 0, offset to padding = 0
+CHECK: bytes of callee saved registers = 0, exception handler addr = 0000:0000
+CHECK: flags = marked inline | has async eh | opt speed
+CHECK: 284 | S_END [size = 4]
+CHECK-LABEL: Mod 0001 | `{{.*}}pdb_comdat_bar.obj`:
+CHECK: 4 | S_OBJNAME [size = 56] sig=0, `C:\src\llvm-project\build\pdb_comdat_bar.obj`
+CHECK: 60 | S_COMPILE3 [size = 60]
+CHECK: machine = intel x86-x64, Ver = Microsoft (R) Optimizing Compiler, language = c
+CHECK: frontend = 19.0.24215.1, backend = 19.0.24215.1
+CHECK: flags = security checks | hot patchable
+CHECK: 120 | S_GPROC32 [size = 44] `bar`
+CHECK: parent = 0, end = 196, addr = 0002:0048, code size = 14
+CHECK: debug start = 4, debug end = 9, flags = none
+CHECK: 164 | S_FRAMEPROC [size = 32]
+CHECK: size = 40, padding size = 0, offset to padding = 0
+CHECK: bytes of callee saved registers = 0, exception handler addr = 0000:0000
+CHECK: flags = has async eh | opt speed
+CHECK: 196 | S_END [size = 4]
+CHECK: 200 | S_BUILDINFO [size = 8] BuildId = `0x100D`
+CHECK-NOT: S_GPROC32 {{.*}} `foo`
+CHECK-LABEL: Mod 0002 | `* Linker *`:
+
+Reorder the object files and verify that the other table is selected.
+
+RUN: lld-link pdb_comdat_bar.obj pdb_comdat_main.obj -out:t.exe -debug -pdb:t.pdb -nodefaultlib -entry:main
+RUN: llvm-pdbutil dump -l t.pdb | FileCheck %s --check-prefix=REORDER
+
+REORDER-LABEL: Mod 0000 | `{{.*}}pdb_comdat_bar.obj`:
+REORDER: c:\src\llvm-project\build\pdb_comdat_bar.c (MD5: 365279DB4FCBEDD721BBFC3B14A953C2)
+REORDER: c:\src\llvm-project\build\foo.h (MD5: D74D834EFAC3AE2B45E606A8320B1D5C)
+REORDER-LABEL: Mod 0001 | `{{.*}}pdb_comdat_main.obj`:
+REORDER: c:\src\llvm-project\build\pdb_comdat_main.c
+REORDER-NOT: c:\src\llvm-project\build\foo.h
+REORDER-LABEL: Mod 0002 | `* Linker *`:
diff --git a/test/COFF/pdb-diff.test b/test/COFF/pdb-diff.test
new file mode 100644
index 000000000..17d26b603
--- /dev/null
+++ b/test/COFF/pdb-diff.test
@@ -0,0 +1,215 @@
+This test verifies that we produce PDBs compatible with MSVC in various ways.
+We check in a cl-generated object file, PDB, and original source which serve
+as the "baseline" for us to measure against. Then we link the same object
+file with LLD and compare the two PDBs. Since the baseline object file and
+PDB are already checked in, we just run LLD on the object file.
+
+RUN: rm -f %T/pdb-diff-lld.pdb %T/pdb-diff-lld.exe
+RUN: lld-link /debug /pdb:%T/pdb-diff-lld.pdb /out:%T/pdb-diff-lld.exe /nodefaultlib \
+RUN: /entry:main %S/Inputs/pdb-diff.obj
+RUN: llvm-pdbutil diff -result -values=false -left-bin-root=%S -right-bin-root=D:/src/llvm-mono/lld/test/COFF/ \
+RUN: %T/pdb-diff-lld.pdb %S/Inputs/pdb-diff-cl.pdb | FileCheck %s
+
+CHECK: ----------------------
+CHECK-NEXT: | MSF Super Block |
+CHECK-NEXT: |----------------+---|
+CHECK-NEXT: | File | |
+CHECK-NEXT: |----------------+---|
+CHECK-NEXT: | Block Size | I |
+CHECK-NEXT: |----------------+---|
+CHECK-NEXT: | Block Count |
+CHECK-NEXT: |----------------+---|
+CHECK-NEXT: | Unknown 1 | I |
+CHECK-NEXT: |----------------+---|
+CHECK-NEXT: | Directory Size |
+CHECK-NEXT: |----------------+---|
+CHECK-NEXT: ------------------------------------
+CHECK-NEXT: | Stream Directory |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | File | |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | Stream Count | I |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | Old MSF Directory | I |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | PDB Stream | I |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | TPI Stream | I |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | DBI Stream | I |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | IPI Stream | I |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | New FPO Data | {{[EI]}} |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | Section Header Data | {{[EI]}} |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | Named Stream "/names" | {{[EI]}} |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | Named Stream "/LinkInfo" | {{[EI]}} |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | Module "Inputs\pdb-diff.obj" | {{[EI]}} |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | Module "* Linker *" | {{[EI]}} |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | TPI Hash | {{[EI]}} |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | IPI Hash | {{[EI]}} |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | Public Symbol Hash | {{[EI]}} |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | Global Symbol Hash | {{[EI]}} |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | Symbol Records | {{[EI]}} |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: ------------------------------------
+CHECK-NEXT: | String Table |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | File | |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | Number of Strings | D |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | Hash Version | I |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | Byte Size |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | Signature | I |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | Empty Strings |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | {{.*}}pdb-diff.cpp | {{[EI]}} |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | $T0 $ebp = $...p $T0 8 + = | D |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: | d:\src\llvm-...er internal) | D |
+CHECK-NEXT: |------------------------------+---|
+CHECK-NEXT: ----------------------------
+CHECK-NEXT: | PDB Stream |
+CHECK-NEXT: |----------------------+---|
+CHECK-NEXT: | File | |
+CHECK-NEXT: |----------------------+---|
+CHECK-NEXT: | Stream Size |
+CHECK-NEXT: |----------------------+---|
+CHECK-NEXT: | Age | I |
+CHECK-NEXT: |----------------------+---|
+CHECK-NEXT: | Guid | D |
+CHECK-NEXT: |----------------------+---|
+CHECK-NEXT: | Signature | D |
+CHECK-NEXT: |----------------------+---|
+CHECK-NEXT: | Version | I |
+CHECK-NEXT: |----------------------+---|
+CHECK-NEXT: | Features (set) | I |
+CHECK-NEXT: |----------------------+---|
+CHECK-NEXT: | Feature | I |
+CHECK-NEXT: |----------------------+---|
+CHECK-NEXT: | Named Stream Size |
+CHECK-NEXT: |----------------------+---|
+CHECK-NEXT: | Named Streams (map) | {{[EI]}} |
+CHECK-NEXT: |----------------------+---|
+CHECK-NEXT: | /names | {{[EI]}} |
+CHECK-NEXT: |----------------------+---|
+CHECK-NEXT: | /LinkInfo | {{[EI]}} |
+CHECK-NEXT: |----------------------+---|
+CHECK-NEXT: ----------------------------------------------
+CHECK-NEXT: | DBI Stream |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | File | |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Dbi Version | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Age | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Machine | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Flags | D |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Build Major | D |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Build Minor | D |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Build Number | D |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | PDB DLL Version | D |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | PDB DLL RBLD | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | DBG (FPO) | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | DBG (Exception) | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | DBG (Fixup) | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | DBG (OmapToSrc) | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | DBG (OmapFromSrc) | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | DBG (SectionHdr) | {{[EI]}} |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | DBG (TokenRidMap) | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | DBG (Xdata) | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | DBG (Pdata) | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | DBG (NewFPO) | {{[EI]}} |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | DBG (SectionHdrOrig) | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Globals Stream | {{[EI]}} |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Publics Stream | {{[EI]}} |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Symbol Records | {{[EI]}} |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Has CTypes | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Is Incrementally Linked | D |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Is Stripped | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Module Count | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Source File Count | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Module "Inputs\pdb-diff.obj" |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - Modi | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - Obj File Name | {{[EI]}} |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - Debug Stream | {{[EI]}} |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - C11 Byte Size | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - C13 Byte Size | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - # of files | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - Pdb File Path Index | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - Source File Name Index | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - Symbol Byte Size |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | Module "* Linker *" |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - Modi | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - Obj File Name | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - Debug Stream | {{[EI]}} |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - C11 Byte Size | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - C13 Byte Size | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - # of files | I |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - Pdb File Path Index | {{[EI]}} |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - Source File Name Index | {{[EI]}} |
+CHECK-NEXT: |----------------------------------------+---|
+CHECK-NEXT: | - Symbol Byte Size |
+CHECK-NEXT: |----------------------------------------+---|
+
+
diff --git a/test/COFF/pdb-global-gc.yaml b/test/COFF/pdb-global-gc.yaml
new file mode 100644
index 000000000..e39831c70
--- /dev/null
+++ b/test/COFF/pdb-global-gc.yaml
@@ -0,0 +1,120 @@
+# RUN: yaml2obj %s -o %t.obj
+# RUN: llvm-mc %S/Inputs/pdb-global-gc.s -triple x86_64-windows-msvc -filetype=obj -o %t2.obj
+# RUN: lld-link %t.obj %t2.obj -debug -entry:main \
+# RUN: -nodefaultlib -debug -out:%t.exe -pdb:%t.pdb -verbose
+# RUN: llvm-pdbutil dump -symbols -globals %t.pdb | FileCheck %s
+
+# This tests the case where an __imp_ chunk is discarded by linker GC. The debug
+# info may refer to the __imp_ symbol still.
+
+# Compile this code with MSVC to regenerate the test case:
+# extern char __declspec(dllimport) __wc_mb_cur;
+# int discarded() { return __wc_mb_cur; }
+# int main() { return g2; }
+
+# CHECK: Global Symbols
+# CHECK-NEXT: ============================================================
+# CHECK-NEXT: Records
+# CHECK-NEXT: 20 | S_GDATA32 [size = 28] `__wc_mb_cur`
+# CHECK-NEXT: type = 0x0070 (char), addr = 0000:0000
+
+# CHECK: Symbols
+# CHECK: ============================================================
+# CHECK-NEXT: Mod 0000 | `{{.*}}pdb-global-gc.yaml.tmp.obj`:
+# CHECK-NEXT: Mod 0001 | `{{.*}}pdb-global-gc.yaml.tmp2.obj`:
+# CHECK-NEXT: Mod 0002 | `* Linker *`:
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_GDATA32
+ DataSym:
+ Type: 112
+ DisplayName: __wc_mb_cur
+ - !StringTable
+ Strings:
+ Relocations:
+ - VirtualAddress: 20
+ SymbolName: __wc_mb_cur
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 24
+ SymbolName: __wc_mb_cur
+ Type: IMAGE_REL_AMD64_SECTION
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 0FBE0500000000C3
+ Relocations:
+ - VirtualAddress: 3
+ SymbolName: __wc_mb_cur
+ Type: IMAGE_REL_AMD64_REL32
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: B82A000000C3
+symbols:
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 240
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 11
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 2906070869
+ Number: 0
+ Selection: IMAGE_COMDAT_SELECT_NODUPLICATES
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 3
+ 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
+ Selection: IMAGE_COMDAT_SELECT_NODUPLICATES
+ - Name: discarded
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: main
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: __wc_mb_cur
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/pdb-globals.test b/test/COFF/pdb-globals.test
new file mode 100644
index 000000000..b5e4f49cb
--- /dev/null
+++ b/test/COFF/pdb-globals.test
@@ -0,0 +1,42 @@
+RUN: yaml2obj %S/Inputs/pdb-globals.yaml > %t.obj
+RUN: lld-link /debug /nodefaultlib /entry:main /out:%t.exe /pdb:%t.pdb %t.obj
+RUN: llvm-pdbutil dump -symbols -globals %t.pdb | FileCheck %s
+
+# Test that we correctly distribute symbols between the globals and module
+# symbol streams. Specifically:
+# * S_UDT, S_GDATA32, and S_CONSTANT end up in the globals stream, and are
+# omitted from the module stream.
+# * S_GPROC32 and S_LPROC32 end up in the symbols stream, but S_PROCREF and
+# S_LPROCREF are added to the globals stream that refer to the module
+# stream.
+# * S_LDATA32 is copied byte for byte into both streams.
+
+
+CHECK-LABEL: Global Symbols
+CHECK-NEXT: ============================================================
+CHECK-NEXT: Records
+CHECK-NEXT: 160 | S_PROCREF [size = 28] `GlobalFunc`
+CHECK-NEXT: module = 1, sum name = 0, offset = 52
+CHECK-NEXT: 188 | S_PROCREF [size = 20] `main`
+CHECK-NEXT: module = 1, sum name = 0, offset = 108
+CHECK-NEXT: 208 | S_LPROCREF [size = 24] `LocalFunc`
+CHECK-NEXT: module = 1, sum name = 0, offset = 292
+CHECK-NEXT: 312 | S_PROCREF [size = 40] `HelloPoint::HelloPoint`
+CHECK-NEXT: module = 1, sum name = 0, offset = 376
+CHECK-NEXT: 232 | S_GDATA32 [size = 28] `__purecall`
+CHECK-NEXT: type = 0x0403 (void*), addr = 0000:0000
+CHECK-NEXT: 260 | S_GDATA32 [size = 24] `GlobalVar`
+CHECK-NEXT: type = 0x100B (const int*), addr = 0001:0000
+CHECK-NEXT: 284 | S_LDATA32 [size = 28] `ConstantVar`
+CHECK-NEXT: type = 0x100A (const int), addr = 0002:0000
+
+CHECK-LABEL: Symbols
+CHECK-NEXT: ============================================================
+CHECK-NEXT: Mod 0000
+CHECK-NOT: | S_GDATA32
+CHECK-NOT: | S_UDT
+CHECK: 52 | S_GPROC32 [size = 52] `GlobalFunc`
+CHECK: 108 | S_GPROC32 [size = 44] `main`
+CHECK: 292 | S_LPROC32 [size = 52] `LocalFunc`
+CHECK: 348 | S_LDATA32 [size = 28] `ConstantVar`
+CHECK: 376 | S_GPROC32 [size = 64] `HelloPoint::HelloPoint`
diff --git a/test/COFF/pdb-import-gc.yaml b/test/COFF/pdb-import-gc.yaml
new file mode 100644
index 000000000..db3ee7e28
--- /dev/null
+++ b/test/COFF/pdb-import-gc.yaml
@@ -0,0 +1,118 @@
+# RUN: yaml2obj %s -o %t.obj
+# RUN: lld-link %t.obj %S/Inputs/pdb-import-gc.lib -debug -entry:main \
+# RUN: -nodefaultlib -debug -out:%t.exe -pdb:%t.pdb
+# RUN: llvm-pdbutil dump -globals -symbols %t.pdb | FileCheck %s
+
+# This tests the case where an __imp_ chunk is discarded by linker GC. The debug
+# info may refer to the __imp_ symbol still.
+
+# Compile this code with MSVC to regenerate the test case:
+# extern char __declspec(dllimport) __wc_mb_cur;
+# int discarded() { return __wc_mb_cur; }
+# int main() { return g2; }
+
+# CHECK: Global Symbols
+# CHECK-NEXT: ============================================================
+# CHECK-NEXT: Records
+# CHECK-NEXT: 20 | S_GDATA32 [size = 32] `__imp___wc_mb_cur`
+# CHECK-NEXT: type = 0x0070 (char), addr = 0000:0000
+
+# CHECK: Symbols
+# CHECK-NEXT: ============================================================
+# CHECK-NEXT: Mod 0000 | `{{.*}}pdb-import-gc.yaml.tmp.obj`:
+# CHECK-NEXT: Mod 0001 | `* Linker *`:
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_GDATA32
+ DataSym:
+ Type: 112
+ DisplayName: __imp___wc_mb_cur
+ - !StringTable
+ Strings:
+ Relocations:
+ - VirtualAddress: 20
+ SymbolName: __imp___wc_mb_cur
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 24
+ SymbolName: __imp___wc_mb_cur
+ Type: IMAGE_REL_AMD64_SECTION
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 488B05000000000FBE00C3
+ Relocations:
+ - VirtualAddress: 3
+ SymbolName: __imp___wc_mb_cur
+ Type: IMAGE_REL_AMD64_REL32
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: B82A000000C3
+symbols:
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 240
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 11
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 2906070869
+ Number: 0
+ Selection: IMAGE_COMDAT_SELECT_NODUPLICATES
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 3
+ 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
+ Selection: IMAGE_COMDAT_SELECT_NODUPLICATES
+ - Name: discarded
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: main
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: __imp___wc_mb_cur
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/pdb-invalid-func-type.yaml b/test/COFF/pdb-invalid-func-type.yaml
new file mode 100644
index 000000000..219dd424c
--- /dev/null
+++ b/test/COFF/pdb-invalid-func-type.yaml
@@ -0,0 +1,146 @@
+# This test has an S_GPROC32_ID symbol with an invalid type index. Make sure we
+# keep the record, or we'll have unbalanced scopes, which is bad. This situation
+# can arise when we can't find the type server PDB.
+
+# RUN: yaml2obj %s -o %t.obj
+# RUN: lld-link %t.obj -out:%t.exe -debug -pdb:%t.pdb -nodefaultlib -entry:main
+# RUN: llvm-pdbutil dump -symbols %t.pdb | FileCheck %s
+
+# CHECK: Mod 0000 | `{{.*}}pdb-invalid-func-type.yaml.tmp.obj`:
+# CHECK: 4 | S_GPROC32 [size = 44] `main`
+# CHECK: parent = 0, end = 80, addr = 0001:0000, code size = 3
+# CHECK: 48 | S_FRAMEPROC [size = 32]
+# CHECK: 80 | S_END [size = 4]
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 3
+ DbgStart: 0
+ DbgEnd: 2
+ # Corrupt function type!
+ FunctionType: 4101
+ Flags: [ ]
+ DisplayName: main
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 0
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 3
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\t.c'
+ Lines:
+ - Offset: 0
+ LineStart: 1
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !FileChecksums
+ Checksums:
+ - FileName: 'c:\src\llvm-project\build\t.c'
+ Kind: MD5
+ Checksum: 270A878DCC1B845655B162F56C4F5020
+ - !StringTable
+ Strings:
+ - 'c:\src\llvm-project\build\t.c'
+ Relocations:
+ - VirtualAddress: 44
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 48
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 100
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 104
+ 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
+ Types:
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 0 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 116
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4096
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4097
+ Name: main
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 33C0C3
+symbols:
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 328
+ NumberOfRelocations: 4
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: '.debug$T'
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 564
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 3
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 4021952397
+ Number: 0
+ - Name: main
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/pdb-lib.s b/test/COFF/pdb-lib.s
index 073c6f08a..319d4bc0f 100644
--- a/test/COFF/pdb-lib.s
+++ b/test/COFF/pdb-lib.s
@@ -1,24 +1,28 @@
+# REQUIRES: x86
# RUN: rm -rf %t && mkdir -p %t && cd %t
# RUN: llvm-mc -filetype=obj -triple=i686-windows-msvc %s -o foo.obj
# RUN: llc %S/Inputs/bar.ll -filetype=obj -mtriple=i686-windows-msvc -o bar.obj
# RUN: llvm-lib bar.obj -out:bar.lib
# RUN: lld-link -debug -pdb:foo.pdb foo.obj bar.lib -out:foo.exe -entry:main
-# RUN: llvm-pdbutil raw -modules %t/foo.pdb | FileCheck %s
+# RUN: llvm-pdbutil dump -modules %t/foo.pdb | FileCheck %s
# Make sure that the PDB has module descriptors. foo.obj and bar.lib should be
# absolute paths, and bar.obj should be the relative path passed to llvm-lib.
# CHECK: Modules
# CHECK-NEXT: ============================================================
-# CHECK-NEXT: Mod 0000 | Name: `{{.*pdb-lib.s.tmp[/\\]foo.obj}}`:
+# CHECK-NEXT: Mod 0000 | `{{.*pdb-lib.s.tmp[/\\]foo.obj}}`:
# CHECK-NEXT: Obj: `{{.*pdb-lib.s.tmp[/\\]foo.obj}}`:
# CHECK-NEXT: debug stream: 9, # files: 0, has ec info: false
-# CHECK-NEXT: Mod 0001 | Name: `bar.obj`:
+# CHECK-NEXT: pdb file ni: 0 ``, src file ni: 0 ``
+# CHECK-NEXT: Mod 0001 | `bar.obj`:
# CHECK-NEXT: Obj: `{{.*pdb-lib.s.tmp[/\\]bar.lib}}`:
# CHECK-NEXT: debug stream: 10, # files: 0, has ec info: false
-# CHECK-NEXT: Mod 0002 | Name: `* Linker *`:
+# CHECK-NEXT: pdb file ni: 0 ``, src file ni: 0 ``
+# CHECK-NEXT: Mod 0002 | `* Linker *`:
# CHECK-NEXT: Obj: ``:
# CHECK-NEXT: debug stream: 11, # files: 0, has ec info: false
+# CHECK-NEXT: pdb file ni: 1 `{{.*foo.pdb}}`, src file ni: 0 ``
.def _main;
.scl 2;
diff --git a/test/COFF/pdb-linker-module.test b/test/COFF/pdb-linker-module.test
new file mode 100644
index 000000000..1bb57298f
--- /dev/null
+++ b/test/COFF/pdb-linker-module.test
@@ -0,0 +1,24 @@
+RUN: lld-link /debug /pdb:%t.pdb /nodefaultlib /entry:main %S/Inputs/pdb-diff.obj
+RUN: llvm-pdbutil dump -modules %t.pdb | FileCheck --check-prefix=MODS %s
+RUN: llvm-pdbutil dump -symbols %t.pdb | FileCheck --check-prefix=SYMS %s
+
+MODS: Mod 0001 | `* Linker *`
+MODS-NEXT: Obj: ``:
+MODS-NEXT: debug stream: 10, # files: 0, has ec info: false
+MODS-NEXT: pdb file ni: 1 `{{.*}}pdb-linker-module.test.tmp.pdb`, src file ni: 0 ``
+
+SYMS: Mod 0001 | `* Linker *`
+SYMS-NEXT: 4 | S_OBJNAME [size = 20] sig=0, `* Linker *`
+SYMS-NEXT: 24 | S_COMPILE3 [size = 40]
+SYMS-NEXT: machine = intel 80386, Ver = LLVM Linker, language = link
+SYMS-NEXT: frontend = 0.0.0.0, backend = 14.10.25019.0
+SYMS-NEXT: flags = none
+SYMS-NEXT: 64 | S_ENVBLOCK
+SYMS-NEXT: - cwd
+SYMS-NEXT: -
+SYMS-NEXT: - exe
+SYMS-NEXT: - {{.*}}lld-link
+SYMS-NEXT: - pdb
+SYMS-NEXT: - {{.*}}pdb-linker-module{{.*}}pdb
+SYMS-NEXT: - cmd
+SYMS-NEXT: - /debug /pdb:{{.*}}pdb-linker-module{{.*}}pdb /nodefaultlib /entry:main {{.*}}pdb-diff.obj
diff --git a/test/COFF/pdb-none.test b/test/COFF/pdb-none.test
index a028cf055..4f5ba0ebb 100644
--- a/test/COFF/pdb-none.test
+++ b/test/COFF/pdb-none.test
@@ -1,14 +1,15 @@
# RUN: yaml2obj < %p/Inputs/pdb1.yaml > %t1.obj
# RUN: yaml2obj < %p/Inputs/pdb2.yaml > %t2.obj
+# RUN: rm -f %t.pdb %t.dll
# RUN: lld-link /debug /debugtype:pdata /pdb:%t.pdb /dll /out:%t.dll /entry:main /nodefaultlib \
# RUN: %t1.obj %t2.obj
# RUN: llvm-pdbutil pdb2yaml -pdb-stream %t.pdb | FileCheck %s
# CHECK: PdbStream:
-# CHECK-NEXT: Age: 0
-# CHECK-NEXT: Guid: '{00000000-0000-0000-0000-000000000000}'
-# CHECK-NEXT: Signature: 0
+# CHECK-NEXT: Age: 1
+# CHECK-NEXT: Guid:
+# CHECK-NEXT: Signature:
# CHECK-NEXT: Features: [ VC140 ]
# CHECK-NEXT: Version: VC70
diff --git a/test/COFF/pdb-procid-remapping.test b/test/COFF/pdb-procid-remapping.test
new file mode 100644
index 000000000..cb612400a
--- /dev/null
+++ b/test/COFF/pdb-procid-remapping.test
@@ -0,0 +1,29 @@
+# RUN: yaml2obj < %p/Inputs/pdb1.yaml > %t1.obj
+# RUN: yaml2obj < %p/Inputs/pdb2.yaml > %t2.obj
+# RUN: lld-link /debug /pdb:%t.pdb /dll /out:%t.dll /entry:main /nodefaultlib \
+# RUN: %t1.obj %t2.obj
+
+# RUN: llvm-pdbutil dump -symbols %t.pdb | FileCheck %s
+
+CHECK: Symbols
+CHECK-NEXT: ============================================================
+CHECK-LABEL: Mod 0000 |
+CHECK: 92 | S_GPROC32 [size = 44] `main`
+CHECK-NEXT: parent = 0, end = 168, addr = 0002:0000, code size = 14
+CHECK-NEXT: type = `0x1004 (int (<no type>))`, debug start = 4, debug end = 9, flags = none
+CHECK-NEXT: 136 | S_FRAMEPROC [size = 32]
+CHECK-NEXT: size = 40, padding size = 0, offset to padding = 0
+CHECK-NEXT: bytes of callee saved registers = 0, exception handler addr = 0000:0000
+CHECK-NEXT: flags = has async eh | opt speed
+CHECK-NEXT: 168 | S_END [size = 4]
+CHECK-LABEL: Mod 0001 |
+CHECK: 92 | S_GPROC32 [size = 44] `foo`
+CHECK-NEXT: parent = 0, end = 168, addr = 0002:0016, code size = 6
+CHECK-NEXT: type = `0x1001 (int ())`, debug start = 0, debug end = 5, flags = none
+CHECK-NEXT: 136 | S_FRAMEPROC [size = 32]
+CHECK-NEXT: size = 0, padding size = 0, offset to padding = 0
+CHECK-NEXT: bytes of callee saved registers = 0, exception handler addr = 0000:0000
+CHECK-NEXT: flags = has async eh | opt speed
+CHECK-NEXT: 168 | S_END [size = 4]
+CHECK-LABEL: Mod 0002 |
+CHECK: 4 | S_OBJNAME [size = 20] sig=0, `* Linker *`
diff --git a/test/COFF/pdb-publics-import.test b/test/COFF/pdb-publics-import.test
new file mode 100644
index 000000000..1c75e905e
--- /dev/null
+++ b/test/COFF/pdb-publics-import.test
@@ -0,0 +1,42 @@
+Make a DLL that exports a few functions, then make a DLL with PDBs that imports
+them. Check that the __imp_ pointer and the generated thunks appear in the
+publics stream.
+
+RUN: yaml2obj < %p/Inputs/export.yaml > %t1.obj
+RUN: lld-link /out:%t1.dll /dll %t1.obj /implib:%t1.lib \
+RUN: /export:exportfn1 /export:exportfn2
+RUN: yaml2obj < %p/Inputs/import.yaml > %t2.obj
+RUN: lld-link /out:%t2.exe /pdb:%t2.pdb /debug /entry:main %t2.obj %t1.lib
+RUN: llvm-pdbutil dump %t2.pdb -publics -section-contribs | FileCheck %s
+
+CHECK: Public Symbols
+CHECK-NEXT: ============================================================
+CHECK-NEXT: Records
+CHECK-NEXT: 112 | S_PUB32 [size = 20] `main`
+CHECK-NEXT: flags = function, addr = 0001:0000
+CHECK-NEXT: 64 | S_PUB32 [size = 24] `exportfn1`
+CHECK-NEXT: flags = function, addr = 0001:0016
+CHECK-NEXT: 88 | S_PUB32 [size = 24] `exportfn2`
+CHECK-NEXT: flags = function, addr = 0001:0032
+CHECK-NEXT: 32 | S_PUB32 [size = 32] `__imp_exportfn2`
+CHECK-NEXT: flags = none, addr = 0003:0072
+CHECK-NEXT: 0 | S_PUB32 [size = 32] `__imp_exportfn1`
+CHECK-NEXT: flags = none, addr = 0003:0064
+
+CHECK: Section Contributions
+CHECK-NEXT: ============================================================
+ main
+CHECK-NEXT: SC[.text] | mod = 0, 0001:0000, size = 8, data crc = 0, reloc crc = 0
+CHECK-NEXT: IMAGE_SCN_CNT_CODE | IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_EXECUTE |
+CHECK-NEXT: IMAGE_SCN_MEM_READ
+ exportfn1 thunk
+CHECK-NEXT: SC[.text] | mod = 1, 0001:0016, size = 6, data crc = 0, reloc crc = 0
+CHECK-NEXT: IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ
+ exportfn2 thunk
+CHECK-NEXT: SC[.text] | mod = 1, 0001:0032, size = 6, data crc = 0, reloc crc = 0
+CHECK-NEXT: IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ
+ .rdata debug directory data chunks
+CHECK-NEXT: SC[.rdata] | mod = 1, 0002:0000, size = 28, data crc = 0, reloc crc = 0
+CHECK-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+CHECK-NEXT: SC[.rdata] | mod = 1, 0002:0028, size = {{.*}}, data crc = 0, reloc crc = 0
+CHECK-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
diff --git a/test/COFF/pdb-safeseh.yaml b/test/COFF/pdb-safeseh.yaml
new file mode 100644
index 000000000..27948e38d
--- /dev/null
+++ b/test/COFF/pdb-safeseh.yaml
@@ -0,0 +1,86 @@
+# RUN: yaml2obj %s -o %t.obj
+# RUN: lld-link -debug -entry:main -out:%t.exe -pdb:%t.pdb %t.obj
+# RUN: llvm-pdbutil dump -globals %t.pdb | FileCheck %s
+
+# There is an S_GDATA32 symbol record with .secrel32 and .secidx relocations in
+# it in this debug info. This is similar to the relocations in the loadcfg.obj
+# file in the MSVC CRT. We need to make sure that our relocation logic matches
+# MSVC's for these absolute, linker-provided symbols.
+
+# CHECK: Global Symbols
+# CHECK-NEXT: ============================================================
+# CHECK-NEXT: Records
+# CHECK-NEXT: 20 | S_GDATA32 [size = 40] `___safe_se_handler_table`
+# CHECK-NEXT: type = 0x0022 (unsigned long), addr = 0003:0000
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_I386
+ Characteristics: [ ]
+sections:
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_GDATA32
+ DataSym:
+ Type: 34
+ DisplayName: ___safe_se_handler_table
+ - !StringTable
+ Strings:
+ Relocations:
+ - VirtualAddress: 20
+ SymbolName: ___safe_se_handler_table
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 24
+ SymbolName: ___safe_se_handler_table
+ Type: IMAGE_REL_I386_SECTION
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 488D0500000000C3
+ Relocations:
+ - VirtualAddress: 3
+ SymbolName: ___safe_se_handler_table
+ Type: IMAGE_REL_I386_REL32
+symbols:
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 372
+ NumberOfRelocations: 6
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 1092178131
+ Number: 0
+ - Name: _main
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: ___safe_se_handler_table
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
+
diff --git a/test/COFF/pdb-same-name.test b/test/COFF/pdb-same-name.test
new file mode 100644
index 000000000..76db69fab
--- /dev/null
+++ b/test/COFF/pdb-same-name.test
@@ -0,0 +1,23 @@
+# RUN: rm -rf %t1 %t2
+# RUN: mkdir %t1 %t2
+# RUN: yaml2obj < %p/Inputs/pdb1.yaml > %t1/foo.obj
+# RUN: llvm-ar cru %t1/foo.lib %t1/foo.obj
+# RUN: yaml2obj < %p/Inputs/pdb2.yaml > %t2/foo.obj
+# RUN: llvm-ar cru %t2/foo.lib %t2/foo.obj
+
+# RUN: rm -f %t.dll %t.pdb
+# RUN: lld-link /debug /pdb:%t.pdb /dll /out:%t.dll /entry:main /nodefaultlib \
+# RUN: %t1/foo.lib %t2/foo.lib
+
+# RUN: llvm-pdbutil dump -modules %t.pdb | FileCheck -check-prefix RAW %s
+
+RAW: Modules
+RAW-NEXT: ============================================================
+RAW-NEXT: Mod 0000 | `foo.obj`:
+RAW-NEXT: Obj: `{{.*}}1\foo.lib`:
+RAW-NEXT: debug stream: 9, # files: 1, has ec info: false
+RAW-NEXT: pdb file ni: 0 ``, src file ni: 0 ``
+RAW-NEXT: Mod 0001 | `foo.obj`:
+RAW-NEXT: Obj: `{{.*}}2\foo.lib`:
+RAW-NEXT: debug stream: 10, # files: 1, has ec info: false
+RAW-NEXT: pdb file ni: 0 ``, src file ni: 0 ``
diff --git a/test/COFF/pdb-scopes.test b/test/COFF/pdb-scopes.test
new file mode 100644
index 000000000..2649167e9
--- /dev/null
+++ b/test/COFF/pdb-scopes.test
@@ -0,0 +1,75 @@
+Consider this program:
+
+$ cat a.c
+void g(int x) {}
+void f(int x);
+int main(int argc) {
+ if (argc) {
+ int x = 42;
+ f(x);
+ } else {
+ int y = 13;
+ f(y);
+ }
+}
+
+$ cat b.c
+extern void g();
+void f(int x) {
+ if (x) {
+ int y = x + 3;
+ g(y);
+ } else {
+ int w = x + 4;
+ g(w);
+ }
+}
+
+This program is interesting because there are two TUs, and each TU has nested
+scopes. Make sure we get the right parent and end offsets.
+
+RUN: yaml2obj %S/Inputs/pdb-scopes-a.yaml -o %t-a.obj
+RUN: yaml2obj %S/Inputs/pdb-scopes-b.yaml -o %t-b.obj
+RUN: lld-link %t-a.obj %t-b.obj -debug -entry:main -nodefaultlib -out:%t.exe -pdb:%t.pdb
+RUN: llvm-pdbutil dump -symbols %t.pdb | FileCheck %s
+
+CHECK-LABEL: Mod 0000 | `{{.*}}pdb-scopes.test.tmp-a.obj`:
+CHECK: 104 | S_GPROC32 [size = 44] `g`
+CHECK: parent = 0, end = 196, addr = 0002:0000, code size = 5
+CHECK: debug start = 4, debug end = 4, flags = none
+CHECK: 180 | S_REGREL32 [size = 16] `x`
+CHECK: 196 | S_END [size = 4]
+CHECK: 200 | S_GPROC32 [size = 44] `main`
+CHECK: parent = 0, end = 384, addr = 0002:0016, code size = 58
+CHECK: debug start = 8, debug end = 53, flags = none
+CHECK: 276 | S_REGREL32 [size = 20] `argc`
+CHECK: 296 | S_BLOCK32 [size = 24] ``
+CHECK: parent = 200, end = 336
+CHECK: code size = 17, addr = 0002:0031
+CHECK: 320 | S_REGREL32 [size = 16] `x`
+CHECK: 336 | S_END [size = 4]
+CHECK: 340 | S_BLOCK32 [size = 24] ``
+CHECK: parent = 200, end = 380
+CHECK: code size = 17, addr = 0002:0050
+CHECK: 364 | S_REGREL32 [size = 16] `y`
+CHECK: 380 | S_END [size = 4]
+CHECK: 384 | S_END [size = 4]
+
+CHECK-LABEL: Mod 0001 | `{{.*}}pdb-scopes.test.tmp-b.obj`:
+CHECK: 104 | S_GPROC32 [size = 44] `f`
+CHECK: parent = 0, end = 284, addr = 0002:0080, code size = 62
+CHECK: debug start = 8, debug end = 57, flags = none
+CHECK: 180 | S_REGREL32 [size = 16] `x`
+CHECK: 196 | S_BLOCK32 [size = 24] ``
+CHECK: parent = 104, end = 236
+CHECK: code size = 20, addr = 0002:0095
+CHECK: 220 | S_REGREL32 [size = 16] `y`
+CHECK: 236 | S_END [size = 4]
+CHECK: 240 | S_BLOCK32 [size = 24] ``
+CHECK: parent = 104, end = 280
+CHECK: code size = 20, addr = 0002:0117
+CHECK: 264 | S_REGREL32 [size = 16] `w`
+CHECK: 280 | S_END [size = 4]
+CHECK: 284 | S_END [size = 4]
+
+CHECK-LABEL: Mod 0002 | `* Linker *`:
diff --git a/test/COFF/pdb-secrel-absolute.yaml b/test/COFF/pdb-secrel-absolute.yaml
new file mode 100644
index 000000000..330106b3b
--- /dev/null
+++ b/test/COFF/pdb-secrel-absolute.yaml
@@ -0,0 +1,85 @@
+# RUN: yaml2obj %s -o %t.obj
+# RUN: lld-link -debug -entry:main -out:%t.exe -pdb:%t.pdb %t.obj
+# RUN: llvm-pdbutil dump -globals %t.pdb | FileCheck %s
+
+# There is an S_GDATA32 symbol record with .secrel32 and .secidx relocations in
+# it in this debug info. This is similar to the relocations in the loadcfg.obj
+# file in the MSVC CRT. We need to make sure that our relocation logic matches
+# MSVC's for these absolute, linker-provided symbols.
+
+# CHECK: Global Symbols
+# CHECK-NEXT: ============================================================
+# CHECK-NEXT: Records
+# CHECK-NEXT: 20 | S_GDATA32 [size = 36] `__guard_fids_table`
+# CHECK-NEXT: type = 0x0022 (unsigned long), addr = 0003:0000
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_GDATA32
+ DataSym:
+ Type: 34
+ DisplayName: __guard_fids_table
+ - !StringTable
+ Strings:
+ Relocations:
+ - VirtualAddress: 20
+ SymbolName: __guard_fids_table
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 24
+ SymbolName: __guard_fids_table
+ Type: IMAGE_REL_AMD64_SECTION
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 488D0500000000C3
+ Relocations:
+ - VirtualAddress: 3
+ SymbolName: __guard_fids_table
+ Type: IMAGE_REL_AMD64_REL32
+symbols:
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 372
+ NumberOfRelocations: 6
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 1092178131
+ Number: 0
+ - Name: main
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: __guard_fids_table
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/pdb-source-lines.test b/test/COFF/pdb-source-lines.test
new file mode 100644
index 000000000..389d91f20
--- /dev/null
+++ b/test/COFF/pdb-source-lines.test
@@ -0,0 +1,125 @@
+Test the linker line tables on roughly the following example:
+
+==> foo.h <==
+void bar(void);
+inline void foo(void) {
+ bar();
+}
+==> pdb_lines_1.c <==
+#include "foo.h"
+int main(void) {
+ foo();
+ return 42;
+}
+==> pdb_lines_2.c <==
+void bar(void) {
+}
+
+$ cl -c -Z7 pdb_lines*.c
+
+RUN: yaml2obj %S/Inputs/pdb_lines_1.yaml -o %t.pdb_lines_1.obj
+RUN: yaml2obj %S/Inputs/pdb_lines_2.yaml -o %t.pdb_lines_2.obj
+RUN: rm -f %t.exe %t.pdb
+RUN: lld-link -debug -entry:main -nodefaultlib -out:%t.exe -pdb:%t.pdb %t.pdb_lines_1.obj %t.pdb_lines_2.obj
+RUN: llvm-pdbutil pdb2yaml -modules -module-files -subsections=lines,fc %t.pdb | FileCheck %s
+
+CHECK-LABEL: DbiStream:
+CHECK-NEXT: VerHeader: V70
+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: Modules:
+
+CHECK-LABEL: - Module: {{.*}}pdb_lines_1.obj
+CHECK-NEXT: ObjFile: {{.*}}pdb_lines_1.obj
+CHECK-NEXT: SourceFiles:
+CHECK-NEXT: - '{{.*}}pdb_lines_1.c'
+CHECK-NEXT: - '{{.*}}foo.h'
+CHECK-NEXT: Subsections:
+CHECK-NEXT: - !Lines
+CHECK-NEXT: CodeSize: 19
+CHECK-NEXT: Flags: [ ]
+CHECK-NEXT: RelocOffset: 0
+CHECK-NEXT: RelocSegment: 2
+CHECK-NEXT: Blocks:
+CHECK-NEXT: - FileName: '{{.*}}pdb_lines_1.c'
+CHECK-NEXT: Lines:
+CHECK-NEXT: - Offset: 0
+CHECK-NEXT: LineStart: 2
+CHECK-NEXT: IsStatement: true
+CHECK-NEXT: EndDelta: 0
+CHECK-NEXT: - Offset: 4
+CHECK-NEXT: LineStart: 3
+CHECK-NEXT: IsStatement: true
+CHECK-NEXT: EndDelta: 0
+CHECK-NEXT: - Offset: 9
+CHECK-NEXT: LineStart: 4
+CHECK-NEXT: IsStatement: true
+CHECK-NEXT: EndDelta: 0
+CHECK-NEXT: - Offset: 14
+CHECK-NEXT: LineStart: 5
+CHECK-NEXT: IsStatement: true
+CHECK-NEXT: EndDelta: 0
+CHECK-NEXT: Columns:
+CHECK-NEXT: - !FileChecksums
+CHECK-NEXT: Checksums:
+CHECK-NEXT: - FileName: '{{.*}}pdb_lines_1.c'
+CHECK-NEXT: Kind: MD5
+CHECK-NEXT: Checksum: 4EB19DCD86C3BA2238A255C718572E7B
+CHECK-NEXT: - FileName: '{{.*}}foo.h'
+CHECK-NEXT: Kind: MD5
+CHECK-NEXT: Checksum: 061EB73ABB642532857A4F1D9CBAC323
+CHECK-NEXT: - !Lines
+CHECK-NEXT: CodeSize: 14
+CHECK-NEXT: Flags: [ ]
+CHECK-NEXT: RelocOffset: 32
+CHECK-NEXT: RelocSegment: 2
+CHECK-NEXT: Blocks:
+CHECK-NEXT: - FileName: '{{.*}}foo.h'
+CHECK-NEXT: Lines:
+CHECK-NEXT: - Offset: 0
+CHECK-NEXT: LineStart: 2
+CHECK-NEXT: IsStatement: true
+CHECK-NEXT: EndDelta: 0
+CHECK-NEXT: - Offset: 4
+CHECK-NEXT: LineStart: 3
+CHECK-NEXT: IsStatement: true
+CHECK-NEXT: EndDelta: 0
+CHECK-NEXT: - Offset: 9
+CHECK-NEXT: LineStart: 4
+CHECK-NEXT: IsStatement: true
+CHECK-NEXT: EndDelta: 0
+CHECK-NEXT: Columns:
+
+CHECK-LABEL: - Module: {{.*}}pdb_lines_2.obj
+CHECK-NEXT: ObjFile: {{.*}}pdb_lines_2.obj
+CHECK-NEXT: SourceFiles:
+CHECK-NEXT: - '{{.*}}pdb_lines_2.c'
+CHECK-NEXT: Subsections:
+CHECK-NEXT: - !Lines
+CHECK-NEXT: CodeSize: 1
+CHECK-NEXT: Flags: [ ]
+CHECK-NEXT: RelocOffset: 48
+CHECK-NEXT: RelocSegment: 2
+CHECK-NEXT: Blocks:
+CHECK-NEXT: - FileName: '{{.*}}pdb_lines_2.c'
+CHECK-NEXT: Lines:
+CHECK-NEXT: - Offset: 0
+CHECK-NEXT: LineStart: 1
+CHECK-NEXT: IsStatement: true
+CHECK-NEXT: EndDelta: 0
+CHECK-NEXT: - Offset: 0
+CHECK-NEXT: LineStart: 2
+CHECK-NEXT: IsStatement: true
+CHECK-NEXT: EndDelta: 0
+CHECK-NEXT: Columns:
+CHECK-NEXT: - !FileChecksums
+CHECK-NEXT: Checksums:
+CHECK-NEXT: - FileName: '{{.*}}pdb_lines_2.c'
+CHECK-NEXT: Kind: MD5
+CHECK-NEXT: Checksum: DF91CB3A2B8D917486574BB50CAC4CC7
+CHECK-NEXT: - Module: '* Linker *'
+CHECK-NEXT: ObjFile: ''
diff --git a/test/COFF/pdb-symbol-types.yaml b/test/COFF/pdb-symbol-types.yaml
new file mode 100644
index 000000000..9dad72d3c
--- /dev/null
+++ b/test/COFF/pdb-symbol-types.yaml
@@ -0,0 +1,346 @@
+# RUN: yaml2obj %s -o %t.obj
+# RUN: lld-link %t.obj -nodefaultlib -entry:main -debug -out:%t.exe -pdb:%t.pdb
+# RUN: llvm-pdbutil dump -symbols -globals %t.pdb | FileCheck %s
+
+# To regenerate the object file:
+# $ cat symbol-types.c
+# struct Foo { int x; };
+# typedef struct Foo UDT_Foo;
+# UDT_Foo global_foo = {42};
+# int main() { return global_foo.x; }
+# $ cl -c -Z7 symbol-types.c
+
+# Note that the type of 'global' goes from 0x1005 in the object file to 0x1004
+# in the PDB because the LF_FUNC_ID is moved to the id stream.
+
+# CHECK-LABEL: Global Symbols
+# CHECK-NEXT: ============================================================
+# CHECK-NEXT: Records
+# CHECK-NEXT: 48 | S_PROCREF [size = 20] `main`
+# CHECK-NEXT: module = 1, sum name = 0, offset = 116
+# CHECK-NEXT: 68 | S_GDATA32 [size = 28] `global_foo`
+# CHECK-NEXT: type = 0x1004 (Foo), addr = 0001:0000
+
+# CHECK: Symbols
+# CHECK: ============================================================
+# CHECK-LABEL: Mod 0000 | `{{.*}}pdb-symbol-types.yaml.tmp.obj`:
+# CHECK: 4 | S_OBJNAME [size = 52] sig=0, `C:\src\llvm-project\build\symbol-types.obj`
+# CHECK: 56 | S_COMPILE3 [size = 60]
+# CHECK: machine = intel x86-x64, Ver = Microsoft (R) Optimizing Compiler, language = c
+# CHECK: frontend = 19.0.24215.1, backend = 19.0.24215.1
+# CHECK: flags = security checks | hot patchable
+# CHECK: 116 | S_GPROC32 [size = 44] `main`
+# CHECK: parent = 0, end = 192, addr = 0002:0000, code size = 7
+# CHECK: debug start = 0, debug end = 6, flags = none
+# CHECK: 160 | S_FRAMEPROC [size = 32]
+# CHECK: size = 0, padding size = 0, offset to padding = 0
+# CHECK: bytes of callee saved registers = 0, exception handler addr = 0000:0000
+# CHECK: flags = has async eh | opt speed
+# CHECK: 192 | S_END [size = 4]
+# CHECK: 196 | S_BUILDINFO [size = 8] BuildId = `0x100A`
+# CHECK-LABEL: Mod 0001 | `* Linker *`:
+
+--- !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
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_OBJNAME
+ ObjNameSym:
+ Signature: 0
+ ObjectName: 'C:\src\llvm-project\build\symbol-types.obj'
+ - Kind: S_COMPILE3
+ Compile3Sym:
+ Flags: [ SecurityChecks, HotPatch ]
+ Machine: X64
+ FrontendMajor: 19
+ FrontendMinor: 0
+ FrontendBuild: 24215
+ FrontendQFE: 1
+ BackendMajor: 19
+ BackendMinor: 0
+ BackendBuild: 24215
+ BackendQFE: 1
+ Version: 'Microsoft (R) Optimizing Compiler'
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 7
+ DbgStart: 0
+ DbgEnd: 6
+ FunctionType: 4098
+ Flags: [ ]
+ DisplayName: main
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 0
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 7
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\symbol-types.c'
+ Lines:
+ - Offset: 0
+ LineStart: 4
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 0
+ LineStart: 5
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 6
+ LineStart: 6
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !Symbols
+ Records:
+ - Kind: S_GDATA32
+ DataSym:
+ Type: 4101
+ DisplayName: global_foo
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4101
+ UDTName: UDT_Foo
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4101
+ UDTName: Foo
+ - !FileChecksums
+ Checksums:
+ - FileName: 'c:\src\llvm-project\build\symbol-types.c'
+ Kind: MD5
+ Checksum: F833E1A4909FF6FEC5689A664F3BE725
+ - !StringTable
+ Strings:
+ - 'c:\src\llvm-project\build\symbol-types.c'
+ - !Symbols
+ Records:
+ - Kind: S_BUILDINFO
+ BuildInfoSym:
+ BuildId: 4111
+ Relocations:
+ - VirtualAddress: 164
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 168
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 220
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 224
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 284
+ SymbolName: global_foo
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 288
+ SymbolName: global_foo
+ Type: IMAGE_REL_AMD64_SECTION
+ - Name: '.debug$T'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ Types:
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 0 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 116
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4096
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4097
+ Name: main
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: Foo
+ UniqueName: '.?AUFoo@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 0
+ Name: x
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 1
+ Options: [ None, HasUniqueName ]
+ FieldList: 4100
+ Name: Foo
+ UniqueName: '.?AUFoo@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 4
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'c:\src\llvm-project\build\symbol-types.c'
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4101
+ SourceFile: 4102
+ LineNumber: 1
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\PROGRA~2\MICROS~1.0\VC\Bin\amd64\cl.exe'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: '-c -Z7 -MT -IC:\PROGRA~2\MICROS~1.0\VC\include -IC:\PROGRA~2\MICROS~1.0\VC\atlmfc\include -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\ucrt -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\shared -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\um'
+ - Kind: LF_SUBSTR_LIST
+ StringList:
+ StringIndices: [ 4106 ]
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 4107
+ String: ' -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\winrt -TC -X'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: symbol-types.c
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build\vc140.pdb'
+ - Kind: LF_BUILDINFO
+ BuildInfo:
+ ArgIndices: [ 4104, 4105, 4109, 4110, 4108 ]
+ - Name: .data
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 4
+ SectionData: 2A000000
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 8B0500000000C3
+ Relocations:
+ - VirtualAddress: 2
+ SymbolName: global_foo
+ Type: IMAGE_REL_AMD64_REL32
+symbols:
+ - Name: '@comp.id'
+ Value: 17063575
+ 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: 432
+ NumberOfRelocations: 6
+ 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: 732
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: .data
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 4
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 3482275674
+ Number: 0
+ - Name: global_foo
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 7
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 3635526833
+ Number: 0
+ - Name: main
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/pdb-type-server-missing.yaml b/test/COFF/pdb-type-server-missing.yaml
new file mode 100644
index 000000000..91bb04f56
--- /dev/null
+++ b/test/COFF/pdb-type-server-missing.yaml
@@ -0,0 +1,132 @@
+# This is an object compiled with /Zi (see the LF_TYPESERVER2 record) without an
+# adjacent type server PDB. Test that LLD fails gracefully on it.
+
+# FIXME: Ideally we'd do what MSVC does, which is to warn and drop all debug
+# info in the object with the missing PDB.
+
+# RUN: yaml2obj %s -o %t.obj
+# RUN: not lld-link %t.obj -out:%t.exe -debug -pdb:%t.pdb -nodefaultlib -entry:main 2>&1 | FileCheck %s
+
+# CHECK: error: Type server PDB was not found
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 1
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 3
+ DbgStart: 0
+ DbgEnd: 2
+ FunctionType: 4199
+ Flags: [ ]
+ DisplayName: main
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 0
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 3
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\t.c'
+ Lines:
+ - Offset: 0
+ LineStart: 1
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !FileChecksums
+ Checksums:
+ - FileName: 'c:\src\llvm-project\build\t.c'
+ Kind: MD5
+ Checksum: 270A878DCC1B845655B162F56C4F5020
+ - !StringTable
+ Strings:
+ - 'c:\src\llvm-project\build\t.c'
+ Relocations:
+ - VirtualAddress: 44
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 48
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 100
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 104
+ 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
+ Types:
+ - Kind: LF_TYPESERVER2
+ TypeServer2:
+ Guid: '{01DF191B-22BF-6B42-96CE-5258B8329FE5}'
+ Age: 18
+ Name: 'C:\src\llvm-project\build\definitely_not_found_for_sure.pdb'
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 33C0C3
+symbols:
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 328
+ NumberOfRelocations: 4
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: '.debug$T'
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 564
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 3
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 4021952397
+ Number: 0
+ - Name: main
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/pdb-type-server-simple.test b/test/COFF/pdb-type-server-simple.test
new file mode 100644
index 000000000..898c5d4aa
--- /dev/null
+++ b/test/COFF/pdb-type-server-simple.test
@@ -0,0 +1,95 @@
+Replicate this scenario:
+
+$ cat a.c
+struct Foo { int x; };
+int g(struct Foo *p);
+int main() {
+ struct Foo f = {42};
+ return g(&f);
+}
+
+$ cat b.c
+struct Foo { int x; };
+int g(struct Foo *p) { return p->x; }
+
+$ cl -c a.c b.c -Zi -Fdts.pdb
+
+$ lld-link a.obj b.obj -debug -entry:main -nodefaultlib -out:t.exe
+
+RUN: rm -rf %t && mkdir -p %t && cd %t
+RUN: yaml2obj %S/Inputs/pdb-type-server-simple-a.yaml -o a.obj
+RUN: yaml2obj %S/Inputs/pdb-type-server-simple-b.yaml -o b.obj
+RUN: llvm-pdbutil yaml2pdb %S/Inputs/pdb-type-server-simple-ts.yaml -pdb ts.pdb
+RUN: lld-link a.obj b.obj -entry:main -debug -out:t.exe -pdb:t.pdb -nodefaultlib
+RUN: llvm-pdbutil dump -symbols -types -ids -globals %t/t.pdb | FileCheck %s
+
+
+CHECK-LABEL: Types (TPI Stream)
+CHECK: ============================================================
+
+CHECK: [[FOO_DECL:[^ ]*]] | LF_STRUCTURE [size = 36] `Foo`
+
+CHECK: [[FOO_PTR:[^ ]*]] | LF_POINTER [size = 12]
+CHECK-NEXT: referent = [[FOO_DECL]]
+
+CHECK: [[G_ARGS:[^ ]*]] | LF_ARGLIST [size = 12]
+CHECK-NEXT: [[FOO_PTR]]: `Foo*`
+
+CHECK: [[G_PROTO:[^ ]*]] | LF_PROCEDURE [size = 16]
+CHECK-NEXT: return type = 0x0074 (int), # args = 1, param list = [[G_ARGS]]
+CHECK-NEXT: calling conv = cdecl, options = None
+
+CHECK: [[FOO_COMPLETE:[^ ]*]] | LF_STRUCTURE [size = 36] `Foo`
+CHECK-NEXT: unique name: `.?AUFoo@@`
+CHECK-NEXT: vtable: <no type>, base list: <no type>, field list: 0x{{.*}}
+CHECK: options: has unique name
+CHECK: [[MAIN_PROTO:[^ ]*]] | LF_PROCEDURE [size = 16]
+CHECK: return type = 0x0074 (int), # args = 0, param list = 0x{{.*}}
+CHECK: calling conv = cdecl, options = None
+
+
+CHECK-LABEL: Types (IPI Stream)
+CHECK: ============================================================
+CHECK: [[MAIN_ID:[^ ]*]] | LF_FUNC_ID [size = 20]
+CHECK: name = main, type = [[MAIN_PROTO]], parent scope = <no type>
+CHECK: [[G_ID:[^ ]*]] | LF_FUNC_ID [size = 16]
+CHECK: name = g, type = [[G_PROTO]], parent scope = <no type>
+CHECK: [[A_BUILD:[^ ]*]] | LF_BUILDINFO [size = 28]
+CHECK: {{.*}}: `a.c`
+CHECK: [[B_BUILD:[^ ]*]] | LF_BUILDINFO [size = 28]
+CHECK: {{.*}}: `b.c`
+
+CHECK-LABEL: Global Symbols
+CHECK: ============================================================
+CHECK-NEXT: Records
+CHECK-NEXT: 36 | S_PROCREF [size = 20] `main`
+CHECK-NEXT: module = 1, sum name = 0, offset = 104
+CHECK-NEXT: 56 | S_PROCREF [size = 16] `g`
+CHECK-NEXT: module = 2, sum name = 0, offset = 104
+
+CHECK-LABEL: Symbols
+CHECK: ============================================================
+CHECK-LABEL: Mod 0000 | `{{.*}}a.obj`:
+CHECK: 4 | S_OBJNAME [size = 40] sig=0, `C:\src\llvm-project\build\a.obj`
+CHECK: 104 | S_GPROC32 [size = 44] `main`
+CHECK: parent = 0, end = 196, addr = 0002:0000, code size = 27
+CHECK: type = {{.*}}, debug start = 4, debug end = 22, flags = none
+CHECK: 200 | S_BUILDINFO [size = 8] BuildId = `[[A_BUILD]]`
+CHECK-LABEL: Mod 0001 | `{{.*}}b.obj`:
+CHECK: 4 | S_OBJNAME [size = 40] sig=0, `C:\src\llvm-project\build\b.obj`
+CHECK: 44 | S_COMPILE3 [size = 60]
+CHECK: machine = intel x86-x64, Ver = Microsoft (R) Optimizing Compiler, language = c
+CHECK: frontend = 19.0.24215.1, backend = 19.0.24215.1
+CHECK: flags = security checks | hot patchable
+CHECK: 104 | S_GPROC32 [size = 44] `g`
+CHECK: parent = 0, end = 196, addr = 0002:0032, code size = 13
+CHECK: type = {{.*}}, debug start = 5, debug end = 12, flags = none
+CHECK: 148 | S_FRAMEPROC [size = 32]
+CHECK: size = 0, padding size = 0, offset to padding = 0
+CHECK: bytes of callee saved registers = 0, exception handler addr = 0000:0000
+CHECK: flags = has async eh | opt speed
+CHECK: 180 | S_REGREL32 [size = 16] `p`
+CHECK: type = [[FOO_PTR]] (Foo*), register = RSP, offset = 8
+CHECK: 196 | S_END [size = 4]
+CHECK: 200 | S_BUILDINFO [size = 8] BuildId = `[[B_BUILD]]`
+CHECK-LABEL: Mod 0002 | `* Linker *`:
diff --git a/test/COFF/pdb.test b/test/COFF/pdb.test
index c2881669d..524c7846d 100644
--- a/test/COFF/pdb.test
+++ b/test/COFF/pdb.test
@@ -1,13 +1,15 @@
# RUN: yaml2obj < %p/Inputs/pdb1.yaml > %t1.obj
# RUN: yaml2obj < %p/Inputs/pdb2.yaml > %t2.obj
+# RUN: rm -f %t.dll %t.pdb
# RUN: lld-link /debug /pdb:%t.pdb /dll /out:%t.dll /entry:main /nodefaultlib \
# RUN: %t1.obj %t2.obj
# RUN: llvm-pdbutil pdb2yaml -stream-metadata -stream-directory -pdb-stream \
# RUN: -dbi-stream -ipi-stream -tpi-stream %t.pdb | FileCheck %s
-# RUN: llvm-pdbutil raw -modules -section-map -section-contribs \
-# RUN: -types -ids %t.pdb | FileCheck -check-prefix RAW %s
+# RUN: llvm-pdbutil dump -modules -section-map -section-contribs -section-headers \
+# RUN: -publics -public-extras -types -ids -type-extras -id-extras %t.pdb \
+# RUN: | FileCheck -check-prefix RAW %s
# CHECK: MSF:
# CHECK-NEXT: SuperBlock:
@@ -22,15 +24,15 @@
# CHECK-NEXT: NumStreams:
# CHECK-NEXT: FileSize:
# CHECK-NEXT: StreamSizes:
-# CHECK-NEXT: StreamMap:
+# CHECK: StreamMap:
# CHECK: PdbStream:
# CHECK-NEXT: Age: 1
# CHECK-NEXT: Guid:
-# CHECK-NEXT: Signature: 0
+# CHECK-NEXT: Signature:
# CHECK-NEXT: Features: [ VC140 ]
# CHECK-NEXT: Version: VC70
# CHECK-NEXT: DbiStream:
-# CHECK-NEXT: VerHeader: V110
+# CHECK-NEXT: VerHeader: V70
# CHECK-NEXT: Age: 1
# CHECK-NEXT: BuildNumber: 0
# CHECK-NEXT: PdbDllVersion: 0
@@ -117,83 +119,165 @@
RAW: Modules
RAW-NEXT: ============================================================
-RAW-NEXT: Mod 0000 | Name: `{{.*}}pdb.test.tmp1.obj`:
+RAW-NEXT: Mod 0000 | `{{.*}}pdb.test.tmp1.obj`:
RAW-NEXT: Obj: `{{.*}}pdb.test.tmp1.obj`:
-RAW-NEXT: debug stream: 9, # files: 0, has ec info: false
-RAW-NEXT: Mod 0001 | Name: `{{.*}}pdb.test.tmp2.obj`:
+RAW-NEXT: debug stream: 9, # files: 1, has ec info: false
+RAW-NEXT: pdb file ni: 0 ``, src file ni: 0 ``
+RAW-NEXT: Mod 0001 | `{{.*}}pdb.test.tmp2.obj`:
RAW-NEXT: Obj: `{{.*}}pdb.test.tmp2.obj`:
-RAW-NEXT: debug stream: 10, # files: 0, has ec info: false
-RAW-NEXT: Mod 0002 | Name: `* Linker *`:
+RAW-NEXT: debug stream: 10, # files: 1, has ec info: false
+RAW-NEXT: pdb file ni: 0 ``, src file ni: 0 ``
+RAW-NEXT: Mod 0002 | `* Linker *`:
RAW-NEXT: Obj: ``:
RAW-NEXT: debug stream: 11, # files: 0, has ec info: false
+RAW-NEXT: pdb file ni: 1 `{{.*pdb.test.tmp.pdb}}`, src file ni: 0 ``
RAW: Types (TPI Stream)
RAW-NEXT: ============================================================
RAW-NEXT: Showing 5 records
-RAW-NEXT: 0x1000 | LF_ARGLIST [size = 8]
-RAW-NEXT: 0x1001 | LF_PROCEDURE [size = 16]
+RAW-NEXT: 0x1000 | LF_ARGLIST [size = 8, hash = 0xEC0]
+RAW-NEXT: 0x1001 | LF_PROCEDURE [size = 16, hash = 0x7BC]
RAW-NEXT: return type = 0x0074 (int), # args = 0, param list = 0x1000
RAW-NEXT: calling conv = cdecl, options = None
-RAW-NEXT: 0x1002 | LF_POINTER [size = 12]
+RAW-NEXT: 0x1002 | LF_POINTER [size = 12, hash = 0x884]
RAW-NEXT: referent = 0x1001, mode = pointer, opts = None, kind = ptr64
-RAW-NEXT: 0x1003 | LF_ARGLIST [size = 12]
+RAW-NEXT: 0x1003 | LF_ARGLIST [size = 12, hash = 0x936]
RAW-NEXT: <no type>: ``
-RAW-NEXT: 0x1004 | LF_PROCEDURE [size = 16]
+RAW-NEXT: 0x1004 | LF_PROCEDURE [size = 16, hash = 0x852]
RAW-NEXT: return type = 0x0074 (int), # args = 0, param list = 0x1003
RAW-NEXT: calling conv = cdecl, options = None
RAW: Types (IPI Stream)
RAW-NEXT: ============================================================
RAW-NEXT: Showing 12 records
-RAW-NEXT: 0x1000 | LF_FUNC_ID [size = 20]
+RAW-NEXT: 0x1000 | LF_FUNC_ID [size = 20, hash = 0x330]
RAW-NEXT: name = main, type = 0x1004, parent scope = <no type>
-RAW-NEXT: 0x1001 | LF_FUNC_ID [size = 16]
+RAW-NEXT: 0x1001 | LF_FUNC_ID [size = 16, hash = 0x120]
RAW-NEXT: name = foo, type = 0x1001, parent scope = <no type>
-RAW-NEXT: 0x1002 | LF_STRING_ID [size = 16] ID: <no type>, String: D:\b
-RAW-NEXT: 0x1003 | LF_STRING_ID [size = 36] ID: <no type>, String: C:\vs14\VC\BIN\amd64\cl.exe
-RAW-NEXT: 0x1004 | LF_STRING_ID [size = 260] ID: <no type>, 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"
-RAW-NEXT: 0x1005 | LF_SUBSTR_LIST [size = 12]
+RAW-NEXT: 0x1002 | LF_STRING_ID [size = 16, hash = 0x757] ID: <no type>, String: D:\b
+RAW-NEXT: 0x1003 | LF_STRING_ID [size = 36, hash = 0xC3A] ID: <no type>, String: C:\vs14\VC\BIN\amd64\cl.exe
+RAW-NEXT: 0x1004 | LF_STRING_ID [size = 260, hash = 0x433] ID: <no type>, 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"
+RAW-NEXT: 0x1005 | LF_SUBSTR_LIST [size = 12, hash = 0x759]
RAW-NEXT: 0x1004: `-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: 0x1006 | LF_STRING_ID [size = 132] ID: 0x1005, 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
-RAW-NEXT: 0x1007 | LF_STRING_ID [size = 24] ID: <no type>, String: ret42-main.c
-RAW-NEXT: 0x1008 | LF_STRING_ID [size = 24] ID: <no type>, String: D:\b\vc140.pdb
-RAW-NEXT: 0x1009 | LF_BUILDINFO [size = 28]
+RAW-NEXT: 0x1006 | LF_STRING_ID [size = 132, hash = 0xF57] ID: 0x1005, 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
+RAW-NEXT: 0x1007 | LF_STRING_ID [size = 24, hash = 0x2D1] ID: <no type>, String: ret42-main.c
+RAW-NEXT: 0x1008 | LF_STRING_ID [size = 24, hash = 0xB8B] ID: <no type>, String: D:\b\vc140.pdb
+RAW-NEXT: 0x1009 | LF_BUILDINFO [size = 28, hash = 0xA8C]
RAW-NEXT: 0x1002: `D:\b`
RAW-NEXT: 0x1003: `C:\vs14\VC\BIN\amd64\cl.exe`
RAW-NEXT: 0x1007: `ret42-main.c`
RAW-NEXT: 0x1008: `D:\b\vc140.pdb`
RAW-NEXT: 0x1006: ` -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: 0x100A | LF_STRING_ID [size = 20] ID: <no type>, String: ret42-sub.c
-RAW-NEXT: 0x100B | LF_BUILDINFO [size = 28]
+RAW-NEXT: 0x100A | LF_STRING_ID [size = 20, hash = 0x39C] ID: <no type>, String: ret42-sub.c
+RAW-NEXT: 0x100B | LF_BUILDINFO [size = 28, hash = 0xAD7]
RAW-NEXT: 0x1002: `D:\b`
RAW-NEXT: 0x1003: `C:\vs14\VC\BIN\amd64\cl.exe`
RAW-NEXT: 0x100A: `ret42-sub.c`
RAW-NEXT: 0x1008: `D:\b\vc140.pdb`
RAW-NEXT: 0x1006: ` -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: Public Symbols
+RAW-NEXT: ============================================================
+RAW-NEXT: Publics Header
+RAW-NEXT: sym hash = 556, thunk table addr = 0000:0000
+RAW-NEXT: GSI Header
+RAW-NEXT: sig = 0xFFFFFFFF, hdr = 0xF12F091A, hr size = 16, num buckets = 524
+RAW-NEXT: Records
+RAW-NEXT: 20 | S_PUB32 [size = 20] `main`
+RAW-NEXT: flags = function, addr = 0002:0000
+RAW-NEXT: 0 | S_PUB32 [size = 20] `foo`
+RAW-NEXT: flags = function, addr = 0002:0016
+RAW-NOT: S_PUB32
+RAW-NEXT: Hash Entries
+RAW-NEXT: off = 21, refcnt = 1
+RAW-NEXT: off = 1, refcnt = 1
+RAW-NEXT: Hash Buckets
+RAW-NEXT: 0x00000000
+RAW-NEXT: 0x0000000c
+RAW-NEXT: Address Map
+RAW-NEXT: off = 20
+RAW-NEXT: off = 0
+RAW: Section Headers
+RAW-NEXT: ============================================================
+RAW: SECTION HEADER #1
+RAW-NEXT: .pdata name
+RAW-NEXT: virtual size
+RAW-NEXT: 1000 virtual address
+RAW-NEXT: 200 size of raw data
+RAW-NEXT: 400 file pointer to raw data
+RAW-NEXT: 0 file pointer to relocation table
+RAW-NEXT: 0 file pointer to line numbers
+RAW-NEXT: 0 number of relocations
+RAW-NEXT: 0 number of line numbers
+RAW-NEXT: 40000040 flags
+RAW-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA
+RAW-NEXT: IMAGE_SCN_MEM_READ
+RAW: SECTION HEADER #2
+RAW-NEXT: .text name
+RAW-NEXT: virtual size
+RAW-NEXT: 2000 virtual address
+RAW-NEXT: 200 size of raw data
+RAW-NEXT: 600 file pointer to raw data
+RAW-NEXT: 0 file pointer to relocation table
+RAW-NEXT: 0 file pointer to line numbers
+RAW-NEXT: 0 number of relocations
+RAW-NEXT: 0 number of line numbers
+RAW-NEXT: 60000020 flags
+RAW-NEXT: IMAGE_SCN_CNT_CODE
+RAW-NEXT: IMAGE_SCN_MEM_EXECUTE
+RAW-NEXT: IMAGE_SCN_MEM_READ
+RAW: SECTION HEADER #3
+RAW-NEXT: .xdata name
+RAW-NEXT: virtual size
+RAW-NEXT: 3000 virtual address
+RAW-NEXT: 200 size of raw data
+RAW-NEXT: 800 file pointer to raw data
+RAW-NEXT: 0 file pointer to relocation table
+RAW-NEXT: 0 file pointer to line numbers
+RAW-NEXT: 0 number of relocations
+RAW-NEXT: 0 number of line numbers
+RAW-NEXT: 40000040 flags
+RAW-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA
+RAW-NEXT: IMAGE_SCN_MEM_READ
+RAW: SECTION HEADER #4
+RAW-NEXT: .rdata name
+RAW-NEXT: virtual size
+RAW-NEXT: 4000 virtual address
+RAW-NEXT: 200 size of raw data
+RAW-NEXT: A00 file pointer to raw data
+RAW-NEXT: 0 file pointer to relocation table
+RAW-NEXT: 0 file pointer to line numbers
+RAW-NEXT: 0 number of relocations
+RAW-NEXT: 0 number of line numbers
+RAW-NEXT: 40000040 flags
+RAW-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA
+RAW-NEXT: IMAGE_SCN_MEM_READ
+RAW: Original Section Headers
+RAW-NEXT: ============================================================
+RAW-NEXT: PDB does not contain the requested image section header type
RAW: Section Contributions
RAW-NEXT: ============================================================
-RAW-NEXT: SC | mod = 0, 65535:1288, size = 14, data crc = 0, reloc crc = 0
-RAW-NEXT: IMAGE_SCN_CNT_CODE | IMAGE_SCN_ALIGN_16BYTES | IMAGE_SCN_MEM_EXECUTE |
-RAW-NEXT: IMAGE_SCN_MEM_READ
-RAW-NEXT: SC | mod = 0, 65535:1312, size = 8, data crc = 0, reloc crc = 0
-RAW-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ
-RAW-NEXT: SC | mod = 0, 65535:1320, size = 12, data crc = 0, reloc crc = 0
-RAW-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ
-RAW-NEXT: SC | mod = 1, 65535:1144, size = 6, data crc = 0, reloc crc = 0
-RAW-NEXT: IMAGE_SCN_CNT_CODE | IMAGE_SCN_ALIGN_16BYTES | IMAGE_SCN_MEM_EXECUTE |
-RAW-NEXT: IMAGE_SCN_MEM_READ
+RAW-NEXT: SC[.pdata] | mod = 0, 0001:0000, size = 12, data crc = 361370162, reloc crc = 0
+RAW-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ
+RAW-NEXT: SC[.text] | mod = 0, 0002:0000, size = 14, data crc = 1682752513, reloc crc = 0
+RAW-NEXT: IMAGE_SCN_CNT_CODE | IMAGE_SCN_ALIGN_16BYTES | IMAGE_SCN_MEM_EXECUTE |
+RAW-NEXT: IMAGE_SCN_MEM_READ
+RAW-NEXT: SC[.text] | mod = 1, 0002:0016, size = 6, data crc = 2139436471, reloc crc = 0
+RAW-NEXT: IMAGE_SCN_CNT_CODE | IMAGE_SCN_ALIGN_16BYTES | IMAGE_SCN_MEM_EXECUTE |
+RAW-NEXT: IMAGE_SCN_MEM_READ
+RAW-NEXT: SC[.xdata] | mod = 0, 0003:0000, size = 8, data crc = 264583633, reloc crc = 0
+RAW-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ
RAW: Section Map
RAW-NEXT: ============================================================
-RAW-NEXT: Section 0000 | ovl = 0, group = 0, frame = 0, name = 1
-RAW-NEXT: class = 65535, offset = 0, size =
+RAW-NEXT: Section 0000 | ovl = 0, group = 0, frame = 1, name = 65535
+RAW-NEXT: class = 65535, offset = 0, size =
RAW-NEXT: flags = read | 32 bit addr | selector
-RAW-NEXT: Section 0001 | ovl = 1, group = 0, frame = 0, name = 2
-RAW-NEXT: class = 65535, offset = 0, size =
+RAW-NEXT: Section 0001 | ovl = 0, group = 0, frame = 2, name = 65535
+RAW-NEXT: class = 65535, offset = 0, size =
RAW-NEXT: flags = read | execute | 32 bit addr | selector
-RAW-NEXT: Section 0002 | ovl = 2, group = 0, frame = 0, name = 3
-RAW-NEXT: class = 65535, offset = 0, size =
+RAW-NEXT: Section 0002 | ovl = 0, group = 0, frame = 3, name = 65535
+RAW-NEXT: class = 65535, offset = 0, size =
RAW-NEXT: flags = read | 32 bit addr | selector
-RAW-NEXT: Section 0003 | ovl = 3, group = 0, frame = 0, name = 4
-RAW-NEXT: class = 65535, offset = 0, size =
+RAW-NEXT: Section 0003 | ovl = 0, group = 0, frame = 4, name = 65535
+RAW-NEXT: class = 65535, offset = 0, size =
RAW-NEXT: flags = read | 32 bit addr | selector
-RAW-NEXT: Section 0004 | ovl = 4, group = 0, frame = 0, name = 5
-RAW-NEXT: class = 65535, offset = 0, size =
+RAW-NEXT: Section 0004 | ovl = 0, group = 0, frame = 5, name = 65535
+RAW-NEXT: class = 65535, offset = 0, size =
RAW-NEXT: flags = 32 bit addr | absolute addr
diff --git a/test/COFF/reloc-discarded-dwarf.s b/test/COFF/reloc-discarded-dwarf.s
new file mode 100644
index 000000000..14dc5948b
--- /dev/null
+++ b/test/COFF/reloc-discarded-dwarf.s
@@ -0,0 +1,17 @@
+# RUN: llvm-mc -triple=x86_64-windows-msvc -filetype=obj -o %t1.obj %s
+# RUN: llvm-mc -triple=x86_64-windows-msvc -filetype=obj -o %t2.obj %s
+
+# LLD should not error on relocations in DWARF debug sections against symbols in
+# discarded sections.
+# RUN: lld-link -entry:main -debug %t1.obj %t2.obj
+
+ .section .text,"xr",discard,main
+ .globl main
+main:
+f:
+ retq
+
+ .section .debug_info,"dr"
+ .quad f
+ .section .eh_frame,"dr"
+ .quad f
diff --git a/test/COFF/reloc-discarded.s b/test/COFF/reloc-discarded.s
new file mode 100644
index 000000000..94eaba998
--- /dev/null
+++ b/test/COFF/reloc-discarded.s
@@ -0,0 +1,30 @@
+# RUN: echo -e '.section .bss,"bw",discard,main_global\n.global main_global\n main_global:\n .long 0' | \
+# RUN: llvm-mc - -filetype=obj -o %t1.obj -triple x86_64-windows-msvc
+# RUN: llvm-mc %s -filetype=obj -o %t2.obj -triple x86_64-windows-msvc
+
+# LLD should report an error and not assert regardless of whether we are doing
+# GC.
+
+# RUN: not lld-link -entry:main -nodefaultlib %t1.obj %t2.obj -out:%t.exe -opt:ref 2>&1 | FileCheck %s
+# RUN: not lld-link -entry:main -nodefaultlib %t1.obj %t2.obj -out:%t.exe -opt:noref 2>&1 | FileCheck %s
+
+# CHECK: error: relocation against symbol in discarded section: assoc_global
+
+ .section .bss,"bw",discard,main_global
+ .globl main_global
+ .p2align 2
+main_global:
+ .long 0
+
+ .section .CRT$XCU,"dr",associative,main_global
+ .p2align 3
+ .globl assoc_global
+assoc_global:
+ .quad main_global
+
+ .text
+ .globl main
+main:
+ movq assoc_global(%rip), %rax
+ movl (%rax), %eax
+ retq
diff --git a/test/COFF/reloc-oob.yaml b/test/COFF/reloc-oob.yaml
new file mode 100644
index 000000000..0ed4c4d57
--- /dev/null
+++ b/test/COFF/reloc-oob.yaml
@@ -0,0 +1,62 @@
+# Make sure LLD does some light relocation bounds checking.
+
+# RUN: yaml2obj %s -o %t.obj
+# RUN: not lld-link %t.obj -entry:main -nodefaultlib -out:%t.exe 2>&1 | FileCheck %s
+
+# CHECK: error: relocation points beyond the end of its parent section
+
+--- !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: 16
+ SectionData: 5589E550C745FC00000000A10000000083C4045DC3
+ Relocations:
+ - VirtualAddress: 24
+ SymbolName: _g
+ Type: IMAGE_REL_I386_DIR32
+ - Name: .data
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 4
+ SectionData: 2A000000
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 21
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 662775349
+ 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: 4
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 3482275674
+ Number: 2
+ - Name: _main
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: _g
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/resource.test b/test/COFF/resource.test
index a73a20258..53242cdcb 100644
--- a/test/COFF/resource.test
+++ b/test/COFF/resource.test
@@ -6,7 +6,39 @@
EXE: {{H.e.l.l.o}}
-# RUN: llvm-readobj -file-headers %t.exe | FileCheck --check-prefix=HEADER %s
+# Verify the resource tree layout in the final executable.
+# RUN: llvm-readobj -file-headers -coff-resources -section-data %t.exe | \
+# RUN: FileCheck --check-prefix=RESOURCE_INFO %s
-HEADER: ResourceTableRVA: 0x1000
-HEADER: ResourceTableSize: 0x88
+RESOURCE_INFO: ResourceTableRVA: 0x1000
+RESOURCE_INFO-NEXT: ResourceTableSize: 0x88
+RESOURCE_INFO-DAG: Resources [
+RESOURCE_INFO-NEXT: Total Number of Resources: 1
+RESOURCE_INFO-NEXT: Base Table Address: 0x400
+RESOURCE_INFO-DAG: Number of String Entries: 0
+RESOURCE_INFO-NEXT: Number of ID Entries: 1
+RESOURCE_INFO-NEXT: Type: kRT_STRING (ID 6) [
+RESOURCE_INFO-NEXT: Table Offset: 0x18
+RESOURCE_INFO-NEXT: Number of String Entries: 0
+RESOURCE_INFO-NEXT: Number of ID Entries: 1
+RESOURCE_INFO-NEXT: Name: (ID 1) [
+RESOURCE_INFO-NEXT: Table Offset: 0x30
+RESOURCE_INFO-NEXT: Number of String Entries: 0
+RESOURCE_INFO-NEXT: Number of ID Entries: 1
+RESOURCE_INFO-NEXT: Language: (ID 1033) [
+RESOURCE_INFO-NEXT: Entry Offset: 0x48
+RESOURCE_INFO-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0)
+RESOURCE_INFO-NEXT: Major Version: 0
+RESOURCE_INFO-NEXT: Minor Version: 0
+RESOURCE_INFO-NEXT: Characteristics: 0
+RESOURCE_INFO-DAG: .rsrc Data (
+RESOURCE_INFO-NEXT: 0000: 00000000 00000000 00000000 00000100 |................|
+RESOURCE_INFO-NEXT: 0010: 06000000 18000080 00000000 00000000 |................|
+RESOURCE_INFO-NEXT: 0020: 00000000 00000100 01000000 30000080 |............0...|
+RESOURCE_INFO-NEXT: 0030: 00000000 00000000 00000000 00000100 |................|
+RESOURCE_INFO-NEXT: 0040: 09040000 48000000 58100000 2A000000 |....H...X...*...|
+RESOURCE_INFO-NEXT: 0050: 00000000 00000000 00000500 48006500 |............H.e.|
+RESOURCE_INFO-NEXT: 0060: 6C006C00 6F000000 00000000 00000000 |l.l.o...........|
+RESOURCE_INFO-NEXT: 0070: 00000000 00000000 00000000 00000000 |................|
+RESOURCE_INFO-NEXT: 0080: 00000000 00000000 |........|
+RESOURCE_INFO-NEXT: )
diff --git a/test/COFF/responsefile.test b/test/COFF/responsefile.test
index fd4d221c2..742962423 100644
--- a/test/COFF/responsefile.test
+++ b/test/COFF/responsefile.test
@@ -3,5 +3,23 @@
# RUN: echo /out:%t.exe /entry:main %t.obj > %t.rsp
# RUN: lld-link @%t.rsp /heap:0x3000
# RUN: llvm-readobj -file-headers %t.exe | FileCheck %s
-
CHECK: SizeOfHeapReserve: 12288
+
+# RUN: not lld-link --rsp-quoting=foobar @%t.rsp 2>&1 | \
+# RUN: FileCheck --check-prefix=INVRSP %s
+INVRSP: invalid response file quoting: foobar
+
+# RUN: echo "blah\foo" > %t.rsp
+# RUN: not lld-link @%t.rsp 2>&1 | \
+# RUN: FileCheck --check-prefix=DEFRSP %s
+DEFRSP: error: could not open blah\foo
+
+# RUN: echo "blah\foo" > %t.rsp
+# RUN: not lld-link --rsp-quoting=windows @%t.rsp 2>&1 | \
+# RUN: FileCheck --check-prefix=WINRSP %s
+WINRSP: error: could not open blah\foo
+
+# RUN: echo "blah\foo" > %t.rsp
+# RUN: not lld-link --rsp-quoting=posix @%t.rsp 2>&1 | \
+# RUN: FileCheck --check-prefix=POSRSP %s
+POSRSP: error: could not open blahfoo
diff --git a/test/COFF/rsds.test b/test/COFF/rsds.test
index 82b0f220b..176597786 100644
--- a/test/COFF/rsds.test
+++ b/test/COFF/rsds.test
@@ -1,11 +1,20 @@
# RUN: yaml2obj %s > %t.obj
+# RUN: rm -f %t.dll %t.pdb
# RUN: lld-link /debug /dll /out:%t.dll /entry:DllMain %t.obj
-# RUN: llvm-readobj -coff-debug-directory %t.dll | FileCheck %s
+# RUN: llvm-readobj -coff-debug-directory %t.dll > %t.1.txt
+# RUN: lld-link /debug /dll /out:%t.dll /entry:DllMain %t.obj
+# RUN: llvm-readobj -coff-debug-directory %t.dll > %t.2.txt
+# RUN: cat %t.1.txt %t.2.txt | FileCheck %s
+# RUN: rm -f %t.dll %t.pdb
+# RUN: lld-link /debug /pdb:%t.pdb /dll /out:%t.dll /entry:DllMain %t.obj
+# RUN: llvm-readobj -coff-debug-directory %t.dll > %t.3.txt
# 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
+# RUN: llvm-readobj -coff-debug-directory %t.dll > %t.4.txt
+# RUN: cat %t.3.txt %t.4.txt | FileCheck %s
+# CHECK: File: [[FILE:.*]].dll
# CHECK: DebugDirectory [
# CHECK: DebugEntry {
# CHECK: Characteristics: 0x0
@@ -13,17 +22,36 @@
# CHECK: MajorVersion: 0x0
# CHECK: MinorVersion: 0x0
# CHECK: Type: CodeView (0x2)
-# CHECK: SizeOfData:
-# CHECK: AddressOfRawData:
-# CHECK: PointerToRawData:
+# CHECK: SizeOfData: 0x{{[^0]}}
+# CHECK: AddressOfRawData: 0x{{[^0]}}
+# CHECK: PointerToRawData: 0x{{[^0]}}
# CHECK: PDBInfo {
# CHECK: PDBSignature: 0x53445352
-# CHECK: PDBGUID:
+# CHECK: PDBGUID: [[GUID:\(([A-Za-z0-9]{2} ?){16}\)]]
# CHECK: PDBAge: 1
# CHECK: PDBFileName: {{.*}}.pdb
# CHECK: }
# CHECK: }
# CHECK: ]
+# CHECK: File: [[FILE]].dll
+# 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: 0x{{[^0]}}
+# CHECK: AddressOfRawData: 0x{{[^0]}}
+# CHECK: PointerToRawData: 0x{{[^0]}}
+# CHECK: PDBInfo {
+# CHECK: PDBSignature: 0x53445352
+# CHECK: PDBGUID: [[GUID]]
+# CHECK: PDBAge: 2
+# CHECK: PDBFileName: {{.*}}.pdb
+# CHECK: }
+# CHECK: }
+# CHECK: ]
--- !COFF
header:
diff --git a/test/COFF/safeseh.test b/test/COFF/safeseh-diag-feat.test
index ed928a513..ed928a513 100644
--- a/test/COFF/safeseh.test
+++ b/test/COFF/safeseh-diag-feat.test
diff --git a/test/COFF/safeseh.s b/test/COFF/safeseh.s
new file mode 100644
index 000000000..83c15afbf
--- /dev/null
+++ b/test/COFF/safeseh.s
@@ -0,0 +1,60 @@
+# RUN: llvm-mc -triple i686-windows-msvc %s -filetype=obj -o %t.obj
+# RUN: lld-link %t.obj -safeseh -out:%t.exe -opt:noref -entry:main
+# RUN: llvm-readobj -coff-load-config %t.exe | FileCheck %s --check-prefix=CHECK-NOGC
+# RUN: lld-link %t.obj -safeseh -out:%t.exe -opt:ref -entry:main
+# RUN: llvm-readobj -coff-load-config %t.exe | FileCheck %s --check-prefix=CHECK-GC
+
+# CHECK-NOGC: LoadConfig [
+# CHECK-NOGC: Size: 0x48
+# CHECK-NOGC: SEHandlerTable: 0x401048
+# CHECK-NOGC: SEHandlerCount: 1
+# CHECK-NOGC: ]
+# CHECK-NOGC: SEHTable [
+# CHECK-NOGC-NEXT: 0x402006
+# CHECK-NOGC-NEXT: ]
+
+# CHECK-GC: LoadConfig [
+# CHECK-GC: Size: 0x48
+# CHECK-GC: SEHandlerTable: 0x0
+# CHECK-GC: SEHandlerCount: 0
+# CHECK-GC: ]
+# CHECK-GC-NOT: SEHTable
+
+
+ .def @feat.00;
+ .scl 3;
+ .type 0;
+ .endef
+ .globl @feat.00
+@feat.00 = 1
+
+ .def _main;
+ .scl 2;
+ .type 32;
+ .endef
+ .section .text,"xr",one_only,_main
+ .globl _main
+_main:
+ movl $42, %eax
+ ret
+
+# This handler can be GCd, which will make the safeseh table empty, so it should
+# appear null.
+ .def _my_handler;
+ .scl 3;
+ .type 32;
+ .endef
+ .section .text,"xr",one_only,_my_handler
+_my_handler:
+ ret
+
+.safeseh _my_handler
+
+
+ .section .rdata,"dr"
+.globl __load_config_used
+__load_config_used:
+ .long 72
+ .fill 60, 1, 0
+ .long ___safe_se_handler_table
+ .long ___safe_se_handler_count
diff --git a/test/COFF/savetemps.ll b/test/COFF/savetemps.ll
index 4e5981244..7f2e11c17 100644
--- a/test/COFF/savetemps.ll
+++ b/test/COFF/savetemps.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; RUN: rm -fr %T/savetemps
; RUN: mkdir %T/savetemps
; RUN: llvm-as -o %T/savetemps/savetemps.obj %s
diff --git a/test/COFF/secidx-absolute.s b/test/COFF/secidx-absolute.s
new file mode 100644
index 000000000..bfe7136b3
--- /dev/null
+++ b/test/COFF/secidx-absolute.s
@@ -0,0 +1,33 @@
+# RUN: llvm-mc %s -filetype=obj -triple=x86_64-windows-msvc -o %t.obj
+# RUN: lld-link -entry:main -nodefaultlib %t.obj -out:%t.exe
+# RUN: llvm-readobj %t.exe -sections -section-data | FileCheck %s
+
+# Section relocations against absolute symbols resolve to the last real ouput
+# section index plus one.
+
+.text
+.global main
+main:
+ret
+
+.section .rdata,"dr"
+.secidx __guard_fids_table
+
+# CHECK: Sections [
+# CHECK: Section {
+# CHECK: Number: 1
+# CHECK: Name: .rdata (2E 72 64 61 74 61 00 00)
+# CHECK: SectionData (
+# CHECK: 0000: 0300 |..|
+# CHECK: )
+# CHECK: }
+# CHECK: Section {
+# CHECK: Number: 2
+# CHECK: Name: .text (2E 74 65 78 74 00 00 00)
+# CHECK: VirtualSize: 0x1
+# CHECK: SectionData (
+# CHECK: 0000: C3 |.|
+# CHECK: )
+# CHECK: }
+# CHECK-NOT: Section
+# CHECK: ]
diff --git a/test/COFF/secrel-absolute.s b/test/COFF/secrel-absolute.s
new file mode 100644
index 000000000..bc61fb94b
--- /dev/null
+++ b/test/COFF/secrel-absolute.s
@@ -0,0 +1,14 @@
+# RUN: llvm-mc %s -filetype=obj -triple=x86_64-windows-msvc -o %t.obj
+# RUN: not lld-link -entry:main -nodefaultlib %t.obj -out:%t.exe 2>&1 | FileCheck %s
+
+# secrel relocations against absolute symbols are errors.
+
+# CHECK: SECREL relocation cannot be applied to absolute symbols
+
+.text
+.global main
+main:
+ret
+
+.section .rdata,"dr"
+.secrel32 __guard_fids_table
diff --git a/test/COFF/secrel-common.s b/test/COFF/secrel-common.s
new file mode 100644
index 000000000..0188f6cb9
--- /dev/null
+++ b/test/COFF/secrel-common.s
@@ -0,0 +1,41 @@
+# RUN: llvm-mc %s -filetype=obj -triple=x86_64-windows-msvc -o %t.obj
+# RUN: lld-link -entry:main -nodefaultlib %t.obj -out:%t.exe
+# RUN: llvm-readobj %t.exe -sections -section-data | FileCheck %s
+
+# Section relocations against common symbols resolve to .bss.
+
+# CHECK: Sections [
+# CHECK: Section {
+# CHECK: Number: 1
+# CHECK: Name: .bss (2E 62 73 73 00 00 00 00)
+# CHECK: VirtualSize: 0x4
+# CHECK: }
+# CHECK: Section {
+# CHECK: Number: 2
+# CHECK: Name: .rdata (2E 72 64 61 74 61 00 00)
+# CHECK: SectionData (
+# CHECK: 0000: 00000000 01000000 |........|
+# CHECK: )
+# CHECK: }
+# CHECK: Section {
+# CHECK: Number: 3
+# CHECK: Name: .text (2E 74 65 78 74 00 00 00)
+# CHECK: VirtualSize: 0x1
+# CHECK: SectionData (
+# CHECK: 0000: C3 |.|
+# CHECK: )
+# CHECK: }
+# CHECK-NOT: Section
+# CHECK: ]
+
+.text
+.global main
+main:
+ret
+
+.comm common_global,4,2
+
+.section .rdata,"dr"
+.secrel32 common_global
+.secidx common_global
+.short 0
diff --git a/test/COFF/section-size.s b/test/COFF/section-size.s
new file mode 100644
index 000000000..28f3f4acb
--- /dev/null
+++ b/test/COFF/section-size.s
@@ -0,0 +1,14 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc %s -o %tmain.obj
+# RUN: echo '.lcomm s, 0x80000000' | llvm-mc -filetype=obj -triple=x86_64-windows-msvc -o %t1.obj
+# RUN: cp %t1.obj %t2.obj
+# RUN: echo '.lcomm s, 0xffffffff' | llvm-mc -filetype=obj -triple=x86_64-windows-msvc -o %t3.obj
+
+# Run: lld-link -entry:main %tmain.obj %t3.obj -out:%t.exe
+
+# RUN: not lld-link -entry:main %tmain.obj %t1.obj %t2.obj -out:%t.exe 2>&1 | FileCheck %s
+# CHECK: error: section larger than 4 GiB: .bss
+
+.globl main
+main:
+ retq
diff --git a/test/COFF/thinlto-archives.ll b/test/COFF/thinlto-archives.ll
index 7a5e36aa1..9a47a3a6f 100644
--- a/test/COFF/thinlto-archives.ll
+++ b/test/COFF/thinlto-archives.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; RUN: rm -fr %T/thinlto-archives
; RUN: mkdir %T/thinlto-archives %T/thinlto-archives/a %T/thinlto-archives/b
; RUN: opt -thinlto-bc -o %T/thinlto-archives/main.obj %s
diff --git a/test/COFF/thinlto-mangled.ll b/test/COFF/thinlto-mangled.ll
index efcd9c3d2..8c901cbd7 100644
--- a/test/COFF/thinlto-mangled.ll
+++ b/test/COFF/thinlto-mangled.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; RUN: opt -thinlto-bc %s -o %t.obj
; RUN: opt -thinlto-bc %S/Inputs/thinlto-mangled-qux.ll -o %T/thinlto-mangled-qux.obj
; RUN: lld-link -out:%t.exe -entry:main %t.obj %T/thinlto-mangled-qux.obj
diff --git a/test/COFF/thinlto.ll b/test/COFF/thinlto.ll
index 11b689d63..f01d0d802 100644
--- a/test/COFF/thinlto.ll
+++ b/test/COFF/thinlto.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; RUN: rm -fr %T/thinlto
; RUN: mkdir %T/thinlto
; RUN: opt -thinlto-bc -o %T/thinlto/main.obj %s
diff --git a/test/COFF/wholearchive.s b/test/COFF/wholearchive.s
new file mode 100644
index 000000000..da9976382
--- /dev/null
+++ b/test/COFF/wholearchive.s
@@ -0,0 +1,19 @@
+# REQEUIRES: x86
+
+# RUN: yaml2obj < %p/Inputs/export.yaml > %t.archive.obj
+# RUN: llvm-ar rcs %t.archive.lib %t.archive.obj
+# RUN: llvm-mc -triple=x86_64-windows-msvc %s -filetype=obj -o %t.main.obj
+
+# RUN: lld-link -dll -out:%t.dll -entry:main %t.main.obj -wholearchive:%t.archive.lib -implib:%t.lib
+# RUN: llvm-readobj %t.lib | FileCheck %s -check-prefix CHECK-IMPLIB
+
+# RUN: lld-link -dll -out:%t.dll -entry:main %t.main.obj -wholearchive %t.archive.lib -implib:%t.lib
+# RUN: llvm-readobj %t.lib | FileCheck %s -check-prefix CHECK-IMPLIB
+
+# CHECK-IMPLIB: Symbol: __imp_exportfn3
+# CHECK-IMPLIB: Symbol: exportfn3
+
+.global main
+.text
+main:
+ ret
diff --git a/test/ELF/Inputs/corrupt-version-reference.so b/test/ELF/Inputs/corrupt-version-reference.so
new file mode 100644
index 000000000..ef6adc6a0
--- /dev/null
+++ b/test/ELF/Inputs/corrupt-version-reference.so
Binary files differ
diff --git a/test/ELF/Inputs/ctors_dtors_priority1.s b/test/ELF/Inputs/ctors_dtors_priority1.s
index 2eb19d7ed..2102dba27 100644
--- a/test/ELF/Inputs/ctors_dtors_priority1.s
+++ b/test/ELF/Inputs/ctors_dtors_priority1.s
@@ -1,5 +1,5 @@
.section .ctors, "aw", @progbits
- .byte 0xA1
+ .quad 0xA1
.section .dtors, "aw", @progbits
- .byte 0xA2
+ .quad 0xA2
diff --git a/test/ELF/Inputs/ctors_dtors_priority2.s b/test/ELF/Inputs/ctors_dtors_priority2.s
index fb85ce87c..e94b15e2b 100644
--- a/test/ELF/Inputs/ctors_dtors_priority2.s
+++ b/test/ELF/Inputs/ctors_dtors_priority2.s
@@ -1,5 +1,5 @@
.section .ctors, "aw", @progbits
- .byte 0xB1
+ .quad 0xB1
.section .dtors, "aw", @progbits
- .byte 0xB2
+ .quad 0xB2
diff --git a/test/ELF/Inputs/ctors_dtors_priority3.s b/test/ELF/Inputs/ctors_dtors_priority3.s
index 96418d351..7bba90cc7 100644
--- a/test/ELF/Inputs/ctors_dtors_priority3.s
+++ b/test/ELF/Inputs/ctors_dtors_priority3.s
@@ -1,5 +1,5 @@
.section .ctors, "aw", @progbits
- .byte 0xC1
+ .quad 0xC1
.section .dtors, "aw", @progbits
- .byte 0xC2
+ .quad 0xC2
diff --git a/test/ELF/Inputs/dso-undef-size.s b/test/ELF/Inputs/dso-undef-size.s
new file mode 100644
index 000000000..424f56f82
--- /dev/null
+++ b/test/ELF/Inputs/dso-undef-size.s
@@ -0,0 +1,4 @@
+.text
+.global foo
+.size foo, 4
+foo:
diff --git a/test/ELF/Inputs/dynamic-list-weak-archive.s b/test/ELF/Inputs/dynamic-list-weak-archive.s
new file mode 100644
index 000000000..dd28fcbd5
--- /dev/null
+++ b/test/ELF/Inputs/dynamic-list-weak-archive.s
@@ -0,0 +1,2 @@
+.globl foo
+foo:
diff --git a/test/ELF/Inputs/eh-frame.s b/test/ELF/Inputs/eh-frame.s
new file mode 100644
index 000000000..0aa4008b7
--- /dev/null
+++ b/test/ELF/Inputs/eh-frame.s
@@ -0,0 +1,3 @@
+.cfi_startproc
+.cfi_def_cfa_offset 32
+.cfi_endproc
diff --git a/test/ELF/Inputs/exclude-libs.s b/test/ELF/Inputs/exclude-libs.s
new file mode 100644
index 000000000..6d05c5e3a
--- /dev/null
+++ b/test/ELF/Inputs/exclude-libs.s
@@ -0,0 +1,3 @@
+.globl fn
+fn:
+ nop
diff --git a/test/ELF/Inputs/gdb-index-a.elf b/test/ELF/Inputs/gdb-index-a.elf
deleted file mode 100644
index 9b90b0dc2..000000000
--- a/test/ELF/Inputs/gdb-index-a.elf
+++ /dev/null
Binary files differ
diff --git a/test/ELF/Inputs/gdb-index-b.elf b/test/ELF/Inputs/gdb-index-b.elf
deleted file mode 100644
index b3356d8c7..000000000
--- a/test/ELF/Inputs/gdb-index-b.elf
+++ /dev/null
Binary files differ
diff --git a/test/ELF/Inputs/gdb-index.s b/test/ELF/Inputs/gdb-index.s
new file mode 100644
index 000000000..907a66d35
--- /dev/null
+++ b/test/ELF/Inputs/gdb-index.s
@@ -0,0 +1,73 @@
+.text
+.Ltext0:
+.globl main2
+.type main2, @function
+main2:
+ nop
+ nop
+.Letext0:
+
+.section .debug_info,"",@progbits
+.long 0x30
+.value 0x4
+.long 0
+.byte 0x8
+.uleb128 0x1
+.quad .Ltext0
+.quad .Letext0-.Ltext0
+.long 0
+.long 0
+.long 0
+.long 0
+.byte 0x63
+.byte 0x88
+.byte 0xb4
+.byte 0x61
+.byte 0xaa
+.byte 0xb6
+.byte 0xb0
+.byte 0x67
+
+.section .debug_abbrev,"",@progbits
+.uleb128 0x1
+.uleb128 0x11
+.byte 0
+.uleb128 0x11
+.uleb128 0x1
+.uleb128 0x12
+.uleb128 0x7
+.uleb128 0x10
+.uleb128 0x17
+.uleb128 0x2130
+.uleb128 0xe
+.uleb128 0x1b
+.uleb128 0xe
+.uleb128 0x2134
+.uleb128 0x19
+.uleb128 0x2133
+.uleb128 0x17
+.uleb128 0x2131
+.uleb128 0x7
+.byte 0
+.byte 0
+.byte 0
+
+.section .debug_gnu_pubnames,"",@progbits
+.long 0x18
+.value 0x2
+.long 0
+.long 0x33
+.long 0x18
+.byte 0x30
+.string "main2"
+.long 0
+
+.section .debug_gnu_pubtypes,"",@progbits
+.long 0x17
+.value 0x2
+.long 0
+.long 0x33
+.long 0x2b
+.byte 0x90
+.string "int"
+.long 0
diff --git a/test/ELF/Inputs/gnu-ifunc-dso.s b/test/ELF/Inputs/gnu-ifunc-dso.s
new file mode 100644
index 000000000..bd8271871
--- /dev/null
+++ b/test/ELF/Inputs/gnu-ifunc-dso.s
@@ -0,0 +1,3 @@
+.type foo STT_GNU_IFUNC
+.globl foo
+foo:
diff --git a/test/ELF/Inputs/mips-micro.s b/test/ELF/Inputs/mips-micro.s
new file mode 100644
index 000000000..0d0b11f6f
--- /dev/null
+++ b/test/ELF/Inputs/mips-micro.s
@@ -0,0 +1,12 @@
+ .text
+ .set micromips
+ .global foo
+ .type foo,@function
+foo:
+ nop
+
+ .set nomicromips
+ .global bar
+ .type bar,@function
+bar:
+ nop
diff --git a/test/ELF/Inputs/symver-archive1.s b/test/ELF/Inputs/symver-archive1.s
new file mode 100644
index 000000000..be7c64494
--- /dev/null
+++ b/test/ELF/Inputs/symver-archive1.s
@@ -0,0 +1,6 @@
+.text
+.globl x
+.type x, @function
+x:
+
+.symver x, xx@@VER
diff --git a/test/ELF/Inputs/symver-archive2.s b/test/ELF/Inputs/symver-archive2.s
new file mode 100644
index 000000000..a9b9d0b0a
--- /dev/null
+++ b/test/ELF/Inputs/symver-archive2.s
@@ -0,0 +1 @@
+call xx@PLT
diff --git a/test/ELF/Inputs/undefined-error.s b/test/ELF/Inputs/undefined-error.s
new file mode 100644
index 000000000..84ac4f121
--- /dev/null
+++ b/test/ELF/Inputs/undefined-error.s
@@ -0,0 +1 @@
+callq fmod@PLT
diff --git a/test/ELF/Inputs/verdef-defaultver.s b/test/ELF/Inputs/verdef-defaultver.s
index 6664d62c9..d9e7f3829 100644
--- a/test/ELF/Inputs/verdef-defaultver.s
+++ b/test/ELF/Inputs/verdef-defaultver.s
@@ -1,4 +1,7 @@
+.global b@V1
b@V1 = b_1
+
+.global b@@V2
b@@V2 = b_2
.globl a
diff --git a/test/ELF/Inputs/verneed.so.sh b/test/ELF/Inputs/verneed.so.sh
deleted file mode 100755
index 3423f678e..000000000
--- a/test/ELF/Inputs/verneed.so.sh
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/bin/sh -eu
-
-# This script was used to produce the verneed{1,2}.so files.
-
-tmp=$(mktemp -d)
-
-echo "v1 {}; v2 {}; v3 {}; { local: *; };" > $tmp/verneed.script
-
-cat > $tmp/verneed1.s <<eof
-.globl f1_v1
-f1_v1:
-ret
-
-.globl f1_v2
-f1_v2:
-ret
-
-.globl f1_v3
-f1_v3:
-ret
-
-.symver f1_v1, f1@v1
-.symver f1_v2, f1@v2
-.symver f1_v3, f1@@v3
-
-.globl f2_v1
-f2_v1:
-ret
-
-.globl f2_v2
-f2_v2:
-ret
-
-.symver f2_v1, f2@v1
-.symver f2_v2, f2@@v2
-
-.globl f3_v1
-f3_v1:
-ret
-
-.symver f3_v1, f3@v1
-eof
-
-as -o $tmp/verneed1.o $tmp/verneed1.s
-ld.gold -shared -o verneed1.so $tmp/verneed1.o --version-script $tmp/verneed.script -soname verneed1.so.0
-
-cat > $tmp/verneed2.s <<eof
-.globl g1_v1
-g1_v1:
-ret
-
-.symver g1_v1, g1@@v1
-eof
-
-as -o $tmp/verneed2.o $tmp/verneed2.s
-ld.gold -shared -o verneed2.so $tmp/verneed2.o --version-script $tmp/verneed.script -soname verneed2.so.0
-
-rm -rf $tmp
diff --git a/test/ELF/Inputs/verneed1.s b/test/ELF/Inputs/verneed1.s
new file mode 100644
index 000000000..a342d7dd7
--- /dev/null
+++ b/test/ELF/Inputs/verneed1.s
@@ -0,0 +1,32 @@
+.globl f1_v1
+f1_v1:
+ret
+
+.globl f1_v2
+f1_v2:
+ret
+
+.globl f1_v3
+f1_v3:
+ret
+
+.symver f1_v1, f1@v1
+.symver f1_v2, f1@v2
+.symver f1_v3, f1@@v3
+
+.globl f2_v1
+f2_v1:
+ret
+
+.globl f2_v2
+f2_v2:
+ret
+
+.symver f2_v1, f2@v1
+.symver f2_v2, f2@@v2
+
+.globl f3_v1
+f3_v1:
+ret
+
+.symver f3_v1, f3@v1
diff --git a/test/ELF/Inputs/verneed1.so b/test/ELF/Inputs/verneed1.so
deleted file mode 100755
index 744852b96..000000000
--- a/test/ELF/Inputs/verneed1.so
+++ /dev/null
Binary files differ
diff --git a/test/ELF/Inputs/verneed2.s b/test/ELF/Inputs/verneed2.s
new file mode 100644
index 000000000..1b46de688
--- /dev/null
+++ b/test/ELF/Inputs/verneed2.s
@@ -0,0 +1,5 @@
+.globl g1_v1
+g1_v1:
+ret
+
+.symver g1_v1, g1@@v1
diff --git a/test/ELF/Inputs/verneed2.so b/test/ELF/Inputs/verneed2.so
deleted file mode 100755
index ba6c05ee6..000000000
--- a/test/ELF/Inputs/verneed2.so
+++ /dev/null
Binary files differ
diff --git a/test/ELF/Inputs/version-script-no-warn2.s b/test/ELF/Inputs/version-script-no-warn2.s
new file mode 100644
index 000000000..59de9d470
--- /dev/null
+++ b/test/ELF/Inputs/version-script-no-warn2.s
@@ -0,0 +1 @@
+call foo@plt
diff --git a/test/ELF/Inputs/version-script-weak.s b/test/ELF/Inputs/version-script-weak.s
new file mode 100644
index 000000000..09f5cf09d
--- /dev/null
+++ b/test/ELF/Inputs/version-script-weak.s
@@ -0,0 +1,4 @@
+.text
+.globl foo
+.type foo,@function
+foo:
diff --git a/test/ELF/Inputs/weak-undef-lazy.s b/test/ELF/Inputs/weak-undef-lazy.s
new file mode 100644
index 000000000..c77477315
--- /dev/null
+++ b/test/ELF/Inputs/weak-undef-lazy.s
@@ -0,0 +1,3 @@
+.global foobar
+foobar:
+ nop
diff --git a/test/ELF/Inputs/wrap-dynamic-undef.s b/test/ELF/Inputs/wrap-dynamic-undef.s
new file mode 100644
index 000000000..ade79556d
--- /dev/null
+++ b/test/ELF/Inputs/wrap-dynamic-undef.s
@@ -0,0 +1,2 @@
+.global foo
+foo:
diff --git a/test/ELF/Inputs/wrap-no-real.s b/test/ELF/Inputs/wrap-no-real.s
new file mode 100644
index 000000000..2fd1bcc6b
--- /dev/null
+++ b/test/ELF/Inputs/wrap-no-real.s
@@ -0,0 +1,3 @@
+.globl foo, __wrap_foo
+foo = 0x11000
+__wrap_foo = 0x11010
diff --git a/test/ELF/Inputs/wrap-no-real2.s b/test/ELF/Inputs/wrap-no-real2.s
new file mode 100644
index 000000000..dbcb0889b
--- /dev/null
+++ b/test/ELF/Inputs/wrap-no-real2.s
@@ -0,0 +1,2 @@
+.globl __real_foo
+__real_foo = 0x11020
diff --git a/test/ELF/aarch64-gnu-ifunc-plt.s b/test/ELF/aarch64-gnu-ifunc-plt.s
index be9a8a7e6..513867567 100644
--- a/test/ELF/aarch64-gnu-ifunc-plt.s
+++ b/test/ELF/aarch64-gnu-ifunc-plt.s
@@ -1,7 +1,7 @@
// 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: ld.lld --hash-style=sysv %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
diff --git a/test/ELF/aarch64-gnu-ifunc.s b/test/ELF/aarch64-gnu-ifunc.s
index 46f4a292d..4e0dc3280 100644
--- a/test/ELF/aarch64-gnu-ifunc.s
+++ b/test/ELF/aarch64-gnu-ifunc.s
@@ -15,7 +15,7 @@
// CHECK-NEXT: Address: [[RELA:.*]]
// CHECK-NEXT: Offset: 0x158
// CHECK-NEXT: Size: 48
-// CHECK-NEXT: Link: 6
+// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 8
// CHECK-NEXT: EntrySize: 24
diff --git a/test/ELF/aarch64-got-reloc.s b/test/ELF/aarch64-got-reloc.s
index fec1ad6a1..1882dcd43 100644
--- a/test/ELF/aarch64-got-reloc.s
+++ b/test/ELF/aarch64-got-reloc.s
@@ -9,8 +9,8 @@
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x30000
-// CHECK-NEXT: Offset: 0x20000
+// CHECK-NEXT: Address:
+// CHECK-NEXT: Offset:
// CHECK-NEXT: Size: 8
// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
diff --git a/test/ELF/aarch64-got-relocations.s b/test/ELF/aarch64-got-relocations.s
index 13ee09a89..a7745b059 100644
--- a/test/ELF/aarch64-got-relocations.s
+++ b/test/ELF/aarch64-got-relocations.s
@@ -1,6 +1,6 @@
# REQUIRES: aarch64
# RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-cloudabi %s -o %t.o
-# RUN: ld.lld -pie %t.o -o %t
+# RUN: ld.lld --hash-style=sysv -pie %t.o -o %t
# RUN: llvm-readobj -r %t | FileCheck %s
# If we're addressing a global relatively through the GOT, we still need to
diff --git a/test/ELF/aarch64-ldprel-lo19-invalid.s b/test/ELF/aarch64-ldprel-lo19-invalid.s
new file mode 100644
index 000000000..9552b9925
--- /dev/null
+++ b/test/ELF/aarch64-ldprel-lo19-invalid.s
@@ -0,0 +1,11 @@
+# REQUIRES: aarch64
+
+# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-none %s -o %t.o
+# RUN: not ld.lld -shared %t.o -o %t 2>&1 | FileCheck %s
+
+# CHECK: relocation R_AARCH64_LD_PREL_LO19 out of range
+
+ ldr x8, patatino
+ .data
+ .zero 2000000
+patatino:
diff --git a/test/ELF/aarch64-lo12-alignment.s b/test/ELF/aarch64-lo12-alignment.s
new file mode 100644
index 000000000..137832dc3
--- /dev/null
+++ b/test/ELF/aarch64-lo12-alignment.s
@@ -0,0 +1,45 @@
+// REQUIRES: aarch64
+// RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux %s -o %t
+// RUN: not ld.lld %t -o %t2 2>&1 | FileCheck %s
+
+// Test derived from a typical ODR violation where a global is declared
+// extern int but defined as a half or byte sized type.
+ .section .text
+ .globl _start
+ .type _start, %function
+// Access foo2 as if it were an aligned 32-bit int, expect an error as
+// foo is not aligned
+
+_start:
+ ldrb w2, [x0, #:lo12:foo1] // Ok as no shift involved
+ ldrh w2, [x0, #:lo12:foo1] // Error foo1 is not 2-byte aligned
+ ldrh w2, [x0, #:lo12:foo2] // Ok as foo2 is 2-byte aligned
+ ldr w2, [x0, #:lo12:foo2] // Error foo2 is not 4-byte aligned
+ ldr w2, [x0, #:lo12:foo4] // Ok as foo4 is 4-byte aligned
+ ldr x3, [x0, #:lo12:foo4] // Error foo4 is not 8-byte aligned
+ ldr x3, [x0, #:lo12:foo8] // Ok as foo8 is 8-byte aligned
+ ldr q0, [x0, #:lo12:foo8] // Error foo8 is not 16-byte aligned
+ ldr q0, [x0, #:lo12:foo16] // Ok as foo16 is 16-byte aligned
+
+ .section .data.bool, "a", @nobits
+ .balign 16
+ .globl foo16
+ .globl foo1
+ .globl foo2
+ .globl foo4
+ .globl foo8
+foo16:
+ .space 1
+foo1:
+ .space 1
+foo2:
+ .space 2
+foo4:
+ .space 4
+foo8:
+ .space 8
+
+// CHECK: improper alignment for relocation R_AARCH64_LDST16_ABS_LO12_NC
+// CHECK-NEXT: improper alignment for relocation R_AARCH64_LDST32_ABS_LO12_NC
+// CHECK-NEXT: improper alignment for relocation R_AARCH64_LDST64_ABS_LO12_NC
+// CHECK-NEXT: improper alignment for relocation R_AARCH64_LDST128_ABS_LO12_NC
diff --git a/test/ELF/aarch64-load-alignment.s b/test/ELF/aarch64-load-alignment.s
new file mode 100644
index 000000000..d0c51c5b9
--- /dev/null
+++ b/test/ELF/aarch64-load-alignment.s
@@ -0,0 +1,11 @@
+# REQUIRES: aarch64
+
+# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-none %s -o %t.o
+# RUN: not ld.lld -shared %t.o -o %t 2>&1 | FileCheck %s
+
+# CHECK: improper alignment for relocation R_AARCH64_LD_PREL_LO19
+
+ ldr x8, patatino
+ .data
+ .zero 5
+patatino:
diff --git a/test/ELF/aarch64-tls-gdie.s b/test/ELF/aarch64-tls-gdie.s
index c66ea6cfc..ab7461ac2 100644
--- a/test/ELF/aarch64-tls-gdie.s
+++ b/test/ELF/aarch64-tls-gdie.s
@@ -2,7 +2,7 @@
// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=aarch64-pc-linux
// RUN: llvm-mc %p/Inputs/aarch64-tls-gdie.s -o %t2.o -filetype=obj -triple=aarch64-pc-linux
// RUN: ld.lld %t2.o -o %t2.so -shared
-// RUN: ld.lld %t.o %t2.so -o %t
+// RUN: ld.lld --hash-style=sysv %t.o %t2.so -o %t
// RUN: llvm-readobj -s %t | FileCheck --check-prefix=SEC %s
// RUN: llvm-objdump -d %t | FileCheck %s
diff --git a/test/ELF/aarch64-tls-ie.s b/test/ELF/aarch64-tls-ie.s
index 81ca326af..8b7431093 100644
--- a/test/ELF/aarch64-tls-ie.s
+++ b/test/ELF/aarch64-tls-ie.s
@@ -2,7 +2,7 @@
# RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-freebsd %p/Inputs/aarch64-tls-ie.s -o %tdso.o
# RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-freebsd %s -o %tmain.o
# RUN: ld.lld -shared %tdso.o -o %tdso.so
-# RUN: ld.lld %tmain.o %tdso.so -o %tout
+# RUN: ld.lld --hash-style=sysv %tmain.o %tdso.so -o %tout
# RUN: llvm-objdump -d %tout | FileCheck %s
# RUN: llvm-readobj -s -r %tout | FileCheck -check-prefix=RELOC %s
# REQUIRES: aarch64
diff --git a/test/ELF/aarch64-tls-static.s b/test/ELF/aarch64-tls-static.s
index 24306d5d7..309a2e9f5 100644
--- a/test/ELF/aarch64-tls-static.s
+++ b/test/ELF/aarch64-tls-static.s
@@ -1,6 +1,6 @@
// REQUIRES: aarch64
// RUN: llvm-mc %s -o %t.o -triple aarch64-pc-linux -filetype=obj
-// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: ld.lld --hash-style=sysv %t.o -o %t.so -shared
// RUN: llvm-readobj -s %t.so | FileCheck --check-prefix=SEC %s
// RUN: llvm-objdump -d %t.so | FileCheck %s
diff --git a/test/ELF/aarch64-tlsdesc.s b/test/ELF/aarch64-tlsdesc.s
index 09dfd04d8..b7c2e65a1 100644
--- a/test/ELF/aarch64-tlsdesc.s
+++ b/test/ELF/aarch64-tlsdesc.s
@@ -1,6 +1,6 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-pc-linux %s -o %t.o
-// RUN: ld.lld -shared %t.o -o %t.so
+// RUN: ld.lld --hash-style=sysv -shared %t.o -o %t.so
// RUN: llvm-objdump -d %t.so | FileCheck %s
// RUN: llvm-readobj -r %t.so | FileCheck --check-prefix=REL %s
diff --git a/test/ELF/aarch64-undefined-weak.s b/test/ELF/aarch64-undefined-weak.s
index 1c2121364..35f504174 100644
--- a/test/ELF/aarch64-undefined-weak.s
+++ b/test/ELF/aarch64-undefined-weak.s
@@ -30,9 +30,11 @@ _start:
.xword target - .
// R_AARCH64_PREL16
.hword target - .
+// R_AARCH64_LD_PREL_LO19
+ ldr x8, target
// CHECK: Disassembly of section .text:
-// 131076 = 0x20004
+// 131072 = 0x20000
// CHECK: 20000: {{.*}} b #4
// CHECK-NEXT: 20004: {{.*}} bl #4
// CHECK-NEXT: 20008: {{.*}} b.eq #4
@@ -43,3 +45,5 @@ _start:
// CHECK-NEXT: 2001c: {{.*}} .word 0x00000000
// CHECK-NEXT: 20020: {{.*}} .word 0x00000000
// CHECK-NEXT: 20024: {{.*}} .short 0x0000
+// CHECK: $x.2:
+// CHECK-NEXT: 20026: {{.*}} ldr x8, #0
diff --git a/test/ELF/abs-hidden.s b/test/ELF/abs-hidden.s
index 5fad4cf6c..82d19cdc0 100644
--- a/test/ELF/abs-hidden.s
+++ b/test/ELF/abs-hidden.s
@@ -1,7 +1,7 @@
// 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/abs-hidden.s -o %t2.o
-// RUN: ld.lld %t.o %t2.o -o %t.so -shared
+// RUN: ld.lld --hash-style=sysv %t.o %t2.o -o %t.so -shared
// RUN: llvm-readobj -r -s -section-data %t.so | FileCheck %s
.quad foo
diff --git a/test/ELF/allow-multiple-definition.s b/test/ELF/allow-multiple-definition.s
index e4637e1a5..c54438d9f 100644
--- a/test/ELF/allow-multiple-definition.s
+++ b/test/ELF/allow-multiple-definition.s
@@ -8,6 +8,11 @@
# RUN: llvm-objdump -d %t3 | FileCheck %s
# RUN: llvm-objdump -d %t4 | FileCheck -check-prefix=REVERT %s
+# RUN: ld.lld -z muldefs %t1 %t2 -o %t3
+# RUN: ld.lld -z muldefs %t2 %t1 -o %t4
+# RUN: llvm-objdump -d %t3 | FileCheck %s
+# RUN: llvm-objdump -d %t4 | FileCheck -check-prefix=REVERT %s
+
# inputs contain different constants for instuction movl.
# Tests below checks that order of files in command line
# affects on what symbol will be used.
diff --git a/test/ELF/allow-shlib-undefined.s b/test/ELF/allow-shlib-undefined.s
index 2d068b0f6..abb0351db 100644
--- a/test/ELF/allow-shlib-undefined.s
+++ b/test/ELF/allow-shlib-undefined.s
@@ -1,3 +1,4 @@
+# REQUIRES: x86
# --allow-shlib-undefined and --no-allow-shlib-undefined are fully
# ignored in linker implementation.
# --allow-shlib-undefined is set by default
diff --git a/test/ELF/amdgpu-relocs.s b/test/ELF/amdgpu-relocs.s
index 1adb1faf2..8b5a61ed2 100644
--- a/test/ELF/amdgpu-relocs.s
+++ b/test/ELF/amdgpu-relocs.s
@@ -1,5 +1,5 @@
# RUN: llvm-mc -filetype=obj -triple=amdgcn--amdhsa -mcpu=fiji %s -o %t.o
-# RUN: ld.lld -shared %t.o -o %t.so
+# RUN: ld.lld --hash-style=sysv -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
@@ -65,10 +65,23 @@ ptr:
.quad temp
.size ptr, 8
+# R_AMDGPU_RELATIVE64:
+ .type temp2, @object
+ .local temp2
+ .size temp2, 4
+
+ .type ptr2, @object
+ .globl ptr2
+ .size ptr2, 8
+ .p2align 3
+ptr2:
+ .quad temp2
+
# 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_RELATIVE64 - 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
diff --git a/test/ELF/arm-copy.s b/test/ELF/arm-copy.s
index e5ce1577b..dc9e3628d 100644
--- a/test/ELF/arm-copy.s
+++ b/test/ELF/arm-copy.s
@@ -2,7 +2,7 @@
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %p/Inputs/relocation-copy-arm.s -o %t2.o
// RUN: ld.lld -shared %t2.o -o %t2.so
-// RUN: ld.lld %t.o %t2.so -o %t3
+// RUN: ld.lld --hash-style=sysv %t.o %t2.so -o %t3
// RUN: llvm-readobj -s -r --expand-relocs -symbols %t3 | FileCheck %s
// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t3 | FileCheck -check-prefix=CODE %s
// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi -section=.rodata %t3 | FileCheck -check-prefix=RODATA %s
diff --git a/test/ELF/arm-exidx-canunwind.s b/test/ELF/arm-exidx-canunwind.s
index e98ec0ec8..96a7808e8 100644
--- a/test/ELF/arm-exidx-canunwind.s
+++ b/test/ELF/arm-exidx-canunwind.s
@@ -72,7 +72,7 @@ _start:
// 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:
+// CHECK-EXIDX-NEXT: Contents of section .ARM.extab:
// 100ec + f28 = 11014 = __gxx_personality_v0
// CHECK-EXIDX-NEXT: 100ec 280f0000 b0b0b000 00000000
diff --git a/test/ELF/arm-exidx-gc.s b/test/ELF/arm-exidx-gc.s
index b1a5be54a..1336c256f 100644
--- a/test/ELF/arm-exidx-gc.s
+++ b/test/ELF/arm-exidx-gc.s
@@ -110,16 +110,15 @@ _start:
// 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)
+// 100dc + f34 = 11010 = func2 (100e0 + 1c = 100fc = .ARM.extab)
// 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:
+// CHECK-EXIDX-NEXT: Contents of section .ARM.extab:
// 100fc + f18 = 11014 = __gxx_personality_v0
// CHECK-EXIDX-NEXT: 100fc 180f0000 b0b0b000
diff --git a/test/ELF/arm-exidx-shared.s b/test/ELF/arm-exidx-shared.s
index 13628405e..b43575eaa 100644
--- a/test/ELF/arm-exidx-shared.s
+++ b/test/ELF/arm-exidx-shared.s
@@ -1,5 +1,5 @@
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
-// RUN: ld.lld %t --shared -o %t2 2>&1
+// RUN: ld.lld --hash-style=sysv %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
@@ -40,6 +40,6 @@ __aeabi_unwind_cpp_pr0:
// CHECK-NEXT: Section (6) .rel.plt {
// CHECK-NEXT: 0x200C R_ARM_JUMP_SLOT __gxx_personality_v0
-// CHECK-EXTAB: Contents of section .ARM.extab.text.func2:
+// CHECK-EXTAB: Contents of section .ARM.extab:
// 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
index 2561a4d34..01ffc600b 100644
--- a/test/ELF/arm-gnu-ifunc-plt.s
+++ b/test/ELF/arm-gnu-ifunc-plt.s
@@ -1,7 +1,7 @@
// 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: ld.lld --hash-style=sysv %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
@@ -61,9 +61,9 @@
// DISASM: $d:
// DISASM-NEXT: 11050: c0 0f 00 00 .word 0x00000fc0
// Alignment to 16 byte boundary not strictly necessary on ARM, but harmless
-// DISASM-NEXT: 11054: 00 00 00 00 .word 0x00000000
-// DISASM-NEXT: 11058: 00 00 00 00 .word 0x00000000
-// DISASM-NEXT: 1105c: 00 00 00 00 .word 0x00000000
+// DISASM-NEXT: 11054: d4 d4 d4 d4 .word 0xd4d4d4d4
+// DISASM-NEXT: 11058: d4 d4 d4 d4 .word 0xd4d4d4d4
+// DISASM-NEXT: 1105c: d4 d4 d4 d4 .word 0xd4d4d4d4
// DISASM: $a:
// DISASM-NEXT: 11060: 04 c0 9f e5 ldr r12, [pc, #4]
// DISASM-NEXT: 11064: 0f c0 8c e0 add r12, r12, pc
diff --git a/test/ELF/arm-got-relative.s b/test/ELF/arm-got-relative.s
index db76711fe..cf8b0a64a 100644
--- a/test/ELF/arm-got-relative.s
+++ b/test/ELF/arm-got-relative.s
@@ -1,6 +1,6 @@
// REQUIRES: arm
// RUN: llvm-mc -position-independent -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
-// RUN: ld.lld %t.o -shared -o %t
+// RUN: ld.lld --hash-style=sysv %t.o -shared -o %t
// RUN: llvm-readobj -s -symbols -dyn-relocations %t | FileCheck %s
// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t | FileCheck -check-prefix=CODE %s
.syntax unified
@@ -16,9 +16,9 @@ _start:
bx lr
.align 2
.LGOT:
- // gas implicitly uses (GOT_PREL) for _GLOBAL_OFFSET_TABLE_ in PIC
- // llvm-mc needs the (GOT_PREL) suffix or it generates R_ARM_REL32
- .word _GLOBAL_OFFSET_TABLE_(GOT_PREL) - (.LPIC+8)
+ // gas implicitly uses (R_ARM_BASE_PREL) for _GLOBAL_OFFSET_TABLE_ in PIC
+ // llvm-mc generates R_ARM_REL32, this will need updating when MC changes
+ .word _GLOBAL_OFFSET_TABLE_ - (.LPIC+8)
.word function(GOT)
.globl function
@@ -28,17 +28,17 @@ function:
bx lr
// CHECK: Dynamic Relocations {
-// CHECK-NEXT: 0x204C R_ARM_GLOB_DAT function 0x0
+// CHECK-NEXT: 0x2048 R_ARM_GLOB_DAT function 0x0
// CHECK: Name: _GLOBAL_OFFSET_TABLE_
-// CHECK-NEXT: Value: 0x0
+// CHECK-NEXT: Value: 0x2048
// CHECK-NEXT: Size:
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
// CHECK-NEXT: Other [
// CHECK-NEXT: STV_HIDDEN
// CHECK-NEXT: ]
-// CHECK-NEXT: Section: Absolute
+// CHECK-NEXT: Section: .got
// CODE: Disassembly of section .text:
// CODE-NEXT: _start:
@@ -49,5 +49,5 @@ function:
// CODE:$d.1:
// (_GLOBAL_OFFSET_TABLE_ = 0x2048) - (0x1008 + 8) 0x1038
// CODE-NEXT: 1010: 38 10 00 00
-// (Got(function) - GotBase = 0x4
-// CODE-NEXT: 1014: 04 00 00 00
+// (Got(function) - GotBase = 0x0
+// CODE-NEXT: 1014: 00 00 00 00
diff --git a/test/ELF/arm-icf-exidx.s b/test/ELF/arm-icf-exidx.s
index 6af30285d..629505cb7 100644
--- a/test/ELF/arm-icf-exidx.s
+++ b/test/ELF/arm-icf-exidx.s
@@ -23,7 +23,7 @@ __aeabi_unwind_cpp_pr0:
bx lr
// CHECK: Disassembly of section .text:
-// CHECK-NEXT: f:
+// CHECK-NEXT: g:
// CHECK-NEXT: 11000: 1e ff 2f e1 bx lr
// CHECK: __aeabi_unwind_cpp_pr0:
// CHECK-NEXT: 11004: 00 f0 20 e3 nop
diff --git a/test/ELF/arm-mov-relocs.s b/test/ELF/arm-mov-relocs.s
index 31ccba4cc..7e3ce67e0 100644
--- a/test/ELF/arm-mov-relocs.s
+++ b/test/ELF/arm-mov-relocs.s
@@ -26,14 +26,9 @@ _start:
.section .R_ARM_MOVT_ABS, "ax",%progbits
movt r0, :upper16:label
movt r1, :upper16:label1
-// FIXME: We shouldn't need to multiply by 65536.
-// arguably llvm-mc incorrectly assembles addends for
-// SHT_REL relocated movt instructions. When there is a relocation
-// the interpretation of the addend for SHT_REL is not shifted
- movt r2, :upper16:label2 + (4 * 65536)
+ movt r2, :upper16:label2 + 4
movt r3, :upper16:label3
-// FIXME: We shouldn't need to multiply by 65536 see comment above.
- movt r4, :upper16:label3 + (4 * 65536)
+ movt r4, :upper16:label3 + 4
// CHECK: Disassembly of section .R_ARM_MOVT_ABS
// CHECK: movt r0, #2
// CHECK: movt r1, #2
diff --git a/test/ELF/arm-pie-relative.s b/test/ELF/arm-pie-relative.s
index f965c24f4..f225015eb 100644
--- a/test/ELF/arm-pie-relative.s
+++ b/test/ELF/arm-pie-relative.s
@@ -1,5 +1,5 @@
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
-// RUN: ld.lld %t --pie -o %t2
+// RUN: ld.lld --hash-style=sysv %t --pie -o %t2
// RUN: llvm-readobj -r %t2 | FileCheck %s
// RUN: llvm-objdump -s %t2 | FileCheck %s --check-prefix=GOT
// REQUIRES: arm
diff --git a/test/ELF/arm-plt-reloc.s b/test/ELF/arm-plt-reloc.s
index 1588f745f..217979e9f 100644
--- a/test/ELF/arm-plt-reloc.s
+++ b/test/ELF/arm-plt-reloc.s
@@ -2,7 +2,7 @@
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t2
// RUN: ld.lld %t1 %t2 -o %t
// RUN: llvm-objdump -triple=armv7a-none-linux-gnueabi -d %t | FileCheck %s
-// RUN: ld.lld -shared %t1 %t2 -o %t3
+// RUN: ld.lld --hash-style=sysv -shared %t1 %t2 -o %t3
// RUN: llvm-objdump -triple=armv7a-none-linux-gnueabi -d %t3 | FileCheck -check-prefix=DSO %s
// RUN: llvm-readobj -s -r %t3 | FileCheck -check-prefix=DSOREL %s
// REQUIRES: arm
diff --git a/test/ELF/arm-thumb-branch.s b/test/ELF/arm-thumb-branch.s
index 4dc0280b2..81bf7a3c6 100644
--- a/test/ELF/arm-thumb-branch.s
+++ b/test/ELF/arm-thumb-branch.s
@@ -54,7 +54,7 @@ callee_high:
// CHECK-NEXT: 1001c: ff f3 fd 97 b.w #16777210
// CHECK-NEXT: 10020: 3f f3 ff af bgt.w #1048574
// CHECK-NEXT: 10024: 70 47 bx lr
-// CHECK-NEXT: 10026: 00 00 movs r0, r0
+// CHECK-NEXT: 10026:
// CHECK-NEXT: Disassembly of section .callee2:
// CHECK-NEXT: callee_high:
// CHECK-NEXT: 10028: 70 47 bx lr
diff --git a/test/ELF/arm-thumb-plt-reloc.s b/test/ELF/arm-thumb-plt-reloc.s
index 95a6e9e7a..b92cf0deb 100644
--- a/test/ELF/arm-thumb-plt-reloc.s
+++ b/test/ELF/arm-thumb-plt-reloc.s
@@ -2,7 +2,7 @@
// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t2
// RUN: ld.lld %t1 %t2 -o %t
// RUN: llvm-objdump -triple=thumbv7a-none-linux-gnueabi -d %t | FileCheck %s
-// RUN: ld.lld -shared %t1 %t2 -o %t3
+// RUN: ld.lld --hash-style=sysv -shared %t1 %t2 -o %t3
// RUN: llvm-objdump -triple=thumbv7a-none-linux-gnueabi -d %t3 | FileCheck -check-prefix=DSOTHUMB %s
// RUN: llvm-objdump -triple=armv7a-none-linux-gnueabi -d %t3 | FileCheck -check-prefix=DSOARM %s
// RUN: llvm-readobj -s -r %t3 | FileCheck -check-prefix=DSOREL %s
@@ -30,7 +30,7 @@ _start:
// CHECK-NEXT: 11002: 70 47 bx lr
// CHECK: func3:
// CHECK-NEXT: 11004: 70 47 bx lr
-// CHECK-NEXT: 11006: 00 00 movs r0, r0
+// CHECK-NEXT: 11006: d4 d4
// CHECK: _start:
// 11008 + 4 -12 = 0x11000 = func1
// CHECK-NEXT: 11008: ff f7 fa ff bl #-12
@@ -49,7 +49,7 @@ _start:
// DSOTHUMB-NEXT: 1002: 70 47 bx lr
// DSOTHUMB: func3:
// DSOTHUMB-NEXT: 1004: 70 47 bx lr
-// DSOTHUMB-NEXT: 1006: 00 00 movs r0, r0
+// DSOTHUMB-NEXT: 1006: d4 d4
// DSOTHUMB: _start:
// 0x1008 + 0x28 + 4 = 0x1034 = PLT func1
// DSOTHUMB-NEXT: 1008: 00 f0 14 e8 blx #40
diff --git a/test/ELF/arm-thunk-edgecase.s b/test/ELF/arm-thunk-edgecase.s
new file mode 100644
index 000000000..81837c7b3
--- /dev/null
+++ b/test/ELF/arm-thunk-edgecase.s
@@ -0,0 +1,37 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
+// RUN: echo "SECTIONS { \
+// RUN: .text_armfunc 0x1000 : { *(.text_armfunc) } \
+// RUN: .text_thumbfunc 0x11010 : { *(.text_thumbfunc) } \
+// RUN: }" > %tarm_to_thumb.script
+// RUN: echo "SECTIONS { \
+// RUN: .text_thumbfunc 0x1000 : { *(.text_thumbfunc) } \
+// RUN: .text_armfunc 0x1100c : { *(.text_armfunc) } \
+// RUN: }" > %tthumb_to_arm.script
+// RUN: ld.lld -shared -Bsymbolic -script %tarm_to_thumb.script %t.o -o %tarm_to_thumb.so
+// RUN: ld.lld -shared -Bsymbolic -script %tthumb_to_arm.script %t.o -o %tthumb_to_arm.so
+// RUN: llvm-objdump -triple=armv7a-none-linux-gnueabi -d %tarm_to_thumb.so | FileCheck -check-prefix=ARM-TO-THUMB %s
+// RUN: llvm-objdump -triple=thumbv7a-none-linux-gnueabi -d %tthumb_to_arm.so | FileCheck -check-prefix=THUMB-TO-ARM %s
+
+.syntax unified
+
+.arm
+.section .text_armfunc, "ax", %progbits
+.globl armfunc
+armfunc:
+ b thumbfunc
+
+.thumb
+.section .text_thumbfunc, "ax", %progbits
+.globl thumbfunc
+.thumb_func
+thumbfunc:
+ b.w armfunc
+
+// ARM-TO-THUMB: __ARMV7PILongThunk_thumbfunc:
+// ARM-TO-THUMB-NEXT: 1004: fd cf 0f e3 movw r12, #65533
+// ARM-TO-THUMB-NEXT: 1008: 00 c0 40 e3 movt r12, #0
+
+// THUMB-TO-ARM: __ThumbV7PILongThunk_armfunc:
+// THUMB-TO-ARM-NEXT: 1004: 4f f6 fc 7c movw r12, #65532
+// THUMB-TO-ARM-NEXT: 1008: c0 f2 00 0c movt r12, #0
diff --git a/test/ELF/arm-tls-gd-nonpreemptible.s b/test/ELF/arm-tls-gd-nonpreemptible.s
index 650c00800..ebaad4788 100644
--- a/test/ELF/arm-tls-gd-nonpreemptible.s
+++ b/test/ELF/arm-tls-gd-nonpreemptible.s
@@ -2,7 +2,7 @@
// RUN: ld.lld %t -o %t2
// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=armv7a-linux-gnueabi
// RUN: llvm-objdump -s %t2 | FileCheck %s
-// RUN: ld.lld %t --shared -o %t3.so
+// RUN: ld.lld --hash-style=sysv %t --shared -o %t3.so
// RUN: llvm-objdump -s %t3.so | FileCheck -check-prefix=CHECK-SHARED %s
// REQUIRES: arm
diff --git a/test/ELF/arm-tls-gd32.s b/test/ELF/arm-tls-gd32.s
index 206b65d05..a32e26f2a 100644
--- a/test/ELF/arm-tls-gd32.s
+++ b/test/ELF/arm-tls-gd32.s
@@ -1,5 +1,5 @@
// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=armv7a-linux-gnueabi
-// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: ld.lld --hash-style=sysv %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
diff --git a/test/ELF/arm-tls-ie32.s b/test/ELF/arm-tls-ie32.s
index 48120fa68..26e126556 100644
--- a/test/ELF/arm-tls-ie32.s
+++ b/test/ELF/arm-tls-ie32.s
@@ -1,5 +1,5 @@
// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=armv7a-linux-gnueabi
-// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: ld.lld --hash-style=sysv %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
diff --git a/test/ELF/arm-tls-ldm32.s b/test/ELF/arm-tls-ldm32.s
index 47e879102..629dcd038 100644
--- a/test/ELF/arm-tls-ldm32.s
+++ b/test/ELF/arm-tls-ldm32.s
@@ -1,5 +1,5 @@
// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=armv7a-linux-gnueabi
-// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: ld.lld --hash-style=sysv %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
diff --git a/test/ELF/arm-tls-norelax-gd-ie.s b/test/ELF/arm-tls-norelax-gd-ie.s
index 2617089b4..bcee56165 100644
--- a/test/ELF/arm-tls-norelax-gd-ie.s
+++ b/test/ELF/arm-tls-norelax-gd-ie.s
@@ -1,7 +1,7 @@
// 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: ld.lld --hash-style=sysv %t1.so %t.o -o %t
// RUN: llvm-readobj -s -dyn-relocations %t | FileCheck %s
// REQUIRES: arm
diff --git a/test/ELF/arm-tls-norelax-gd-le.s b/test/ELF/arm-tls-norelax-gd-le.s
index 41df72494..788c845b3 100644
--- a/test/ELF/arm-tls-norelax-gd-le.s
+++ b/test/ELF/arm-tls-norelax-gd-le.s
@@ -1,7 +1,7 @@
// 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: ld.lld --hash-style=sysv %t1.so %t.o -o %t
// RUN: llvm-objdump -s %t | FileCheck %s
// REQUIRES: arm
@@ -35,3 +35,7 @@ x:
// Module index is always 1 for executable
// CHECK-NEXT: 13060 01000000 00000000
+
+// Without any definition of __tls_get_addr we get an error
+// RUN: not ld.lld %t.o -o %t 2>&1 | FileCheck --check-prefix=ERR %s
+// ERR: error: undefined symbol: __tls_get_addr
diff --git a/test/ELF/arm-tls-norelax-ie-le.s b/test/ELF/arm-tls-norelax-ie-le.s
index e8c528b40..eb96aa0fa 100644
--- a/test/ELF/arm-tls-norelax-ie-le.s
+++ b/test/ELF/arm-tls-norelax-ie-le.s
@@ -1,7 +1,7 @@
// 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: ld.lld --hash-style=sysv %t1.so %t.o -o %t
// RUN: llvm-objdump -s -triple=armv7a-linux-gnueabi %t | FileCheck %s
// REQUIRES: arm
diff --git a/test/ELF/arm-tls-norelax-ld-le.s b/test/ELF/arm-tls-norelax-ld-le.s
index 9fd822aef..fc5b72b80 100644
--- a/test/ELF/arm-tls-norelax-ld-le.s
+++ b/test/ELF/arm-tls-norelax-ld-le.s
@@ -1,7 +1,7 @@
// 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: ld.lld --hash-style=sysv %t1.so %t.o -o %t
// RUN: llvm-objdump -s %t | FileCheck %s
// REQUIRES: arm
diff --git a/test/ELF/as-needed-no-reloc.s b/test/ELF/as-needed-no-reloc.s
index 68f03d782..f8c34f80a 100644
--- a/test/ELF/as-needed-no-reloc.s
+++ b/test/ELF/as-needed-no-reloc.s
@@ -16,7 +16,7 @@
# CHECK-NEXT: Other: 0
# CHECK-NEXT: Section: Undefined
-# CHECK: NEEDED SharedLibrary ({{.*}}as-needed-no-reloc{{.*}}2.so)
+# CHECK: NEEDED Shared library: [{{.*}}as-needed-no-reloc{{.*}}2.so]
.globl _start
_start:
diff --git a/test/ELF/as-needed.s b/test/ELF/as-needed.s
index 4f1a48aba..37c6103b0 100644
--- a/test/ELF/as-needed.s
+++ b/test/ELF/as-needed.s
@@ -28,13 +28,13 @@
// RUN: ld.lld %t.o %t.script -o %t2
// RUN: llvm-readobj -dynamic-table %t2 | FileCheck -check-prefix=CHECK2 %s
-// CHECK: NEEDED SharedLibrary (shared1)
-// CHECK: NEEDED SharedLibrary (shared2)
-// CHECK: NEEDED SharedLibrary (shared3)
+// CHECK: NEEDED Shared library: [shared1]
+// CHECK: NEEDED Shared library: [shared2]
+// CHECK: NEEDED Shared library: [shared3]
-// CHECK2: NEEDED SharedLibrary (shared1)
-// CHECK2-NOT: NEEDED SharedLibrary (shared2)
-// CHECK2-NOT: NEEDED SharedLibrary (shared3)
+// CHECK2: NEEDED Shared library: [shared1]
+// CHECK2-NOT: NEEDED Shared library: [shared2]
+// CHECK2-NOT: NEEDED Shared library: [shared3]
.global _start
_start:
diff --git a/test/ELF/assignment-archive.s b/test/ELF/assignment-archive.s
new file mode 100644
index 000000000..11753a692
--- /dev/null
+++ b/test/ELF/assignment-archive.s
@@ -0,0 +1,27 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %ta.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux -o %t.o < /dev/null
+# RUN: rm -f %tar.a
+# RUN: llvm-ar rcs %tar.a %ta.o
+
+# RUN: echo "SECTIONS { foo = 1; }" > %t1.script
+# RUN: ld.lld -o %t1.exe --script %t1.script %tar.a %t.o
+# RUN: llvm-readobj -symbols %t1.exe | FileCheck %s
+# CHECK-NOT: bar
+# CHECK: foo
+# CHECK-NOT: bar
+
+# RUN: echo "SECTIONS { zed = foo; }" > %t2.script
+# RUN: ld.lld -o %t2.exe --script %t2.script %tar.a %t.o
+# RUN: llvm-readobj -symbols %t2.exe | FileCheck %s --check-prefix=SYMS
+# SYMS: bar
+# SYMS: foo
+
+.text
+.globl foo
+foo:
+ nop
+
+.globl bar
+bar:
+ nop
diff --git a/test/ELF/auxiliary.s b/test/ELF/auxiliary.s
index 236d0a421..18fbdf05f 100644
--- a/test/ELF/auxiliary.s
+++ b/test/ELF/auxiliary.s
@@ -1,3 +1,4 @@
+# REQUIRES: x86
# 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
diff --git a/test/ELF/avoid-empty-program-headers.s b/test/ELF/avoid-empty-program-headers.s
index 271d1a303..731ecce67 100644
--- a/test/ELF/avoid-empty-program-headers.s
+++ b/test/ELF/avoid-empty-program-headers.s
@@ -42,8 +42,8 @@ _start:
// CHECK-NEXT: Offset: 0x1000
// CHECK-NEXT: VirtualAddress: 0x201000
// CHECK-NEXT: PhysicalAddress: 0x201000
-// CHECK-NEXT: FileSize: 1
-// CHECK-NEXT: MemSize: 1
+// CHECK-NEXT: FileSize: 4096
+// CHECK-NEXT: MemSize: 4096
// CHECK-NEXT: Flags [ (0x5)
// CHECK-NEXT: PF_R (0x4)
// CHECK-NEXT: PF_X (0x1)
@@ -52,7 +52,7 @@ _start:
// CHECK-NEXT: }
// CHECK-NEXT: ProgramHeader {
// CHECK-NEXT: Type: PT_TLS (0x7)
-// CHECK-NEXT: Offset: 0x1001
+// CHECK-NEXT: Offset: 0x2000
// CHECK-NEXT: VirtualAddress: 0x201001
// CHECK-NEXT: PhysicalAddress: 0x201001
// CHECK-NEXT: FileSize: 0
diff --git a/test/ELF/basic-aarch64.s b/test/ELF/basic-aarch64.s
index 144fe6aee..2b128689f 100644
--- a/test/ELF/basic-aarch64.s
+++ b/test/ELF/basic-aarch64.s
@@ -26,7 +26,7 @@ _start:
# CHECK-NEXT: Version: 1
# CHECK-NEXT: Entry: [[ENTRY:0x[0-9A-F]+]]
# CHECK-NEXT: ProgramHeaderOffset: 0x40
-# CHECK-NEXT: SectionHeaderOffset: 0x10098
+# CHECK-NEXT: SectionHeaderOffset: 0x11088
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: HeaderSize: 64
@@ -76,7 +76,7 @@ _start:
# CHECK-NEXT: SHF_STRINGS (0x20)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x1000C
+# CHECK-NEXT: Offset: 0x11000
# CHECK-NEXT: Size: 8
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
@@ -90,7 +90,7 @@ _start:
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x10018
+# CHECK-NEXT: Offset: 0x11008
# CHECK-NEXT: Size: 72
# CHECK-NEXT: Link: 5
# CHECK-NEXT: Info: 2
@@ -104,7 +104,7 @@ _start:
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x10060
+# CHECK-NEXT: Offset: 0x11050
# CHECK-NEXT: Size: 42
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
@@ -118,7 +118,7 @@ _start:
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x1008A
+# CHECK-NEXT: Offset: 0x1107A
# CHECK-NEXT: Size: 13
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
@@ -185,8 +185,8 @@ _start:
# CHECK-NEXT: Offset: 0x1000
# CHECK-NEXT: VirtualAddress: 0x20000
# CHECK-NEXT: PhysicalAddress: 0x20000
-# CHECK-NEXT: FileSize: 12
-# CHECK-NEXT: MemSize: 12
+# CHECK-NEXT: FileSize: 4096
+# CHECK-NEXT: MemSize: 4096
# CHECK-NEXT: Flags [ (0x5)
# CHECK-NEXT: PF_R (0x4)
# CHECK-NEXT: PF_X (0x1)
diff --git a/test/ELF/basic-mips.s b/test/ELF/basic-mips.s
index 4c7a66cf0..c8e9cc2da 100644
--- a/test/ELF/basic-mips.s
+++ b/test/ELF/basic-mips.s
@@ -228,7 +228,7 @@ __start:
# CHECK-NEXT: Other [ (0x2)
# CHECK-NEXT: STV_HIDDEN (0x2)
# CHECK-NEXT: ]
-# CHECK-NEXT: Section: Absolute
+# CHECK-NEXT: Section: .got
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
# CHECK-NEXT: Name: __start
diff --git a/test/ELF/basic-ppc.s b/test/ELF/basic-ppc.s
index 4a36af99e..75704325f 100644
--- a/test/ELF/basic-ppc.s
+++ b/test/ELF/basic-ppc.s
@@ -1,5 +1,5 @@
# RUN: llvm-mc -filetype=obj -triple=powerpc-unknown-freebsd %s -o %t
-# RUN: ld.lld -discard-all -shared %t -o %t2
+# RUN: ld.lld --hash-style=sysv -discard-all -shared %t -o %t2
# RUN: llvm-readobj -file-headers -sections -section-data -program-headers %t2 | FileCheck %s
# REQUIRES: ppc
diff --git a/test/ELF/basic-sparcv9.s b/test/ELF/basic-sparcv9.s
new file mode 100644
index 000000000..d06fcd59d
--- /dev/null
+++ b/test/ELF/basic-sparcv9.s
@@ -0,0 +1,200 @@
+# RUN: llvm-mc -filetype=obj -triple=sparc64-unknown-openbsd %s -o %t
+# RUN: ld.lld %t -o %t2
+# RUN: llvm-readobj -file-headers -sections -program-headers -symbols %t2 \
+# RUN: | FileCheck %s
+# REQUIRES: sparc
+
+# exits with return code 42 on OpenBSD/sparc64
+.global _start
+_start:
+ mov 42, %o0
+ mov 1, %g1
+ ta 0
+
+# CHECK: ElfHeader {
+# CHECK-NEXT: Ident {
+# CHECK-NEXT: Magic: (7F 45 4C 46)
+# CHECK-NEXT: Class: 64-bit (0x2)
+# CHECK-NEXT: DataEncoding: BigEndian (0x2)
+# CHECK-NEXT: FileVersion: 1
+# CHECK-NEXT: OS/ABI: SystemV (0x0)
+# CHECK-NEXT: ABIVersion: 0
+# CHECK-NEXT: Unused: (00 00 00 00 00 00 00)
+# CHECK-NEXT: }
+# CHECK-NEXT: Type: Executable (0x2)
+# CHECK-NEXT: Machine: EM_SPARCV9 (0x2B)
+# CHECK-NEXT: Version: 1
+# CHECK-NEXT: Entry: [[ENTRY:0x[0-9A-F]+]]
+# CHECK-NEXT: ProgramHeaderOffset: 0x40
+# CHECK-NEXT: SectionHeaderOffset: 0x102070
+# 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: 6
+# CHECK-NEXT: StringTableSectionIndex: 4
+# CHECK-NEXT: }
+# CHECK-NEXT: Sections [
+# CHECK-NEXT: Section {
+# CHECK-NEXT: Index: 0
+# CHECK-NEXT: Name: (0)
+# CHECK-NEXT: Type: SHT_NULL (0x0)
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x0
+# CHECK-NEXT: Offset: 0x0
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 0
+# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: }
+# CHECK-NEXT: Section {
+# CHECK-NEXT: Index: 1
+# CHECK-NEXT: Name: .text
+# CHECK-NEXT: Type: SHT_PROGBITS (0x1)
+# CHECK-NEXT: Flags [ (0x6)
+# CHECK-NEXT: SHF_ALLOC (0x2)
+# CHECK-NEXT: SHF_EXECINSTR (0x4)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x200000
+# CHECK-NEXT: Offset: 0x100000
+# CHECK-NEXT: Size: 12
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 4
+# CHECK-NEXT: EntrySize: 0
+# 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: 0x102000
+# CHECK-NEXT: Size: 8
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 1
+# CHECK-NEXT: EntrySize: 0
+# 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: 0x102008
+# CHECK-NEXT: Size: 48
+# CHECK-NEXT: Link: 5
+# CHECK-NEXT: Info: 1
+# CHECK-NEXT: AddressAlignment: 8
+# CHECK-NEXT: EntrySize: 24
+# CHECK-NEXT: }
+# CHECK-NEXT: Section {
+# 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: 0x102038
+# 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: 5
+# CHECK-NEXT: Name: .strtab
+# CHECK-NEXT: Type: SHT_STRTAB (0x3)
+# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x0
+# CHECK-NEXT: Offset: 0x102062
+# CHECK-NEXT: Size: 8
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 1
+# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+# CHECK-NEXT: Symbols [
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: (0)
+# CHECK-NEXT: Value: 0x0
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Local (0x0)
+# CHECK-NEXT: Type: None (0x0)
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Undefined (0x0)
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: _start
+# CHECK-NEXT: Value: [[ENTRY]]
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global (0x1)
+# CHECK-NEXT: Type: None (0x0)
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .text
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+# CHECK-NEXT: ProgramHeaders [
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_PHDR (0x6)
+# CHECK-NEXT: Offset: 0x40
+# CHECK-NEXT: VirtualAddress: 0x100040
+# CHECK-NEXT: PhysicalAddress: 0x100040
+# CHECK-NEXT: FileSize: 224
+# CHECK-NEXT: MemSize: 224
+# CHECK-NEXT: Flags [ (0x4)
+# CHECK-NEXT: PF_R (0x4)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 8
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD (0x1)
+# CHECK-NEXT: Offset: 0x0
+# CHECK-NEXT: VirtualAddress: 0x100000
+# CHECK-NEXT: PhysicalAddress: 0x100000
+# CHECK-NEXT: FileSize: 288
+# CHECK-NEXT: MemSize: 288
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 1048576
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD (0x1)
+# CHECK-NEXT: Offset: 0x100000
+# CHECK-NEXT: VirtualAddress: 0x200000
+# CHECK-NEXT: PhysicalAddress: 0x200000
+# CHECK-NEXT: FileSize: 8192
+# CHECK-NEXT: MemSize: 8192
+# CHECK-NEXT: Flags [ (0x5)
+# CHECK-NEXT: PF_R (0x4)
+# CHECK-NEXT: PF_X (0x1)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 1048576
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_GNU_STACK
+# 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: PF_W
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 0
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
diff --git a/test/ELF/basic.s b/test/ELF/basic.s
index c62a516c1..e9b14e7a0 100644
--- a/test/ELF/basic.s
+++ b/test/ELF/basic.s
@@ -28,7 +28,7 @@ _start:
# CHECK-NEXT: Version: 1
# CHECK-NEXT: Entry: [[ENTRY:0x[0-9A-F]+]]
# CHECK-NEXT: ProgramHeaderOffset: 0x40
-# CHECK-NEXT: SectionHeaderOffset: 0x1080
+# CHECK-NEXT: SectionHeaderOffset: 0x2070
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: HeaderSize: 64
@@ -78,7 +78,7 @@ _start:
# CHECK-NEXT: SHF_STRINGS (0x20)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x1010
+# CHECK-NEXT: Offset: 0x2000
# CHECK-NEXT: Size: 8
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
@@ -92,7 +92,7 @@ _start:
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x1018
+# CHECK-NEXT: Offset: 0x2008
# CHECK-NEXT: Size: 48
# CHECK-NEXT: Link: 5
# CHECK-NEXT: Info: 1
@@ -106,7 +106,7 @@ _start:
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x1048
+# CHECK-NEXT: Offset: 0x2038
# CHECK-NEXT: Size: 42
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
@@ -120,7 +120,7 @@ _start:
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x1072
+# CHECK-NEXT: Offset: 0x2062
# CHECK-NEXT: Size: 8
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
@@ -178,8 +178,8 @@ _start:
# CHECK-NEXT: Offset: 0x1000
# CHECK-NEXT: VirtualAddress: 0x201000
# CHECK-NEXT: PhysicalAddress: 0x201000
-# CHECK-NEXT: FileSize: 16
-# CHECK-NEXT: MemSize: 16
+# CHECK-NEXT: FileSize: 4096
+# CHECK-NEXT: MemSize: 4096
# CHECK-NEXT: Flags [ (0x5)
# CHECK-NEXT: PF_R (0x4)
# CHECK-NEXT: PF_X (0x1)
@@ -246,7 +246,9 @@ _start:
# UNKNOWN_EMUL: unknown emulation: wrong_emul_fbsd
# RUN: not ld.lld %t --lto-partitions=0 2>&1 | FileCheck --check-prefix=NOTHREADS %s
+# RUN: not ld.lld %t --plugin-opt=lto-partitions=0 2>&1 | FileCheck --check-prefix=NOTHREADS %s
# NOTHREADS: --lto-partitions: number of threads must be > 0
# RUN: not ld.lld %t --thinlto-jobs=0 2>&1 | FileCheck --check-prefix=NOTHREADSTHIN %s
+# RUN: not ld.lld %t --plugin-opt=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 cbf67eec8..f4abf13a5 100644
--- a/test/ELF/basic32.s
+++ b/test/ELF/basic32.s
@@ -25,7 +25,7 @@ _start:
# CHECK-NEXT: Version: 1
# CHECK-NEXT: Entry: 0x11000
# CHECK-NEXT: ProgramHeaderOffset: 0x34
-# CHECK-NEXT: SectionHeaderOffset: 0x1068
+# CHECK-NEXT: SectionHeaderOffset: 0x205C
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: HeaderSize: 52
@@ -75,7 +75,7 @@ _start:
# CHECK-NEXT: SHF_STRINGS (0x20)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x100C
+# CHECK-NEXT: Offset: 0x2000
# CHECK-NEXT: Size: 8
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
@@ -89,7 +89,7 @@ _start:
# CHECK-NEXT: Flags [
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x1014
+# CHECK-NEXT: Offset: 0x2008
# CHECK-NEXT: Size: 32
# CHECK-NEXT: Link: 5
# CHECK-NEXT: Info: 1
@@ -103,7 +103,7 @@ _start:
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x1034
+# CHECK-NEXT: Offset: 0x2028
# CHECK-NEXT: Size: 42
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
@@ -117,7 +117,7 @@ _start:
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x105E
+# CHECK-NEXT: Offset: 0x2052
# CHECK-NEXT: Size: 8
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
@@ -155,8 +155,8 @@ _start:
# CHECK-NEXT: Offset: 0x1000
# CHECK-NEXT: VirtualAddress: 0x11000
# CHECK-NEXT: PhysicalAddress: 0x11000
-# CHECK-NEXT: FileSize: 12
-# CHECK-NEXT: MemSize: 12
+# CHECK-NEXT: FileSize: 4096
+# CHECK-NEXT: MemSize: 4096
# CHECK-NEXT: Flags [ (0x5)
# CHECK-NEXT: PF_R (0x4)
# CHECK-NEXT: PF_X (0x1)
diff --git a/test/ELF/build-id.s b/test/ELF/build-id.s
index 2d193478d..1c3cd2a05 100644
--- a/test/ELF/build-id.s
+++ b/test/ELF/build-id.s
@@ -48,15 +48,15 @@ _start:
# DEFAULT: Contents of section .note.test:
# DEFAULT: Contents of section .note.gnu.build-id:
# DEFAULT-NEXT: 04000000 08000000 03000000 474e5500 ............GNU.
-# DEFAULT-NEXT: fd36edb1 f6ff02af
+# DEFAULT-NEXT: bc208f98 7f8e4770
# MD5: Contents of section .note.gnu.build-id:
# MD5-NEXT: 04000000 10000000 03000000 474e5500 ............GNU.
-# MD5-NEXT: fc
+# MD5-NEXT: 8e13f077 f9c94068 9d63c715 6c952cd3
# SHA1: Contents of section .note.gnu.build-id:
# SHA1-NEXT: 04000000 14000000 03000000 474e5500 ............GNU.
-# SHA1-NEXT: 55b1eedb 03b588e1 09987d1d e9a79be7
+# SHA1-NEXT: 754ac1ea 0a2e5144 0f4642b9 3f096bbb
# UUID: Contents of section .note.gnu.build-id:
# UUID-NEXT: 04000000 10000000 03000000 474e5500 ............GNU.
diff --git a/test/ELF/chroot.s b/test/ELF/chroot.s
new file mode 100644
index 000000000..8c97e99af
--- /dev/null
+++ b/test/ELF/chroot.s
@@ -0,0 +1,12 @@
+# REQUIRES: x86
+# RUN: rm -rf %t.dir
+# RUN: mkdir %t.dir
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.dir/chroot.o
+# RUN: ld.lld --chroot %t.dir -o %t.exe /chroot.o
+
+# RUN: echo 'INPUT(/chroot.o)' > %t.dir/scr
+# RUN: ld.lld --chroot %t.dir -o %t.exe /scr
+
+.globl _start
+_start:
+ ret
diff --git a/test/ELF/comment-gc.s b/test/ELF/comment-gc.s
index 8018ff89b..44c08fe9c 100644
--- a/test/ELF/comment-gc.s
+++ b/test/ELF/comment-gc.s
@@ -5,8 +5,7 @@
# 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 .
+# CHECK-NEXT: foo..LLD 1.0.bar
.ident "foo"
diff --git a/test/ELF/common-gc.s b/test/ELF/common-gc.s
new file mode 100644
index 000000000..99fde9ea3
--- /dev/null
+++ b/test/ELF/common-gc.s
@@ -0,0 +1,41 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
+
+# RUN: ld.lld %t -o %t2
+# RUN: llvm-readobj -sections -symbols %t2 | FileCheck %s --check-prefix=NOGC
+
+# NOGC: Name: .bss
+# NOGC-NEXT: Type:
+# NOGC-NEXT: Flags [
+# NOGC-NEXT: SHF_ALLOC
+# NOGC-NEXT: SHF_WRITE
+# NOGC-NEXT: ]
+# NOGC-NEXT: Address:
+# NOGC-NEXT: Offset:
+# NOGC-NEXT: Size: 8
+
+# NOGC: Name: bar
+# NOGC: Name: foo
+
+# RUN: ld.lld -gc-sections %t -o %t1
+# RUN: llvm-readobj -sections -symbols %t1 | FileCheck %s --check-prefix=GC
+
+# GC: Name: .bss
+# GC-NEXT: Type:
+# GC-NEXT: Flags [
+# GC-NEXT: SHF_ALLOC
+# GC-NEXT: SHF_WRITE
+# GC-NEXT: ]
+# GC-NEXT: Address:
+# GC-NEXT: Offset:
+# GC-NEXT: Size: 4
+
+# GC-NOT: Name: bar
+
+.comm foo,4,4
+.comm bar,4,4
+
+.text
+.globl _start
+_start:
+ .quad foo
diff --git a/test/ELF/common-gc2.s b/test/ELF/common-gc2.s
new file mode 100644
index 000000000..165bf6253
--- /dev/null
+++ b/test/ELF/common-gc2.s
@@ -0,0 +1,15 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
+# RUN: ld.lld -gc-sections -export-dynamic %t -o %t1
+# RUN: llvm-readobj --dyn-symbols %t1 | FileCheck %s
+
+# CHECK: Name: bar@
+# CHECK: Name: foo@
+
+.comm foo,4,4
+.comm bar,4,4
+
+.text
+.globl _start
+_start:
+ .quad foo
diff --git a/test/ELF/common-gc3.s b/test/ELF/common-gc3.s
new file mode 100644
index 000000000..e161a635c
--- /dev/null
+++ b/test/ELF/common-gc3.s
@@ -0,0 +1,18 @@
+# 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 .noalloc:
+# 0000 00000000 00000000 ........
+
+ .section .text._start,"ax",@progbits
+ .globl _start
+_start:
+ retq
+
+ .type unused,@object
+ .comm unused,4,4
+
+ .section .noalloc,"",@progbits
+ .quad unused
diff --git a/test/ELF/common.s b/test/ELF/common.s
index c8011a0a5..7f241ee4d 100644
--- a/test/ELF/common.s
+++ b/test/ELF/common.s
@@ -12,13 +12,13 @@
// CHECK-NEXT: ]
// CHECK-NEXT: Address: 0x201000
// CHECK-NEXT: Offset:
-// CHECK-NEXT: Size: 22
+// CHECK-NEXT: Size: 36
// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 16
// CHECK: Name: sym1
-// CHECK-NEXT: Value: 0x201004
+// CHECK-NEXT: Value: 0x201000
// 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: 0x20100C
+// CHECK-NEXT: Value: 0x201008
// 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: 0x201014
+// CHECK-NEXT: Value: 0x201010
// 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: 0x201000
+// CHECK-NEXT: Value: 0x201020
// CHECK-NEXT: Size: 4
// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: Object
diff --git a/test/ELF/compress-debug-sections.s b/test/ELF/compress-debug-sections.s
index 5fb7ee515..a7933e130 100644
--- a/test/ELF/compress-debug-sections.s
+++ b/test/ELF/compress-debug-sections.s
@@ -15,12 +15,17 @@
# ZLIBFLAGS-NEXT: Flags [
# ZLIBFLAGS-NEXT: SHF_COMPRESSED
-# RUN: llvm-dwarfdump %t1 -debug-dump=str | \
+# RUN: llvm-dwarfdump %t1 -debug-str | \
# RUN: FileCheck %s --check-prefix=DEBUGSTR
# DEBUGSTR: .debug_str contents:
# DEBUGSTR-NEXT: AAAAAAAAAAAAAAAAAAAAAAAAAAA
# DEBUGSTR-NEXT: BBBBBBBBBBBBBBBBBBBBBBBBBBB
+## Test alias.
+# RUN: ld.lld %t.o -o %t2 --compress-debug-sections zlib
+# RUN: llvm-objdump -s %t2 | FileCheck %s --check-prefix=ZLIBCONTENT
+# RUN: llvm-readobj -s %t2 | FileCheck %s --check-prefix=ZLIBFLAGS
+
# RUN: not ld.lld %t.o -o %t1 --compress-debug-sections=zlib-gabi 2>&1 | \
# RUN: FileCheck -check-prefix=ERR %s
# ERR: unknown --compress-debug-sections value: zlib-gabi
diff --git a/test/ELF/compressed-debug-conflict.s b/test/ELF/compressed-debug-conflict.s
new file mode 100644
index 000000000..c67bc9201
--- /dev/null
+++ b/test/ELF/compressed-debug-conflict.s
@@ -0,0 +1,29 @@
+# REQUIRES: x86, zlib
+# RUN: llvm-mc -filetype=obj -triple i686-linux-gnu -compress-debug-sections=zlib %s -o %t.o
+# RUN: llvm-readobj -sections %t.o | FileCheck -check-prefix=OBJ %s
+# RUN: not ld.lld %t.o %t.o -o %tout 2>&1 | FileCheck -check-prefix=ERROR %s
+
+# OBJ: Sections [
+# OBJ: Section {
+# OBJ: Index: 3
+# OBJ-NEXT: Name: .debug_line (16)
+# OBJ-NEXT: Type: SHT_PROGBITS (0x1)
+# OBJ-NEXT: Flags [ (0x800)
+# OBJ-NEXT: SHF_COMPRESSED (0x800)
+# OBJ-NEXT: ]
+
+# ERROR: error: duplicate symbol: main
+# ERROR-NEXT: >>> defined at reduced.c:2 (/tmp/reduced.c:2)
+# ERROR-NEXT: >>>
+# ERROR-NEXT: >>> defined at reduced.c:2 (/tmp/reduced.c:2)
+# ERROR-NEXT: >>>
+
+ .text
+ .file "reduced.c"
+ .globl main
+main:
+ .file 1 "/tmp" "reduced.c"
+ .loc 1 2 0
+ xorl %eax, %eax
+ retl
+ .file 2 "/tmp/repeat/repeat/repeat/repeat" "repeat.h"
diff --git a/test/ELF/compressed-debug-input.s b/test/ELF/compressed-debug-input.s
index fbd5b02a0..e091ed9c2 100644
--- a/test/ELF/compressed-debug-input.s
+++ b/test/ELF/compressed-debug-input.s
@@ -1,4 +1,4 @@
-# REQUIRES: zlib
+# REQUIRES: zlib, x86
# 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=ZLIB %s
@@ -39,10 +39,10 @@
# GNU-NEXT: EntrySize: 1
# GNU-NEXT: }
-# RUN: ld.lld %t -o %t.so -shared
+# RUN: ld.lld --hash-style=sysv %t -o %t.so -shared
# RUN: llvm-readobj -sections -section-data %t.so | FileCheck -check-prefix=DATA %s
-# RUN: ld.lld %t2 -o %t2.so -shared
+# RUN: ld.lld --hash-style=sysv %t2 -o %t2.so -shared
# RUN: llvm-readobj -sections -section-data %t2.so | FileCheck -check-prefix=DATA %s
# DATA: Section {
@@ -61,11 +61,11 @@
# DATA-NEXT: AddressAlignment: 1
# DATA-NEXT: EntrySize: 0
# 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: 0000: 6C6F6E67 20756E73 69676E65 6420696E |long unsigned in|
+# DATA-NEXT: 0010: 7400756E 7369676E 65642063 68617200 |t.unsigned char.|
+# DATA-NEXT: 0020: 756E7369 676E6564 20696E74 00636861 |unsigned int.cha|
+# DATA-NEXT: 0030: 72007368 6F727420 756E7369 676E6564 |r.short unsigned|
+# DATA-NEXT: 0040: 20696E74 00 | int.|
# DATA-NEXT: )
# DATA-NEXT: }
diff --git a/test/ELF/copy-errors.s b/test/ELF/copy-errors.s
index d0d6abdf6..0af463812 100644
--- a/test/ELF/copy-errors.s
+++ b/test/ELF/copy-errors.s
@@ -9,6 +9,9 @@
// CHECK: >>> referenced by {{.*}}.o:(.text+0x1)
// CHECK: symbol 'zed' defined in {{.*}}.so has no type
+// RUN: not ld.lld --noinhibit-exec %t.o %t2.so -o %t 2>&1 | FileCheck %s --check-prefix=NOINHIBIT
+// NOINHIBIT: warning: symbol 'zed' defined in {{.*}}.so has no type
+
.global _start
_start:
call bar
diff --git a/test/ELF/copy-in-shared.s b/test/ELF/copy-in-shared.s
index 1d77eaf3a..70439853c 100644
--- a/test/ELF/copy-in-shared.s
+++ b/test/ELF/copy-in-shared.s
@@ -4,7 +4,7 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t2.o
// RUN: not ld.lld %t2.o %t1.so -o %t2.so -shared 2>&1 | FileCheck %s
-// CHECK: can't create dynamic relocation R_X86_64_64 against symbol: foo
+// CHECK: can't create dynamic relocation R_X86_64_64 against symbol: foo in readonly segment
// CHECK: >>> defined in {{.*}}.so
// CHECK: >>> referenced by {{.*}}.o:(.text+0x0)
diff --git a/test/ELF/copy-rel-pie.s b/test/ELF/copy-rel-pie.s
index 769a2431d..dcccf8e30 100644
--- a/test/ELF/copy-rel-pie.s
+++ b/test/ELF/copy-rel-pie.s
@@ -1,7 +1,7 @@
// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux
// RUN: llvm-mc %p/Inputs/copy-rel-pie.s -o %t2.o -filetype=obj -triple=x86_64-pc-linux
// RUN: ld.lld %t2.o -o %t2.so -shared
-// RUN: ld.lld %t.o %t2.so -o %t.exe -pie
+// RUN: ld.lld --hash-style=sysv %t.o %t2.so -o %t.exe -pie
// RUN: llvm-readobj -s -r %t.exe | FileCheck %s
// RUN: llvm-objdump -d %t.exe | FileCheck --check-prefix=DISASM %s
diff --git a/test/ELF/corrupted-version-reference.s b/test/ELF/corrupted-version-reference.s
new file mode 100644
index 000000000..1a41bbc42
--- /dev/null
+++ b/test/ELF/corrupted-version-reference.s
@@ -0,0 +1,11 @@
+# RUN: llvm-mc -triple=mips64-unknown-freebsd %s -filetype=obj -o %t.o
+# RUN: not ld.lld %t.o %S/Inputs/corrupt-version-reference.so -o %t.exe 2>&1 | FileCheck %s
+# REQUIRES: mips
+
+# CHECK: error: corrupt input file: version definition index 9 for symbol __cxa_finalize is out of bounds
+# CHECK: >>> defined in {{.+}}/corrupt-version-reference.so
+
+.globl __start
+__start:
+ dla $a0, __cxa_finalize
+ nop
diff --git a/test/ELF/ctors_dtors_priority.s b/test/ELF/ctors_dtors_priority.s
index 10d6471f9..fcddcbb02 100644
--- a/test/ELF/ctors_dtors_priority.s
+++ b/test/ELF/ctors_dtors_priority.s
@@ -14,28 +14,35 @@ _start:
nop
.section .ctors, "aw", @progbits
- .byte 1
+ .quad 1
.section .ctors.100, "aw", @progbits
- .byte 2
+ .quad 2
.section .ctors.005, "aw", @progbits
- .byte 3
+ .quad 3
.section .ctors, "aw", @progbits
- .byte 4
+ .quad 4
.section .ctors, "aw", @progbits
- .byte 5
+ .quad 5
.section .dtors, "aw", @progbits
- .byte 0x11
+ .quad 0x11
.section .dtors.100, "aw", @progbits
- .byte 0x12
+ .quad 0x12
.section .dtors.005, "aw", @progbits
- .byte 0x13
+ .quad 0x13
.section .dtors, "aw", @progbits
- .byte 0x14
+ .quad 0x14
.section .dtors, "aw", @progbits
- .byte 0x15
+ .quad 0x15
// CHECK: Contents of section .ctors:
-// CHECK-NEXT: a1010405 b10302c1
+// CHECK-NEXT: 202000 a1000000 00000000 01000000 00000000
+// CHECK-NEXT: 202010 04000000 00000000 05000000 00000000
+// CHECK-NEXT: 202020 b1000000 00000000 03000000 00000000
+// CHECK-NEXT: 202030 02000000 00000000 c1000000 00000000
+
// CHECK: Contents of section .dtors:
-// CHECK-NEXT: a2111415 b21312c2
+// CHECK-NEXT: 202040 a2000000 00000000 11000000 00000000
+// CHECK-NEXT: 202050 14000000 00000000 15000000 00000000
+// CHECK-NEXT: 202060 b2000000 00000000 13000000 00000000
+// CHECK-NEXT: 202070 12000000 00000000 c2000000 00000000
diff --git a/test/ELF/debug-gc.s b/test/ELF/debug-gc.s
index 8bcfde16a..5ec43f678 100644
--- a/test/ELF/debug-gc.s
+++ b/test/ELF/debug-gc.s
@@ -4,11 +4,11 @@
# RUN: llvm-objdump -s %t1 | FileCheck %s
# CHECK: Contents of section .debug_str:
-# CHECK-NEXT: 0000 41414100 42424200 43434300 AAA.BBB.CCC.
+# CHECK-NEXT: 0000 41414100 43434300 42424200 AAA.CCC.BBB.
# CHECK: Contents of section .foo:
# CHECK-NEXT: 0000 2a000000
# CHECK: Contents of section .debug_info:
-# CHECK-NEXT: 0000 00000000 04000000
+# CHECK-NEXT: 0000 00000000 08000000
.globl _start
_start:
diff --git a/test/ELF/debug-gnu-pubnames.s b/test/ELF/debug-gnu-pubnames.s
index 0a8693c97..aebfdfd0f 100644
--- a/test/ELF/debug-gnu-pubnames.s
+++ b/test/ELF/debug-gnu-pubnames.s
@@ -1,10 +1,18 @@
# REQUIRES: x86
-# RUN: ld.lld -e main %p/Inputs/gdb-index-a.elf %p/Inputs/gdb-index-b.elf -o %t1.exe
-# RUN: llvm-readobj -sections %t1.exe | FileCheck -check-prefix=CHECK1 %s
-# CHECK1: Name: .debug_gnu_pubnames
-# CHECK1: Name: .debug_gnu_pubtypes
-
-# RUN: ld.lld -gdb-index -e main %p/Inputs/gdb-index-a.elf %p/Inputs/gdb-index-b.elf -o %t2.exe
-# RUN: llvm-readobj -sections %t2.exe | FileCheck -check-prefix=CHECK2 %s
-# CHECK2-NOT: Name: .debug_gnu_pubnames
-# CHECK2-NOT: Name: .debug_gnu_pubtypes
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+
+# RUN: ld.lld %t.o -o %t1.exe
+# RUN: llvm-readobj -sections %t1.exe | FileCheck %s
+# CHECK: .debug_gnu_pubnames
+# CHECK: .debug_gnu_pubtypes
+
+# RUN: ld.lld -gdb-index %t.o -o %t2.exe
+# RUN: llvm-readobj -sections %t2.exe | FileCheck %s --check-prefix=GDB
+# GDB-NOT: .debug_gnu_pubnames
+# GDB-NOT: .debug_gnu_pubtypes
+
+.section .debug_gnu_pubnames,"",@progbits
+.long 0
+
+.section .debug_gnu_pubtypes,"",@progbits
+.long 0
diff --git a/test/ELF/defsym.s b/test/ELF/defsym.s
index 778180dc9..b82148426 100644
--- a/test/ELF/defsym.s
+++ b/test/ELF/defsym.s
@@ -9,8 +9,6 @@
# RUN: llvm-readobj -t -s %t2 | FileCheck %s
# RUN: llvm-objdump -d -print-imm-hex %t2 | FileCheck %s --check-prefix=USE
-## In compare with GNU linkers, symbol defined with --defsym does
-## not get aliased name in symbol table:
# CHECK: Symbol {
# CHECK: Name: foo1
# CHECK-NEXT: Value: 0x123
diff --git a/test/ELF/dso-undef-size.s b/test/ELF/dso-undef-size.s
new file mode 100644
index 000000000..5a235565b
--- /dev/null
+++ b/test/ELF/dso-undef-size.s
@@ -0,0 +1,32 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/dso-undef-size.s -o %t1.o
+# RUN: ld.lld -shared %t1.o -o %t1.so
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t2.o
+# RUN: ld.lld -shared %t2.o %t1.so -o %t2.so
+# RUN: llvm-readobj -symbols -dyn-symbols %t2.so
+
+# CHECK: Symbols [
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: foo
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding:
+# CHECK-NEXT: Type:
+# CHECK-NEXT: Other:
+# CHECK-NEXT: Section: Undefined
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+# CHECK: DynamicSymbols [
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: foo
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding:
+# CHECK-NEXT: Type:
+# CHECK-NEXT: Other:
+# CHECK-NEXT: Section: Undefined
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+.text
+.global foo
diff --git a/test/ELF/duplicated-synthetic-sym.s b/test/ELF/duplicated-synthetic-sym.s
new file mode 100644
index 000000000..a75111e6a
--- /dev/null
+++ b/test/ELF/duplicated-synthetic-sym.s
@@ -0,0 +1,11 @@
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: cd %S
+// RUN: not ld.lld %t.o --format=binary duplicated-synthetic-sym.s -o %t.elf 2>&1 | FileCheck %s
+// RUN: not ld.lld %t.o --format binary duplicated-synthetic-sym.s -o %t.elf 2>&1 | FileCheck %s
+
+// CHECK: duplicate symbol: _binary_duplicated_synthetic_sym_s_start
+// CHECK: defined at (internal):(.data+0x0)
+
+ .globl _binary_duplicated_synthetic_sym_s_start
+_binary_duplicated_synthetic_sym_s_start:
+ .long 0
diff --git a/test/ELF/dynamic-got.s b/test/ELF/dynamic-got.s
index c50c90223..385394b9d 100644
--- a/test/ELF/dynamic-got.s
+++ b/test/ELF/dynamic-got.s
@@ -1,6 +1,6 @@
// 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: ld.lld --hash-style=sysv %t.o -o %t.so -shared
// RUN: llvm-readobj -s -l -section-data -r %t.so | FileCheck %s
// CHECK: Name: .got
diff --git a/test/ELF/dynamic-list-empty.s b/test/ELF/dynamic-list-empty.s
new file mode 100644
index 000000000..5686ce0c4
--- /dev/null
+++ b/test/ELF/dynamic-list-empty.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+
+# BFD reports a parse error on empty lists, but it is clear how to
+# handle it.
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "{ };" > %t.list
+# RUN: ld.lld -dynamic-list %t.list -shared %t.o -o %t.so
+# RUN: llvm-readobj -r %t.so | FileCheck %s
+
+# CHECK: Relocations [
+# CHECK-NEXT: ]
+
+ .globl foo
+foo:
+ ret
+
+ call foo@PLT
diff --git a/test/ELF/dynamic-list-preempt.s b/test/ELF/dynamic-list-preempt.s
new file mode 100644
index 000000000..2bb10a3ed
--- /dev/null
+++ b/test/ELF/dynamic-list-preempt.s
@@ -0,0 +1,76 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "{ foo; zed; };" > %t.list
+# RUN: echo "{ global: foo; bar; local: *; };" > %t.vers
+# RUN: ld.lld --hash-style=sysv -fatal-warnings -dynamic-list %t.list -version-script %t.vers -shared %t.o -o %t.so
+# RUN: llvm-readobj -r %t.so | FileCheck --check-prefix=RELOCS %s
+# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck --check-prefix=DYNSYMS %s
+
+# RELOCS: Relocations [
+# RELOCS-NEXT: Section ({{.*}}) .rela.plt {
+# RELOCS-NEXT: R_X86_64_JUMP_SLOT foo 0x0
+# RELOCS-NEXT: R_X86_64_JUMP_SLOT ext 0x0
+# RELOCS-NEXT: }
+# RELOCS-NEXT: ]
+
+# DYNSYMS: DynamicSymbols [
+# DYNSYMS-NEXT: Symbol {
+# DYNSYMS-NEXT: Name: @ (0)
+# DYNSYMS-NEXT: Value: 0x0
+# DYNSYMS-NEXT: Size: 0
+# DYNSYMS-NEXT: Binding: Local
+# DYNSYMS-NEXT: Type: None
+# DYNSYMS-NEXT: Other: 0
+# DYNSYMS-NEXT: Section: Undefined
+# DYNSYMS-NEXT: }
+# DYNSYMS-NEXT: Symbol {
+# DYNSYMS-NEXT: Name: bar@
+# DYNSYMS-NEXT: Value:
+# DYNSYMS-NEXT: Size:
+# DYNSYMS-NEXT: Binding: Global
+# DYNSYMS-NEXT: Type:
+# DYNSYMS-NEXT: Other:
+# DYNSYMS-NEXT: Section:
+# DYNSYMS-NEXT: }
+# DYNSYMS-NEXT: Symbol {
+# DYNSYMS-NEXT: Name: ext@
+# DYNSYMS-NEXT: Value:
+# DYNSYMS-NEXT: Size:
+# DYNSYMS-NEXT: Binding: Global
+# DYNSYMS-NEXT: Type:
+# DYNSYMS-NEXT: Other:
+# DYNSYMS-NEXT: Section:
+# DYNSYMS-NEXT: }
+# DYNSYMS-NEXT: Symbol {
+# DYNSYMS-NEXT: Name: foo@
+# DYNSYMS-NEXT: Value:
+# DYNSYMS-NEXT: Size:
+# DYNSYMS-NEXT: Binding: Global
+# DYNSYMS-NEXT: Type:
+# DYNSYMS-NEXT: Other:
+# DYNSYMS-NEXT: Section:
+# DYNSYMS-NEXT: }
+# DYNSYMS-NEXT: ]
+
+ .globl foo
+foo:
+ ret
+
+ .globl bar
+bar:
+ ret
+
+ .globl baz
+baz:
+ ret
+
+ .globl zed
+zed:
+ ret
+
+ call foo@PLT
+ call bar@PLT
+ call baz@PLT
+ call zed@PLT
+ call ext@PLT
diff --git a/test/ELF/dynamic-list-weak-archive.s b/test/ELF/dynamic-list-weak-archive.s
new file mode 100644
index 000000000..f7f72afd0
--- /dev/null
+++ b/test/ELF/dynamic-list-weak-archive.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/dynamic-list-weak-archive.s -o %t2.o
+# RUN: rm -f %t.a
+# RUN: llvm-ar rcs %t.a %t2.o
+# RUN: echo "{ zed; };" > %t.list
+# RUN: ld.lld -shared --dynamic-list %t.list %t1.o %t.a -o %t.so
+# RUN: llvm-readobj -r %t.so | FileCheck %s
+
+# CHECK: Relocations [
+# CHECK-NEXT: Section ({{.*}}) .rela.plt {
+# CHECK-NEXT: 0x2018 R_X86_64_JUMP_SLOT foo
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+callq foo@PLT
+.weak foo
diff --git a/test/ELF/dynamic-list.s b/test/ELF/dynamic-list.s
index 7cd587380..888508e27 100644
--- a/test/ELF/dynamic-list.s
+++ b/test/ELF/dynamic-list.s
@@ -1,28 +1,24 @@
-## 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: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o
-# RUN: ld.lld -shared %t2.o -soname shared -o %t2.so
+# RUN: ld.lld --hash-style=sysv -shared %t2.o -soname shared -o %t2.so
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
## Check exporting only one symbol.
# RUN: echo "{ foo1; };" > %t.list
-# RUN: ld.lld --dynamic-list %t.list %t %t2.so -o %t.exe
+# RUN: ld.lld --hash-style=sysv --dynamic-list %t.list %t %t2.so -o %t.exe
# RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck %s
## And now using quoted strings (the output is the same since it does
## use any wildcard character).
# RUN: echo "{ \"foo1\"; };" > %t.list
-# RUN: ld.lld --dynamic-list %t.list %t %t2.so -o %t.exe
+# RUN: ld.lld --hash-style=sysv --dynamic-list %t.list %t %t2.so -o %t.exe
# RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck %s
## And now using --export-dynamic-symbol.
-# RUN: ld.lld --export-dynamic-symbol foo1 %t %t2.so -o %t.exe
+# RUN: ld.lld --hash-style=sysv --export-dynamic-symbol foo1 %t %t2.so -o %t.exe
# RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck %s
-# RUN: ld.lld --export-dynamic-symbol=foo1 %t %t2.so -o %t.exe
+# RUN: ld.lld --hash-style=sysv --export-dynamic-symbol=foo1 %t %t2.so -o %t.exe
# RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck %s
# CHECK: DynamicSymbols [
@@ -49,11 +45,11 @@
## Now export all the foo1, foo2, and foo31 symbols
# RUN: echo "{ foo1; foo2; foo31; };" > %t.list
-# RUN: ld.lld --dynamic-list %t.list %t %t2.so -o %t.exe
+# RUN: ld.lld --hash-style=sysv --dynamic-list %t.list %t %t2.so -o %t.exe
# RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck -check-prefix=CHECK2 %s
# RUN: echo "{ foo1; foo2; };" > %t1.list
# RUN: echo "{ foo31; };" > %t2.list
-# RUN: ld.lld --dynamic-list %t1.list --dynamic-list %t2.list %t %t2.so -o %t.exe
+# RUN: ld.lld --hash-style=sysv --dynamic-list %t1.list --dynamic-list %t2.list %t %t2.so -o %t.exe
# RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck -check-prefix=CHECK2 %s
# CHECK2: DynamicSymbols [
@@ -99,11 +95,11 @@
## --export-dynamic overrides --dynamic-list, i.e. --export-dynamic with an
## incomplete dynamic-list still exports everything.
# RUN: echo "{ foo2; };" > %t.list
-# RUN: ld.lld --dynamic-list %t.list --export-dynamic %t %t2.so -o %t.exe
+# RUN: ld.lld --hash-style=sysv --dynamic-list %t.list --export-dynamic %t %t2.so -o %t.exe
# RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck -check-prefix=CHECK3 %s
## The same with --export-dynamic-symbol.
-# RUN: ld.lld --export-dynamic-symbol=foo2 --export-dynamic %t %t2.so -o %t.exe
+# RUN: ld.lld --hash-style=sysv --export-dynamic-symbol=foo2 --export-dynamic %t %t2.so -o %t.exe
# RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck -check-prefix=CHECK3 %s
# CHECK3: DynamicSymbols [
diff --git a/test/ELF/dynamic-no-rosegment.s b/test/ELF/dynamic-no-rosegment.s
new file mode 100644
index 000000000..e5ad26e3f
--- /dev/null
+++ b/test/ELF/dynamic-no-rosegment.s
@@ -0,0 +1,15 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld -shared --no-rosegment -z rodynamic -o %t %t.o
+# RUN: llvm-readobj -dynamic-table -s %t | FileCheck %s
+
+# CHECK: DynamicSection [ (7 entries)
+# CHECK-NEXT: Tag Type Name/Value
+# CHECK-NEXT: 0x0000000000000006 SYMTAB 0x120
+# CHECK-NEXT: 0x000000000000000B SYMENT 24 (bytes)
+# CHECK-NEXT: 0x0000000000000005 STRTAB 0x1D0
+# CHECK-NEXT: 0x000000000000000A STRSZ 1 (bytes)
+# CHECK-NEXT: 0x000000006FFFFEF5 GNU_HASH 0x138
+# CHECK-NEXT: 0x0000000000000004 HASH 0x150
+# CHECK-NEXT: 0x0000000000000000 NULL 0x0
+# CHECK-NEXT: ]
diff --git a/test/ELF/dynamic-reloc-in-ro.s b/test/ELF/dynamic-reloc-in-ro.s
index 23b068ff8..ecdbfeb66 100644
--- a/test/ELF/dynamic-reloc-in-ro.s
+++ b/test/ELF/dynamic-reloc-in-ro.s
@@ -2,9 +2,9 @@
// 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: can't create dynamic relocation R_X86_64_64 against local symbol in readonly segment
-// CHECK: >>> defined in {{.*}}.o
-// CHECK: >>> referenced by {{.*}}.o:(.text+0x0)
+// CHECK: can't create dynamic relocation R_X86_64_64 against local symbol in readonly segment; recompile object files with -fPIC
+// CHECK-NEXT: >>> defined in {{.*}}.o
+// CHECK-NEXT: >>> referenced by {{.*}}.o:(.text+0x0)
foo:
.quad foo
diff --git a/test/ELF/dynamic-reloc.s b/test/ELF/dynamic-reloc.s
index 8fda0b45a..4d95e41fb 100644
--- a/test/ELF/dynamic-reloc.s
+++ b/test/ELF/dynamic-reloc.s
@@ -2,7 +2,7 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/dynamic-reloc.s -o %t3.o
// RUN: ld.lld -shared %t2.o -o %t2.so
-// RUN: ld.lld %t.o %t3.o %t2.so -o %t
+// RUN: ld.lld --hash-style=sysv %t.o %t3.o %t2.so -o %t
// RUN: llvm-readobj -dynamic-table -r --expand-relocs -s %t | FileCheck %s
// REQUIRES: x86
@@ -43,7 +43,7 @@
// CHECK: DynamicSection [
// CHECK-NEXT: Tag Type Name/Value
-// CHECK-NEXT: 0x0000000000000001 NEEDED SharedLibrary ({{.*}}2.so)
+// CHECK-NEXT: 0x0000000000000001 NEEDED Shared library: [{{.*}}2.so]
// CHECK-NEXT: 0x0000000000000015 DEBUG 0x0
// CHECK-NEXT: 0x0000000000000017 JMPREL
// CHECK-NEXT: 0x0000000000000002 PLTRELSZ 24 (bytes)
diff --git a/test/ELF/dynstr-no-rosegment.s b/test/ELF/dynstr-no-rosegment.s
new file mode 100644
index 000000000..0e12721da
--- /dev/null
+++ b/test/ELF/dynstr-no-rosegment.s
@@ -0,0 +1,12 @@
+# Verify that a .dynstr in the .text segment has null byte terminators
+
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -no-rosegment -o %t.so -shared
+# RUN: llvm-objdump %t.so -s -j .dynstr | FileCheck %s
+
+# CHECK: 00666f6f 00 .foo.
+
+.globl foo
+foo:
+ ret
diff --git a/test/ELF/dynsym-no-rosegment.s b/test/ELF/dynsym-no-rosegment.s
new file mode 100644
index 000000000..947f526e0
--- /dev/null
+++ b/test/ELF/dynsym-no-rosegment.s
@@ -0,0 +1,27 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld -shared --no-rosegment -o %t %t.o
+# RUN: llvm-readobj -dyn-symbols -s %t | FileCheck %s
+
+# CHECK: DynamicSymbols [
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: @ (0)
+# 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: undef@
+# CHECK-NEXT: Value: 0x0
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Undefined
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+callq undef@PLT
diff --git a/test/ELF/dynsym-pie.s b/test/ELF/dynsym-pie.s
index 9d3a9ffe3..b162d2733 100644
--- a/test/ELF/dynsym-pie.s
+++ b/test/ELF/dynsym-pie.s
@@ -3,6 +3,51 @@
# RUN: ld.lld -pie %t -o %t.out
# RUN: llvm-readobj -t -dyn-symbols %t.out | FileCheck %s
+# CHECK: Symbols [
+# CHECK: Symbol {
+# CHECK: Name: hidden
+# CHECK-NEXT: Value: 0x1000
+# 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: }
+# CHECK: Symbol {
+# CHECK: Name: internal
+# CHECK-NEXT: Value: 0x1000
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Local
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other [
+# CHECK-NEXT: STV_INTERNAL
+# CHECK-NEXT: ]
+# CHECK-NEXT: Section: .text
+# CHECK-NEXT: }
+# CHECK: Symbol {
+# CHECK: Name: default
+# 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: Symbol {
+# CHECK: Name: protected
+# CHECK-NEXT: Value: 0x1000
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other [
+# CHECK-NEXT: STV_PROTECTED
+# CHECK-NEXT: ]
+# CHECK-NEXT: Section: .text
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
# CHECK: DynamicSymbols [
# CHECK-NEXT: Symbol {
# CHECK-NEXT: Name: @
@@ -23,14 +68,13 @@ _start:
default:
.global protected
+.protected protected
protected:
.global hidden
+.hidden hidden
hidden:
.global internal
+.internal internal
internal:
-
-.global protected_with_hidden
-.protected
-protected_with_hidden:
diff --git a/test/ELF/edata-etext.s b/test/ELF/edata-etext.s
index 3b0ba49ad..235839985 100644
--- a/test/ELF/edata-etext.s
+++ b/test/ELF/edata-etext.s
@@ -19,7 +19,7 @@
# CHECK: SYMBOL TABLE:
# CHECK-NEXT: 0000000000000000 *UND* 00000000
# CHECK-NEXT: 0000000000202002 .data 00000000 _edata
-# CHECK-NEXT: 000000000020200a .data 00000000 _end
+# CHECK-NEXT: 000000000020200a .bss 00000000 _end
# CHECK-NEXT: 0000000000201001 .text 00000000 _etext
# CHECK-NEXT: 0000000000201000 .text 00000000 _start
diff --git a/test/ELF/eh-align-cie.s b/test/ELF/eh-align-cie.s
index 343dea500..9e6ac2dc1 100644
--- a/test/ELF/eh-align-cie.s
+++ b/test/ELF/eh-align-cie.s
@@ -31,7 +31,7 @@ bar:
// OBJ-NEXT: )
-// RUN: ld.lld %t.o -o %t -shared
+// RUN: ld.lld --hash-style=sysv %t.o -o %t -shared
// RUN: llvm-readobj -s -section-data %t | FileCheck %s
// Check that the size of the CIE was changed to (0x1C + 4) and the FDE one was
diff --git a/test/ELF/eh-frame-hdr-augmentation.s b/test/ELF/eh-frame-hdr-augmentation.s
index 618f5e1a9..135f81196 100644
--- a/test/ELF/eh-frame-hdr-augmentation.s
+++ b/test/ELF/eh-frame-hdr-augmentation.s
@@ -1,6 +1,6 @@
// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-// RUN: ld.lld --eh-frame-hdr %t.o -o %t -shared
+// RUN: ld.lld --hash-style=sysv --eh-frame-hdr %t.o -o %t -shared
// RUN: llvm-objdump --dwarf=frames %t | FileCheck %s
// CHECK: .eh_frame contents:
diff --git a/test/ELF/eh-frame-hdr.s b/test/ELF/eh-frame-hdr.s
index 35c14a4b6..4498d7d30 100644
--- a/test/ELF/eh-frame-hdr.s
+++ b/test/ELF/eh-frame-hdr.s
@@ -1,9 +1,17 @@
// 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 -file-headers -s -section-data -program-headers -symbols %t | FileCheck %s --check-prefix=NOHDR
+// RUN: llvm-readobj -file-headers -s -section-data -program-headers -symbols %t \
+// RUN: | FileCheck %s --check-prefix=NOHDR
+
+// RUN: ld.lld -eh-frame-hdr -no-eh-frame-hdr %t.o -o %t
+// RUN: llvm-readobj -file-headers -s -section-data -program-headers -symbols %t \
+// RUN: | FileCheck %s --check-prefix=NOHDR
+
// RUN: ld.lld --eh-frame-hdr %t.o -o %t
-// RUN: llvm-readobj -file-headers -s -section-data -program-headers -symbols %t | FileCheck %s --check-prefix=HDR
+// RUN: llvm-readobj -file-headers -s -section-data -program-headers -symbols %t \
+// RUN: | FileCheck %s --check-prefix=HDR
// RUN: llvm-objdump -d %t | FileCheck %s --check-prefix=HDRDISASM
.section foo,"ax",@progbits
diff --git a/test/ELF/eh-frame-merge.s b/test/ELF/eh-frame-merge.s
index addbb3f85..4b54c173c 100644
--- a/test/ELF/eh-frame-merge.s
+++ b/test/ELF/eh-frame-merge.s
@@ -1,6 +1,6 @@
// 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 -shared
+// RUN: ld.lld --hash-style=sysv %t.o %t.o -o %t -shared
// RUN: llvm-readobj -s -section-data %t | FileCheck %s
.section foo,"ax",@progbits
diff --git a/test/ELF/eh-frame-padding-no-rosegment.s b/test/ELF/eh-frame-padding-no-rosegment.s
new file mode 100644
index 000000000..e106f2989
--- /dev/null
+++ b/test/ELF/eh-frame-padding-no-rosegment.s
@@ -0,0 +1,64 @@
+// REQUIRES: x86
+
+.cfi_startproc
+.cfi_personality 0x1b, bar
+.cfi_endproc
+
+.global bar
+.hidden bar
+bar:
+
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+
+// Check the size of the CIE (0x18 + 4) and FDE (0x10 + 4)
+// RUN: llvm-readobj -s -section-data %t.o | FileCheck --check-prefix=OBJ %s
+
+// OBJ: Name: .eh_frame
+// OBJ-NEXT: Type:
+// OBJ-NEXT: Flags [
+// OBJ-NEXT: SHF_ALLOC
+// OBJ-NEXT: ]
+// OBJ-NEXT: Address:
+// OBJ-NEXT: Offset:
+// OBJ-NEXT: Size:
+// OBJ-NEXT: Link:
+// OBJ-NEXT: Info:
+// OBJ-NEXT: AddressAlignment:
+// OBJ-NEXT: EntrySize:
+// OBJ-NEXT: SectionData (
+// OBJ-NEXT: 0000: 18000000 00000000 017A5052 00017810
+// OBJ-NEXT: 0010: 061B0000 00001B0C 07089001 10000000
+// OBJ-NEXT: 0020: 20000000 00000000 00000000 00000000
+// OBJ-NEXT: )
+
+// RUN: ld.lld --hash-style=sysv %t.o -no-rosegment -o %t -shared
+
+// Check that .eh_frame is in the same segment as .text
+// RUN: llvm-readobj -l --elf-output-style=GNU %t | FileCheck --check-prefix=PHDR %s
+
+// PHDR: Segment Sections
+// PHDR: .text
+// PHDR-SAME: .eh_frame
+
+// Check that the CIE and FDE are padded with 0x00 and not 0xCC when the
+// .eh_frame section is placed in the executable segment
+// RUN: llvm-readobj -s -section-data %t | FileCheck %s
+
+// CHECK: Name: .eh_frame
+// CHECK-NEXT: Type:
+// CHECK-NEXT: Flags
+// CHECK-NEXT: SHF_ALLOC
+// 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: 1C000000 00000000 017A5052 00017810
+// CHECK-NEXT: 0010: 061BBEFF FFFF1B0C 07089001 00000000
+// CHECK-NEXT: 0020: 14000000 24000000 A8FFFFFF 00000000
+// CHECK-NEXT: 0030: 00000000 00000000
+// CHECK-NEXT: )
diff --git a/test/ELF/eh-frame.s b/test/ELF/eh-frame.s
new file mode 100644
index 000000000..a07d242ad
--- /dev/null
+++ b/test/ELF/eh-frame.s
@@ -0,0 +1,12 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/eh-frame.s -o %t2.o
+// RUN: ld.lld %t1.o %t2.o -o %t
+// RUN: llvm-dwarfdump -eh-frame %t | FileCheck %s
+
+// CHECK: DW_CFA_def_cfa_offset: +64
+// CHECK: DW_CFA_def_cfa_offset: +32
+
+.cfi_startproc
+.cfi_def_cfa_offset 64
+.cfi_endproc
diff --git a/test/ELF/emit-relocs-gc.s b/test/ELF/emit-relocs-gc.s
new file mode 100644
index 000000000..5b8bdfe95
--- /dev/null
+++ b/test/ELF/emit-relocs-gc.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+
+## Show that we emit .rela.bar when GC is disabled.
+# RUN: ld.lld --emit-relocs %t.o -o %t
+# RUN: llvm-objdump %t -section-headers | FileCheck %s --check-prefix=NOGC
+# NOGC: .rela.bar
+
+## GC collects .bar section and we exclude .rela.bar from output.
+# RUN: ld.lld --gc-sections --emit-relocs --print-gc-sections %t.o -o %t \
+# RUN: | FileCheck --check-prefix=MSG %s
+# MSG: removing unused section from '.bar' in file
+# MSG: removing unused section from '.rela.bar' in file
+# RUN: llvm-objdump %t -section-headers | FileCheck %s --check-prefix=GC
+# GC-NOT: rela.bar
+
+.section .bar,"a"
+.quad .bar
diff --git a/test/ELF/emit-relocs-shared.s b/test/ELF/emit-relocs-shared.s
index 7a0d79118..65a12c15e 100644
--- a/test/ELF/emit-relocs-shared.s
+++ b/test/ELF/emit-relocs-shared.s
@@ -1,6 +1,6 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-# RUN: ld.lld --emit-relocs %t.o -o %t.so -shared
+# RUN: ld.lld --hash-style=sysv --emit-relocs %t.o -o %t.so -shared
# RUN: llvm-readobj -r %t.so | FileCheck %s
.data
diff --git a/test/ELF/exclude-libs.s b/test/ELF/exclude-libs.s
new file mode 100644
index 000000000..c36081f40
--- /dev/null
+++ b/test/ELF/exclude-libs.s
@@ -0,0 +1,30 @@
+// 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 \
+// RUN: %p/Inputs/exclude-libs.s -o %t2.o
+// RUN: mkdir -p %t.dir
+// RUN: rm -f %t.dir/exc.a
+// RUN: llvm-ar rcs %t.dir/exc.a %t2.o
+
+// RUN: ld.lld -shared %t.o %t.dir/exc.a -o %t.exe
+// RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck --check-prefix=DEFAULT %s
+
+// RUN: ld.lld -shared %t.o %t.dir/exc.a -o %t.exe --exclude-libs=foo,bar
+// RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck --check-prefix=DEFAULT %s
+
+// RUN: ld.lld -shared %t.o %t.dir/exc.a -o %t.exe --exclude-libs foo,bar,exc.a
+// RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck --check-prefix=EXCLUDE %s
+
+// RUN: ld.lld -shared %t.o %t.dir/exc.a -o %t.exe --exclude-libs foo:bar:exc.a
+// RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck --check-prefix=EXCLUDE %s
+
+// RUN: ld.lld -shared %t.o %t.dir/exc.a -o %t.exe --exclude-libs=ALL
+// RUN: llvm-readobj -dyn-symbols %t.exe | FileCheck --check-prefix=EXCLUDE %s
+
+// DEFAULT: Name: fn
+// EXCLUDE-NOT: Name: fn
+
+.globl fn
+foo:
+ call fn@PLT
diff --git a/test/ELF/executable-undefined-ignoreall.s b/test/ELF/executable-undefined-ignoreall.s
new file mode 100644
index 000000000..44f83a687
--- /dev/null
+++ b/test/ELF/executable-undefined-ignoreall.s
@@ -0,0 +1,13 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: ld.lld %t -o %tout --unresolved-symbols=ignore-all -pie
+# RUN: llvm-readobj -r %tout | FileCheck %s
+
+# CHECK: Relocations [
+# CHECK-NEXT: Section ({{.*}}) .rela.plt {
+# CHECK-NEXT: 0x2018 R_X86_64_JUMP_SLOT foo 0x0
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+_start:
+callq foo@PLT
diff --git a/test/ELF/executable-undefined-protected-ignoreall.s b/test/ELF/executable-undefined-protected-ignoreall.s
new file mode 100644
index 000000000..37911791e
--- /dev/null
+++ b/test/ELF/executable-undefined-protected-ignoreall.s
@@ -0,0 +1,8 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: not ld.lld %t -o %tout --unresolved-symbols=ignore-all -pie 2>&1 | FileCheck %s
+# CHECK: error: undefined symbol: foo
+
+.protected foo
+_start:
+callq foo@PLT
diff --git a/test/ELF/fill-trap.s b/test/ELF/fill-trap.s
new file mode 100644
index 000000000..001b904a7
--- /dev/null
+++ b/test/ELF/fill-trap.s
@@ -0,0 +1,25 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: ld.lld %t -o %t2
+# RUN: llvm-readobj -program-headers %t2 | FileCheck %s
+# RUN: od -Ax -x -N16 -j0x1ff0 %t2 | FileCheck %s -check-prefix=FILL
+
+# CHECK: ProgramHeader {
+# CHECK: Type: PT_LOAD
+# CHECK: Offset: 0x1000
+# CHECK-NEXT: VirtualAddress:
+# CHECK-NEXT: PhysicalAddress:
+# CHECK-NEXT: FileSize: 4096
+# CHECK-NEXT: MemSize:
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: PF_X
+# CHECK-NEXT: ]
+
+## Check that executable page is filled with traps at its end.
+# FILL: 001ff0 cccc cccc cccc cccc cccc cccc cccc cccc
+
+.globl _start
+_start:
+ nop
diff --git a/test/ELF/filter.s b/test/ELF/filter.s
new file mode 100644
index 000000000..4c9104a32
--- /dev/null
+++ b/test/ELF/filter.s
@@ -0,0 +1,19 @@
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -shared -F foo.so -F boo.so -o %t1
+# RUN: llvm-readobj --dynamic-table %t1 | FileCheck %s
+
+# Test alias #1.
+# RUN: ld.lld %t.o -shared --filter=foo.so --filter=boo.so -o %t2
+# RUN: llvm-readobj --dynamic-table %t2 | FileCheck %s
+
+# Test alias #2.
+# RUN: ld.lld %t.o -shared --filter foo.so --filter boo.so -o %t3
+# RUN: llvm-readobj --dynamic-table %t3 | FileCheck %s
+
+# CHECK: DynamicSection [
+# CHECK-NEXT: Tag Type Name/Value
+# CHECK-NEXT: 0x000000007FFFFFFF FILTER Filter library: [foo.so]
+# CHECK-NEXT: 0x000000007FFFFFFF FILTER Filter library: [boo.so]
+
+# RUN: not ld.lld %t.o -F x -o %t 2>&1 | FileCheck -check-prefix=ERR %s
+# ERR: -F may not be used without -shared
diff --git a/test/ELF/format-binary-non-ascii.s b/test/ELF/format-binary-non-ascii.s
new file mode 100644
index 000000000..5a3ad960c
--- /dev/null
+++ b/test/ELF/format-binary-non-ascii.s
@@ -0,0 +1,15 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t£.o
+
+# RUN: ld.lld -o %t.elf %t£.o --format=binary %t£.o
+# RUN: llvm-readobj -symbols %t.elf | FileCheck %s
+
+# CHECK: Name: _binary_{{[a-zA-Z0-9_]+}}test_ELF_Output_format_binary_non_ascii_s_tmp___o_start
+# CHECK: Name: _binary_{{[a-zA-Z0-9_]+}}test_ELF_Output_format_binary_non_ascii_s_tmp___o_end
+# CHECK: Name: _binary_{{[a-zA-Z0-9_]+}}test_ELF_Output_format_binary_non_ascii_s_tmp___o_size
+
+.text
+.align 4
+.globl _start
+_start:
+ nop
diff --git a/test/ELF/gc-sections-shared.s b/test/ELF/gc-sections-shared.s
index a88f2b443..3bb8ff12f 100644
--- a/test/ELF/gc-sections-shared.s
+++ b/test/ELF/gc-sections-shared.s
@@ -28,6 +28,15 @@
# CHECK-NEXT: Section: Undefined
# 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: foo
# CHECK-NEXT: Value:
# CHECK-NEXT: Size:
@@ -38,7 +47,7 @@
# CHECK-NEXT: }
# CHECK-NEXT: ]
-# CHECK: NEEDED SharedLibrary ({{.*}}.so)
+# CHECK: NEEDED Shared library: [{{.*}}.so]
.section .text.foo, "ax"
.globl foo
diff --git a/test/ELF/gc-sections-undefined.s b/test/ELF/gc-sections-undefined.s
new file mode 100644
index 000000000..e1ce9c739
--- /dev/null
+++ b/test/ELF/gc-sections-undefined.s
@@ -0,0 +1,10 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: ld.lld %t -o %t1 --gc-sections --undefined=foo
+# RUN: llvm-readobj -t %t1 | FileCheck %s
+
+# CHECK: foo
+
+.section .foo,"ax"
+.global foo
+foo:
diff --git a/test/ELF/gdb-index-base-addr.s b/test/ELF/gdb-index-base-addr.s
new file mode 100644
index 000000000..3cc1617b6
--- /dev/null
+++ b/test/ELF/gdb-index-base-addr.s
@@ -0,0 +1,70 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o
+# RUN: ld.lld --gdb-index %t1.o -o %t
+# RUN: llvm-dwarfdump -gdb-index %t | FileCheck %s
+
+# CHECK: .gnu_index contents:
+# CHECK: Address area offset = 0x28, has 2 entries:
+# CHECK-NEXT: Low/High address = [0x201000, 0x201001) (Size: 0x1), CU id = 0
+# CHECK-NEXT: Low/High address = [0x201003, 0x201006) (Size: 0x3), CU id = 0
+
+.text
+.globl foo
+.type foo,@function
+foo:
+.Lfunc_begin0:
+ nop
+.Ltmp0:
+ nop
+ nop
+.Ltmp1:
+ nop
+ nop
+ nop
+.Ltmp2:
+
+.section .debug_abbrev,"",@progbits
+.byte 1 # Abbreviation Code
+.byte 17 # DW_TAG_compile_unit
+.byte 0 # DW_CHILDREN_no
+.byte 37 # DW_AT_producer
+.byte 14 # DW_FORM_strp
+.byte 19 # DW_AT_language
+.byte 5 # DW_FORM_data2
+.byte 3 # DW_AT_name
+.byte 14 # DW_FORM_strp
+.byte 16 # DW_AT_stmt_list
+.byte 23 # DW_FORM_sec_offset
+.byte 27 # DW_AT_comp_dir
+.byte 14 # DW_FORM_strp
+.byte 17 # DW_AT_low_pc
+.byte 1 # DW_FORM_addr
+.byte 85 # DW_AT_ranges
+.byte 23 # DW_FORM_sec_offset
+.byte 0 # EOM(1)
+.byte 0 # EOM(2)
+.byte 0 # EOM(3)
+
+.section .debug_info,"",@progbits
+.Lcu_begin0:
+.long 38 # Length of Unit
+.short 4 # DWARF version number
+.long .debug_abbrev # Offset Into Abbrev. Section
+.byte 8 # Address Size (in bytes)
+.byte 1 # Abbrev [1] 0xb:0x1f DW_TAG_compile_unit
+.long 0 # DW_AT_producer
+.short 4 # DW_AT_language
+.long 0 # DW_AT_name
+.long 0 # DW_AT_stmt_list
+.long 0 # DW_AT_comp_dir
+.quad .Lfunc_begin0 # DW_AT_low_pc
+.long .Ldebug_ranges0 # DW_AT_ranges
+
+.section .debug_ranges,"",@progbits
+.Ldebug_ranges0:
+ .quad .Lfunc_begin0-.Lfunc_begin0
+ .quad .Ltmp0-.Lfunc_begin0
+ .quad .Ltmp1-.Lfunc_begin0
+ .quad .Ltmp2-.Lfunc_begin0
+ .quad 0
+ .quad 0
diff --git a/test/ELF/gdb-index-dup-types.s b/test/ELF/gdb-index-dup-types.s
index e0bed33ee..f5df00c45 100644
--- a/test/ELF/gdb-index-dup-types.s
+++ b/test/ELF/gdb-index-dup-types.s
@@ -1,7 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
# RUN: ld.lld --gdb-index %t.o -o %t
-# RUN: llvm-dwarfdump -debug-dump=gdb_index %t | FileCheck %s
+# RUN: llvm-dwarfdump -gdb-index %t | FileCheck %s
## Testcase is based on output produced by gcc version 5.4.1 20160904
## it has duplicate entries in .debug_gnu_pubtypes which seems to be
diff --git a/test/ELF/gdb-index-empty.s b/test/ELF/gdb-index-empty.s
index 24f20803a..ad428ba89 100644
--- a/test/ELF/gdb-index-empty.s
+++ b/test/ELF/gdb-index-empty.s
@@ -1,6 +1,7 @@
+# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux -o %t %s
# RUN: ld.lld --gdb-index --gc-sections -o %t2 %t
-# RUN: llvm-dwarfdump -debug-dump=gdb_index %t2 | FileCheck %s
+# RUN: llvm-dwarfdump -gdb-index %t2 | FileCheck %s
# CHECK: Address area offset = 0x28, has 0 entries:
diff --git a/test/ELF/gdb-index-gc-sections.s b/test/ELF/gdb-index-gc-sections.s
index 70a147546..6016e9ccd 100644
--- a/test/ELF/gdb-index-gc-sections.s
+++ b/test/ELF/gdb-index-gc-sections.s
@@ -1,6 +1,7 @@
+# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux -o %t %s
# RUN: ld.lld --gdb-index --gc-sections -o %t2 %t
-# RUN: llvm-dwarfdump -debug-dump=gdb_index %t2 | FileCheck %s
+# RUN: llvm-dwarfdump -gdb-index %t2 | FileCheck %s
# CHECK: Address area offset = 0x28, has 1 entries:
# CHECK-NEXT: Low/High address = [0x201000, 0x201001) (Size: 0x1), CU id = 0
diff --git a/test/ELF/gdb-index-ranges.s b/test/ELF/gdb-index-ranges.s
index 2dd158ee4..c41be114f 100644
--- a/test/ELF/gdb-index-ranges.s
+++ b/test/ELF/gdb-index-ranges.s
@@ -1,7 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
# RUN: ld.lld --gdb-index -e main %t.o -o %t
-# RUN: llvm-dwarfdump -debug-dump=gdb_index %t | FileCheck %s
+# RUN: llvm-dwarfdump -gdb-index %t | FileCheck %s
# CHECK: .gnu_index contents:
# CHECK: Address area offset = 0x28, has 2 entries:
diff --git a/test/ELF/gdb-index-tls.s b/test/ELF/gdb-index-tls.s
new file mode 100644
index 000000000..0fd7b6115
--- /dev/null
+++ b/test/ELF/gdb-index-tls.s
@@ -0,0 +1,91 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld --gdb-index -shared %t.o -o %t
+
+# This used to fail trying to compute R_X86_64_DTPOFF64
+
+ .section .tdata,"awT",@progbits
+PrettyStackTraceHead:
+ .long 42 # 0x2a
+
+ .section .debug_str,"MS",@progbits,1
+ .asciz ""
+
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 14 # DW_FORM_strp
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 14 # DW_FORM_strp
+ .ascii "\264B" # DW_AT_GNU_pubnames
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+
+ .section .debug_info,"",@progbits
+.Lcu_begin0:
+ .long 56 # Length of Unit
+ .short 4 # DWARF version number
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 8 # Address Size (in bytes)
+ .byte 1 # Abbrev [1] 0xb:0x31 DW_TAG_compile_unit
+ .long .debug_str # DW_AT_producer
+ .short 4 # DW_AT_language
+ .long .debug_str # DW_AT_name
+ .long .debug_line # DW_AT_stmt_list
+ .long .debug_str # DW_AT_comp_dir
+ # DW_AT_GNU_pubnames
+ .byte 2 # Abbrev [2] 0x1e:0x16 DW_TAG_variable
+ .long .debug_str # DW_AT_name
+ .long 52 # DW_AT_type
+ # DW_AT_external
+ .byte 1 # DW_AT_decl_file
+ .byte 1 # DW_AT_decl_line
+ .byte 10 # DW_AT_location
+ .byte 14
+ .quad PrettyStackTraceHead@DTPOFF
+ .byte 224
+ .byte 3 # Abbrev [3] 0x34:0x7 DW_TAG_base_type
+ .long .debug_str # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 0 # End Of Children Mark
+
+ .section .debug_line,"",@progbits
diff --git a/test/ELF/gdb-index.s b/test/ELF/gdb-index.s
index b7d8a708a..8fea83d14 100644
--- a/test/ELF/gdb-index.s
+++ b/test/ELF/gdb-index.s
@@ -1,32 +1,19 @@
-## 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-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/gdb-index.s -o %t2.o
+# RUN: ld.lld --gdb-index %t1.o %t2.o -o %t
+# RUN: llvm-dwarfdump -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
+# DISASM: entrypoint:
+# DISASM-CHECK: 201000: 90 nop
+# DISASM-CHECK: 201001: cc int3
+# DISASM-CHECK: 201002: cc int3
+# DISASM-CHECK: 201003: cc int3
+# DISASM: main2:
+# DISASM-CHECK: 201004: 90 nop
+# DISASM-CHECK: 201005: 90 nop
# CHECK: .gnu_index contents:
# CHECK-NEXT: Version = 7
@@ -34,16 +21,96 @@
# 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/High address = [0x201000, 0x20100b) (Size: 0xb), CU id = 0
-# CHECK-NEXT: Low/High address = [0x20100b, 0x201016) (Size: 0xb), CU id = 1
+# CHECK-NEXT: Low/High address = [0x201000, 0x201001) (Size: 0x1), CU id = 0
+# CHECK-NEXT: Low/High address = [0x201004, 0x201006) (Size: 0x2), CU id = 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: 754: Name offset = 0x27, CU vector offset = 0x8
+# CHECK-NEXT: String name: int, CU vector index: 1
+# CHECK-NEXT: 822: Name offset = 0x1c, CU vector offset = 0x0
+# CHECK-NEXT: String name: entrypoint, CU vector index: 0
+# CHECK-NEXT: 956: Name offset = 0x2b, 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
+
+# RUN: ld.lld --gdb-index --no-gdb-index %t1.o %t2.o -o %t2
+# RUN: llvm-readobj -sections %t2 | FileCheck -check-prefix=NOGDB %s
+# NOGDB-NOT: Name: .gdb_index
+
+## The following section contents are created by this using gcc 7.1.0:
+## echo 'int entrypoint() { return 0; }' | gcc -gsplit-dwarf -xc++ -S -o- -
+
+.text
+.Ltext0:
+.globl entrypoint
+.type entrypoint, @function
+entrypoint:
+ nop
+.Letext0:
+
+.section .debug_info,"",@progbits
+.long 0x30
+.value 0x4
+.long 0
+.byte 0x8
+.uleb128 0x1
+.quad .Ltext0
+.quad .Letext0-.Ltext0
+.long 0
+.long 0
+.long 0
+.long 0
+.byte 0x63
+.byte 0x88
+.byte 0xb4
+.byte 0x61
+.byte 0xaa
+.byte 0xb6
+.byte 0xb0
+.byte 0x67
+
+.section .debug_abbrev,"",@progbits
+.uleb128 0x1
+.uleb128 0x11
+.byte 0
+.uleb128 0x11
+.uleb128 0x1
+.uleb128 0x12
+.uleb128 0x7
+.uleb128 0x10
+.uleb128 0x17
+.uleb128 0x2130
+.uleb128 0xe
+.uleb128 0x1b
+.uleb128 0xe
+.uleb128 0x2134
+.uleb128 0x19
+.uleb128 0x2133
+.uleb128 0x17
+.uleb128 0x2131
+.uleb128 0x7
+.byte 0
+.byte 0
+.byte 0
+
+.section .debug_gnu_pubnames,"",@progbits
+.long 0x18
+.value 0x2
+.long 0
+.long 0x33
+.long 0x18
+.byte 0x30
+.string "entrypoint"
+.long 0
+
+.section .debug_gnu_pubtypes,"",@progbits
+.long 0x17
+.value 0x2
+.long 0
+.long 0x33
+.long 0x2b
+.byte 0x90
+.string "int"
+.long 0
diff --git a/test/ELF/global-offset-table-position-aarch64.s b/test/ELF/global-offset-table-position-aarch64.s
new file mode 100644
index 000000000..68bc4a425
--- /dev/null
+++ b/test/ELF/global-offset-table-position-aarch64.s
@@ -0,0 +1,30 @@
+// RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %s -o %t
+// RUN: ld.lld --hash-style=sysv -shared %t -o %t2
+// RUN: llvm-readobj -t %t2 | FileCheck %s
+// REQUIRES: aarch64
+.globl a
+.type a,@object
+.comm a,4,4
+
+.globl f
+.type f,@function
+f:
+ adrp x0, :got:a
+ ldr x0, [x0, #:got_lo12:a]
+
+.global _start
+.type _start,@function
+_start:
+ bl f
+.data
+.long _GLOBAL_OFFSET_TABLE_ - .
+
+// CHECK: Name: _GLOBAL_OFFSET_TABLE_ (11)
+// CHECK-NEXT: Value: 0x30090
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Local (0x0)
+// CHECK-NEXT: Type: None (0x0)
+// CHECK-NEXT: Other [ (0x2)
+// CHECK-NEXT: STV_HIDDEN (0x2)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Section: .got
diff --git a/test/ELF/global-offset-table-position-arm.s b/test/ELF/global-offset-table-position-arm.s
new file mode 100644
index 000000000..19619b2cc
--- /dev/null
+++ b/test/ELF/global-offset-table-position-arm.s
@@ -0,0 +1,35 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-linux-gnueabihf %s -o %t
+// RUN: ld.lld --hash-style=sysv -shared %t -o %t2
+// RUN: llvm-readobj -t %t2 | FileCheck %s
+// REQUIRES: arm
+
+// The ARM _GLOBAL_OFFSET_TABLE_ should be defined at the start of the .got
+.globl a
+.type a,%object
+.comm a,4,4
+
+.globl f
+.type f,%function
+f:
+ ldr r2, .L1
+.L0:
+ add r2, pc
+.L1:
+.word _GLOBAL_OFFSET_TABLE_ - (.L0+4)
+.word a(GOT)
+
+.global _start
+.type _start,%function
+_start:
+ bl f
+.data
+
+// CHECK: Name: _GLOBAL_OFFSET_TABLE_
+// CHECK-NEXT: Value: 0x3068
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Local
+// CHECK-NEXT: Type: None
+// CHECK-NEXT: Other [ (0x2)
+// CHECK-NEXT: STV_HIDDEN (0x2)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Section: .got
diff --git a/test/ELF/global-offset-table-position-i386.s b/test/ELF/global-offset-table-position-i386.s
new file mode 100644
index 000000000..9f778e1ef
--- /dev/null
+++ b/test/ELF/global-offset-table-position-i386.s
@@ -0,0 +1,31 @@
+// RUN: llvm-mc -filetype=obj -triple=i386-pc-linux %s -o %t
+// RUN: ld.lld --hash-style=sysv -shared %t -o %t2
+// RUN: llvm-readobj -t %t2 | FileCheck %s
+// REQUIRES: x86
+
+// The X86 _GLOBAL_OFFSET_TABLE_ is defined at the end of the .got section.
+.globl a
+.type a,@object
+.comm a,4,4
+
+.globl f
+.type f,@function
+f:
+addl $_GLOBAL_OFFSET_TABLE_, %eax
+movl a@GOT(%eax), %eax
+
+.global _start
+.type _start,@function
+_start:
+addl $_GLOBAL_OFFSET_TABLE_, %eax
+calll f@PLT
+
+// CHECK: Name: _GLOBAL_OFFSET_TABLE_ (1)
+// CHECK-NEXT: Value: 0x306C
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Local (0x0)
+// CHECK-NEXT: Type: None (0x0)
+// CHECK-NEXT: Other [ (0x2)
+// CHECK-NEXT: STV_HIDDEN (0x2)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Section: .got (0xA)
diff --git a/test/ELF/global-offset-table-position-mips.s b/test/ELF/global-offset-table-position-mips.s
new file mode 100644
index 000000000..92daed1c7
--- /dev/null
+++ b/test/ELF/global-offset-table-position-mips.s
@@ -0,0 +1,33 @@
+// RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t
+// RUN: ld.lld -shared %t -o %t2
+// RUN: llvm-readobj -t %t2 | FileCheck %s
+
+// REQUIRES: mips
+
+// The Mips _GLOBAL_OFFSET_TABLE_ should be defined at the start of the .got
+
+.globl a
+.hidden a
+.type a,@object
+.comm a,4,4
+
+.globl f
+.type f,@function
+f:
+ ld $v0,%got_page(a)($gp)
+ daddiu $v0,$v0,%got_ofst(a)
+
+.global _start
+.type _start,@function
+_start:
+ lw $t0,%call16(f)($gp)
+ .word _GLOBAL_OFFSET_TABLE_ - .
+// CHECK: Name: _GLOBAL_OFFSET_TABLE_ (1)
+// CHECK-NEXT: Value: 0x20000
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Local (0x0)
+// CHECK-NEXT: Type: None (0x0)
+// CHECK-NEXT: Other [ (0x2)
+// CHECK-NEXT: STV_HIDDEN (0x2)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Section: .got (0x9)
diff --git a/test/ELF/global-offset-table-position.s b/test/ELF/global-offset-table-position.s
new file mode 100644
index 000000000..f1195b2cf
--- /dev/null
+++ b/test/ELF/global-offset-table-position.s
@@ -0,0 +1,31 @@
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
+// RUN: ld.lld --hash-style=sysv -shared %t -o %t2
+// RUN: llvm-readobj -t %t2 | FileCheck %s
+// REQUIRES: x86
+
+// The X86_64 _GLOBAL_OFFSET_TABLE_ is defined at the end of the .got section.
+.globl a
+.type a,@object
+.comm a,4,4
+
+.globl f
+.type f,@function
+f:
+movq a@GOTPCREL(%rip), %rax
+
+.global _start
+.type _start,@function
+_start:
+callq f@PLT
+.data
+.long _GLOBAL_OFFSET_TABLE_ - .
+
+// CHECK: Name: _GLOBAL_OFFSET_TABLE_
+// CHECK-NEXT: Value: 0x30D8
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Binding: Local
+// CHECK-NEXT: Type: None (0x0)
+// CHECK-NEXT: Other [
+// CHECK-NEXT: STV_HIDDEN
+// CHECK-NEXT: ]
+// CHECK-NEXT: Section: .got
diff --git a/test/ELF/global_offset_table_shared.s b/test/ELF/global_offset_table_shared.s
index 7935925ae..03af02e58 100644
--- a/test/ELF/global_offset_table_shared.s
+++ b/test/ELF/global_offset_table_shared.s
@@ -1,9 +1,14 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
-// RUN: ld.lld -shared %t -o %t2
+// RUN: ld.lld --hash-style=sysv -shared %t -o %t2
// RUN: llvm-readobj -t %t2 | FileCheck %s
-.long _GLOBAL_OFFSET_TABLE_
+.long _GLOBAL_OFFSET_TABLE_ - .
// CHECK: Name: _GLOBAL_OFFSET_TABLE_
-// CHECK-NEXT: Value:
+// CHECK-NEXT: Value: 0x2060
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
+// CHECK-NEXT: Type: None
+// CHECK-NEXT: Other [ (0x2)
+// CHECK-NEXT: STV_HIDDEN (0x2)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Section: .got
diff --git a/test/ELF/gnu-hash-table-copy.s b/test/ELF/gnu-hash-table-copy.s
new file mode 100644
index 000000000..9d9116325
--- /dev/null
+++ b/test/ELF/gnu-hash-table-copy.s
@@ -0,0 +1,30 @@
+# REQUIRES: x86
+
+# RUN: echo ".global foo; .type foo, @object; .size foo, 4; foo:; .long 0" > %t.s
+# RUN: echo ".global bar; .type bar, @object; .size bar, 4; bar:; .long 0" >> %t.s
+# RUN: echo ".global zed; .type zed, @function; zed:" >> %t.s
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %t.s -o %t1.o
+# RUN: ld.lld %t1.o -o %t1.so -shared
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t2.o
+# RUN: ld.lld --hash-style=gnu %t2.o %t1.so -o %t2
+
+# RUN: llvm-readelf --symbols --gnu-hash-table %t2 | FileCheck %s
+
+# CHECK: Symbol table '.dynsym' contains 4 entries:
+# CHECK-NEXT: Num: Value Size Type Bind Vis Ndx Name
+# CHECK-NEXT: 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND @
+# CHECK-NEXT: 1: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND foo@
+# CHECK-DAG: : {{.*}} 4 OBJECT GLOBAL DEFAULT {{.*}} bar@
+# CHECK-DAG: : {{.*}} 0 FUNC GLOBAL DEFAULT UND zed@
+
+# CHECK: First Hashed Symbol Index: 2
+
+.global _start
+_start:
+
+.quad bar
+.quad zed
+
+.data
+.quad foo
diff --git a/test/ELF/gnu-hash-table.s b/test/ELF/gnu-hash-table.s
index 0e37574aa..7df8dfc8d 100644
--- a/test/ELF/gnu-hash-table.s
+++ b/test/ELF/gnu-hash-table.s
@@ -6,10 +6,29 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t-x86_64.o
# RUN: llvm-mc -filetype=obj -triple=powerpc64-pc-linux %s -o %t-ppc64.o
+# RUN: echo ".global zed; zed:" > %t2.s
+# RUN: llvm-mc -filetype=obj -triple=i386-pc-linux %t2.s -o %t2-i386.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %t2.s -o %t2-x86_64.o
+# RUN: llvm-mc -filetype=obj -triple=powerpc64-pc-linux %t2.s -o %t2-ppc64.o
+
+# RUN: rm -f %t2-i386.a %t2-x86_64.a %t2-ppc64.a
+# RUN: llvm-ar rc %t2-i386.a %t2-i386.o
+# RUN: llvm-ar rc %t2-x86_64.a %t2-x86_64.o
+# RUN: llvm-ar rc %t2-ppc64.a %t2-ppc64.o
+
+# RUN: echo ".global xyz; xyz:" > %t3.s
+# RUN: llvm-mc -filetype=obj -triple=i386-pc-linux %t3.s -o %t3-i386.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %t3.s -o %t3-x86_64.o
+# RUN: llvm-mc -filetype=obj -triple=powerpc64-pc-linux %t3.s -o %t3-ppc64.o
+
+# RUN: ld.lld -shared %t3-i386.o -o %t3-i386.so
+# RUN: ld.lld -shared %t3-x86_64.o -o %t3-x86_64.so
+# RUN: ld.lld -shared %t3-ppc64.o -o %t3-ppc64.so
+
# RUN: ld.lld -shared --hash-style=gnu -o %te-i386.so %te-i386.o
-# RUN: ld.lld -shared -hash-style=gnu -o %t-i386.so %t-i386.o
-# RUN: ld.lld -shared -hash-style=gnu -o %t-x86_64.so %t-x86_64.o
-# RUN: ld.lld -shared --hash-style both -o %t-ppc64.so %t-ppc64.o
+# RUN: ld.lld -shared -hash-style=gnu -o %t-i386.so %t-i386.o %t2-i386.a %t3-i386.so
+# RUN: ld.lld -shared -hash-style=gnu -o %t-x86_64.so %t-x86_64.o %t2-x86_64.a %t3-x86_64.so
+# RUN: ld.lld -shared --hash-style both -o %t-ppc64.so %t-ppc64.o %t2-ppc64.a %t3-ppc64.so
# RUN: llvm-readobj -dyn-symbols -gnu-hash-table %te-i386.so \
# RUN: | FileCheck %s -check-prefix=EMPTY
@@ -70,6 +89,16 @@
# I386: Section: Undefined
# I386: }
# I386: Symbol {
+# I386: Name: xyz@
+# I386: Binding: Global
+# I386: Section: Undefined
+# I386: }
+# I386: Symbol {
+# I386: Name: zed@
+# I386: Binding: Weak
+# I386: Section: Undefined
+# I386: }
+# I386: Symbol {
# I386: Name: bar@
# I386: Binding: Global
# I386: Section: .text
@@ -82,11 +111,11 @@
# I386: ]
# I386: GnuHashTable {
# I386-NEXT: Num Buckets: 1
-# I386-NEXT: First Hashed Symbol Index: 2
+# I386-NEXT: First Hashed Symbol Index: 4
# I386-NEXT: Num Mask Words: 1
# I386-NEXT: Shift Count: 5
# I386-NEXT: Bloom Filter: [0x14000220]
-# I386-NEXT: Buckets: [2]
+# I386-NEXT: Buckets: [4]
# I386-NEXT: Values: [0xB8860BA, 0xB887389]
# I386-NEXT: }
@@ -120,6 +149,16 @@
# X86_64: Section: Undefined
# X86_64: }
# X86_64: Symbol {
+# X86_64: Name: xyz@
+# X86_64: Binding: Global
+# X86_64: Section: Undefined
+# X86_64: }
+# X86_64: Symbol {
+# X86_64: Name: zed@
+# X86_64: Binding: Weak
+# X86_64: Section: Undefined
+# X86_64: }
+# X86_64: Symbol {
# X86_64: Name: bar@
# X86_64: Binding: Global
# X86_64: Section: .text
@@ -132,11 +171,11 @@
# X86_64: ]
# X86_64: GnuHashTable {
# X86_64-NEXT: Num Buckets: 1
-# X86_64-NEXT: First Hashed Symbol Index: 2
+# X86_64-NEXT: First Hashed Symbol Index: 4
# X86_64-NEXT: Num Mask Words: 1
# X86_64-NEXT: Shift Count: 6
# X86_64-NEXT: Bloom Filter: [0x400000000004204]
-# X86_64-NEXT: Buckets: [2]
+# X86_64-NEXT: Buckets: [4]
# X86_64-NEXT: Values: [0xB8860BA, 0xB887389]
# X86_64-NEXT: }
@@ -149,10 +188,10 @@
# PPC64-NEXT: Flags [
# PPC64-NEXT: SHF_ALLOC
# PPC64-NEXT: ]
-# PPC64-NEXT: Address: 0x228
-# PPC64-NEXT: Offset: 0x228
+# PPC64-NEXT: Address:
+# PPC64-NEXT: Offset:
# PPC64-NEXT: Size: 36
-# PPC64-NEXT: Link: 1
+# PPC64-NEXT: Link:
# PPC64-NEXT: Info: 0
# PPC64-NEXT: AddressAlignment: 8
# PPC64-NEXT: EntrySize: 0
@@ -170,6 +209,16 @@
# PPC64: Section: Undefined
# PPC64: }
# PPC64: Symbol {
+# PPC64: Name: xyz@
+# PPC64: Binding: Global
+# PPC64: Section: Undefined
+# PPC64: }
+# PPC64: Symbol {
+# PPC64: Name: zed@
+# PPC64: Binding: Weak
+# PPC64: Section: Undefined
+# PPC64: }
+# PPC64: Symbol {
# PPC64: Name: bar@
# PPC64: Binding: Global
# PPC64: Section: .text
@@ -182,14 +231,16 @@
# PPC64: ]
# PPC64: GnuHashTable {
# PPC64-NEXT: Num Buckets: 1
-# PPC64-NEXT: First Hashed Symbol Index: 2
+# PPC64-NEXT: First Hashed Symbol Index: 4
# PPC64-NEXT: Num Mask Words: 1
# PPC64-NEXT: Shift Count: 6
# PPC64-NEXT: Bloom Filter: [0x400000000004204]
-# PPC64-NEXT: Buckets: [2]
+# PPC64-NEXT: Buckets: [4]
# PPC64-NEXT: Values: [0xB8860BA, 0xB887389]
# PPC64-NEXT: }
.globl foo,bar,baz
foo:
bar:
+.weak zed
+.global xyz
diff --git a/test/ELF/gnu-ifunc-dso.s b/test/ELF/gnu-ifunc-dso.s
new file mode 100644
index 000000000..6ceff3b17
--- /dev/null
+++ b/test/ELF/gnu-ifunc-dso.s
@@ -0,0 +1,13 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/gnu-ifunc-dso.s -o %t1.o
+# RUN: ld.lld -shared %t1.o -o %t.so
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t2.o
+# RUN: ld.lld -shared %t2.o %t.so -o %t
+# RUN: llvm-readobj -dyn-relocations %t | FileCheck %s
+
+# CHECK: Dynamic Relocations {
+# CHECK-NEXT: 0x1000 R_X86_64_64 foo 0x0
+# CHECK-NEXT: }
+
+.data
+ .quad foo
diff --git a/test/ELF/gnu-ifunc-dynsym.s b/test/ELF/gnu-ifunc-dynsym.s
new file mode 100644
index 000000000..fca15462d
--- /dev/null
+++ b/test/ELF/gnu-ifunc-dynsym.s
@@ -0,0 +1,19 @@
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: ld.lld -static -export-dynamic %t.o -o %tout
+// RUN: llvm-nm -U %tout | FileCheck %s
+// REQUIRES: x86
+
+// CHECK: __rela_iplt_end
+// CHECK: __rela_iplt_start
+
+.text
+.type foo STT_GNU_IFUNC
+.globl foo
+foo:
+ ret
+
+.globl _start
+_start:
+ call foo
+ movl $__rela_iplt_start,%edx
+ movl $__rela_iplt_end,%edx
diff --git a/test/ELF/gnu-ifunc-gotpcrel.s b/test/ELF/gnu-ifunc-gotpcrel.s
index 2a5814a98..b506ffa8c 100644
--- a/test/ELF/gnu-ifunc-gotpcrel.s
+++ b/test/ELF/gnu-ifunc-gotpcrel.s
@@ -2,7 +2,7 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/gnu-ifunc-gotpcrel.s -o %t2.o
# RUN: ld.lld -shared %t2.o -o %t2.so
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-# RUN: ld.lld %t.o %t2.so -o %t
+# RUN: ld.lld --hash-style=sysv %t.o %t2.so -o %t
# RUN: llvm-readobj -dyn-relocations %t | FileCheck %s
# CHECK: Dynamic Relocations {
diff --git a/test/ELF/gnu-ifunc-i386.s b/test/ELF/gnu-ifunc-i386.s
index 21f1313a9..559e98a3e 100644
--- a/test/ELF/gnu-ifunc-i386.s
+++ b/test/ELF/gnu-ifunc-i386.s
@@ -15,7 +15,7 @@
// CHECK-NEXT: Address: [[RELA:.*]]
// CHECK-NEXT: Offset: 0xD4
// CHECK-NEXT: Size: 16
-// CHECK-NEXT: Link: 6
+// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 4
// CHECK-NEXT: EntrySize: 8
diff --git a/test/ELF/gnu-ifunc-plt-i386.s b/test/ELF/gnu-ifunc-plt-i386.s
index 50f10c5fe..243eff62f 100644
--- a/test/ELF/gnu-ifunc-plt-i386.s
+++ b/test/ELF/gnu-ifunc-plt-i386.s
@@ -1,7 +1,7 @@
// 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: ld.lld --hash-style=sysv %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
diff --git a/test/ELF/gnu-ifunc-plt.s b/test/ELF/gnu-ifunc-plt.s
index cf46380d3..88a099318 100644
--- a/test/ELF/gnu-ifunc-plt.s
+++ b/test/ELF/gnu-ifunc-plt.s
@@ -1,7 +1,7 @@
// 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: ld.lld --hash-style=sysv %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
diff --git a/test/ELF/gnu-ifunc-shared.s b/test/ELF/gnu-ifunc-shared.s
index aee870c28..bde6807e4 100644
--- a/test/ELF/gnu-ifunc-shared.s
+++ b/test/ELF/gnu-ifunc-shared.s
@@ -1,6 +1,6 @@
// 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: ld.lld --hash-style=sysv --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
diff --git a/test/ELF/gnu-ifunc.s b/test/ELF/gnu-ifunc.s
index f86f0300b..17883a320 100644
--- a/test/ELF/gnu-ifunc.s
+++ b/test/ELF/gnu-ifunc.s
@@ -15,7 +15,7 @@
// CHECK-NEXT: Address: [[RELA:.*]]
// CHECK-NEXT: Offset: 0x158
// CHECK-NEXT: Size: 48
-// CHECK-NEXT: Link: 6
+// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 8
// CHECK-NEXT: EntrySize: 24
diff --git a/test/ELF/got-aarch64.s b/test/ELF/got-aarch64.s
index ef6943881..f46946c9f 100644
--- a/test/ELF/got-aarch64.s
+++ b/test/ELF/got-aarch64.s
@@ -1,5 +1,5 @@
// RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-linux %s -o %t.o
-// RUN: ld.lld -shared %t.o -o %t.so
+// RUN: ld.lld --hash-style=sysv -shared %t.o -o %t.so
// RUN: llvm-readobj -s -r %t.so | FileCheck %s
// RUN: llvm-objdump -d %t.so | FileCheck --check-prefix=DISASM %s
// REQUIRES: aarch64
diff --git a/test/ELF/got.s b/test/ELF/got.s
index 57c1baddf..f67ea13d3 100644
--- a/test/ELF/got.s
+++ b/test/ELF/got.s
@@ -1,7 +1,7 @@
// 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 %t.o %t2.so -o %t
+// RUN: ld.lld --hash-style=sysv %t.o %t2.so -o %t
// RUN: llvm-readobj -s -r %t | FileCheck %s
// RUN: llvm-objdump -d %t | FileCheck --check-prefix=DISASM %s
// REQUIRES: x86
diff --git a/test/ELF/got32-i386.s b/test/ELF/got32-i386.s
index 468aaf1f0..00c7c0d6d 100644
--- a/test/ELF/got32-i386.s
+++ b/test/ELF/got32-i386.s
@@ -20,4 +20,4 @@ _start:
# CHECK: .got 00000004 0000000000012000
# RUN: not ld.lld %t.o -o %t -pie 2>&1 | FileCheck %s --check-prefix=ERR
-# ERR: relocation R_386_GOT32 against 'foo' without base register can not be used when PIC enabled
+# ERR: error: can't create dynamic relocation R_386_GOT32 against symbol: foo in readonly segment; recompile object files with -fPIC
diff --git a/test/ELF/got32x-i386.s b/test/ELF/got32x-i386.s
index 9a67d1997..1311472cc 100644
--- a/test/ELF/got32x-i386.s
+++ b/test/ELF/got32x-i386.s
@@ -43,5 +43,5 @@
# RUN: not ld.lld %S/Inputs/i386-got32x-baseless.elf -o %t1 -pie 2>&1 | \
# RUN: FileCheck %s --check-prefix=ERR
-# ERR: relocation R_386_GOT32X against 'foo' without base register can not be used when PIC enabled
-# ERR: relocation R_386_GOT32X against 'foo' without base register can not be used when PIC enabled
+# ERR: error: can't create dynamic relocation R_386_GOT32X against symbol: foo in readonly segment; recompile object files with -fPIC
+# ERR: error: can't create dynamic relocation R_386_GOT32X against symbol: foo in readonly segment; recompile object files with -fPIC
diff --git a/test/ELF/gotpc-relax-nopic.s b/test/ELF/gotpc-relax-nopic.s
index dc7dcf2e9..e51b8bd9f 100644
--- a/test/ELF/gotpc-relax-nopic.s
+++ b/test/ELF/gotpc-relax-nopic.s
@@ -1,6 +1,6 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -relax-relocations -triple=x86_64-unknown-linux %s -o %t.o
-# RUN: ld.lld %t.o -o %t1
+# RUN: ld.lld --hash-style=sysv %t.o -o %t1
# RUN: llvm-readobj -symbols -r %t1 | FileCheck --check-prefix=SYMRELOC %s
# RUN: llvm-objdump -d %t1 | FileCheck --check-prefix=DISASM %s
@@ -25,7 +25,7 @@
# DISASM-NEXT: 201031: {{.*}} xorq $2105344, %r8
# DISASM-NEXT: 201038: {{.*}} testq $2105344, %r15
-# RUN: ld.lld -shared %t.o -o %t2
+# RUN: ld.lld --hash-style=sysv -shared %t.o -o %t2
# 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 {
@@ -64,7 +64,7 @@
# DISASM-PIC-NEXT: 1023: {{.*}} sbbq 8310(%rip), %rsi
# DISASM-PIC-NEXT: 102a: {{.*}} subq 8303(%rip), %rbp
# DISASM-PIC-NEXT: 1031: {{.*}} xorq 8296(%rip), %r8
-# DISASM-PIC-NEXT: 1038: {{.*}} testq 8289(%rip), %r15
+# DISASM-PIC-NEXT: 1038: {{.*}} testq %r15, 8289(%rip)
.data
.type bar, @object
diff --git a/test/ELF/gotpc-relax-und-dso.s b/test/ELF/gotpc-relax-und-dso.s
index ed6c4bc9b..3c5c8ba8c 100644
--- a/test/ELF/gotpc-relax-und-dso.s
+++ b/test/ELF/gotpc-relax-und-dso.s
@@ -2,7 +2,7 @@
# RUN: llvm-mc -filetype=obj -relax-relocations -triple=x86_64-unknown-linux %s -o %t.o
# RUN: llvm-mc -filetype=obj -relax-relocations -triple=x86_64-pc-linux %S/Inputs/gotpc-relax-und-dso.s -o %tdso.o
# RUN: ld.lld -shared %tdso.o -o %t.so
-# RUN: ld.lld -shared %t.o %t.so -o %tout
+# RUN: ld.lld --hash-style=sysv -shared %t.o %t.so -o %tout
# RUN: llvm-readobj -r -s %tout | FileCheck --check-prefix=RELOC %s
# RUN: llvm-objdump -d %tout | FileCheck --check-prefix=DISASM %s
diff --git a/test/ELF/gotpcrelx.s b/test/ELF/gotpcrelx.s
index 95dbf663f..3ccbc56ab 100644
--- a/test/ELF/gotpcrelx.s
+++ b/test/ELF/gotpcrelx.s
@@ -1,7 +1,7 @@
// RUN: llvm-mc -filetype=obj -relax-relocations -triple x86_64-pc-linux-gnu \
// RUN: %s -o %t.o
// RUN: llvm-readobj -r %t.o | FileCheck --check-prefix=RELS %s
-// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: ld.lld --hash-style=sysv %t.o -o %t.so -shared
// RUN: llvm-readobj -s -r %t.so | FileCheck %s
movq foo@GOTPCREL(%rip), %rax
diff --git a/test/ELF/help.s b/test/ELF/help.s
new file mode 100644
index 000000000..255453153
--- /dev/null
+++ b/test/ELF/help.s
@@ -0,0 +1,5 @@
+# RUN: ld.lld --help 2>&1 | FileCheck %s
+# CHECK: OPTIONS:
+# CHECK: --output=<value> Path to file to write output
+# CHECK: --output <value> Path to file to write output
+# CHECK: -o <path> Path to file to write output
diff --git a/test/ELF/i386-got-and-copy.s b/test/ELF/i386-got-and-copy.s
index f5b0b8ec5..81bac22fd 100644
--- a/test/ELF/i386-got-and-copy.s
+++ b/test/ELF/i386-got-and-copy.s
@@ -9,7 +9,7 @@
# RUN: %S/Inputs/copy-in-shared.s -o %t.so.o
# RUN: llvm-mc -filetype=obj -triple=i386-pc-linux %s -o %t.o
# RUN: ld.lld %t.so.o -shared -o %t.so
-# RUN: ld.lld %t.o %t.so -o %t.exe
+# RUN: ld.lld --hash-style=sysv %t.o %t.so -o %t.exe
# RUN: llvm-readobj -r %t.exe | FileCheck %s
# CHECK: Relocations [
diff --git a/test/ELF/i386-gotoff-shared.s b/test/ELF/i386-gotoff-shared.s
index 01242ad01..c22bd6dd5 100644
--- a/test/ELF/i386-gotoff-shared.s
+++ b/test/ELF/i386-gotoff-shared.s
@@ -1,6 +1,6 @@
// 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: ld.lld --hash-style=sysv %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
diff --git a/test/ELF/i386-gotpc-dynamic.s b/test/ELF/i386-gotpc-dynamic.s
index da8a607d5..0ec737e70 100644
--- a/test/ELF/i386-gotpc-dynamic.s
+++ b/test/ELF/i386-gotpc-dynamic.s
@@ -1,6 +1,6 @@
# 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: ld.lld --hash-style=sysv %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
diff --git a/test/ELF/i386-gotpc.s b/test/ELF/i386-gotpc.s
index 8222effd6..d2c5ef3d4 100644
--- a/test/ELF/i386-gotpc.s
+++ b/test/ELF/i386-gotpc.s
@@ -1,6 +1,6 @@
// 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: ld.lld --hash-style=sysv %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
diff --git a/test/ELF/i386-reloc-large-addend.s b/test/ELF/i386-reloc-large-addend.s
index b64464040..5af584475 100644
--- a/test/ELF/i386-reloc-large-addend.s
+++ b/test/ELF/i386-reloc-large-addend.s
@@ -1,3 +1,4 @@
+// REQUIRES: x86
// RUN: llvm-mc %s -o %t.o -triple i386-pc-linux-code16 -filetype=obj
// RUN: echo ".global foo; foo = 0x1" > %t1.s
diff --git a/test/ELF/i386-reloc-range.s b/test/ELF/i386-reloc-range.s
index 47447d0ef..4fb5325e5 100644
--- a/test/ELF/i386-reloc-range.s
+++ b/test/ELF/i386-reloc-range.s
@@ -1,3 +1,4 @@
+// REQUIRES: x86
// RUN: llvm-mc %s -o %t.o -triple i386-pc-linux-code16 -filetype=obj
// RUN: echo ".global foo; foo = 0x10202" > %t1.s
diff --git a/test/ELF/i386-tls-ie-shared.s b/test/ELF/i386-tls-ie-shared.s
index f419eb45d..2b842a86e 100644
--- a/test/ELF/i386-tls-ie-shared.s
+++ b/test/ELF/i386-tls-ie-shared.s
@@ -1,7 +1,7 @@
// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o
// 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: ld.lld --hash-style=sysv -shared %t.o %tso -o %t1
// RUN: llvm-readobj -s -r -d %t1 | FileCheck --check-prefix=GOTRELSHARED %s
// RUN: llvm-objdump -d %t1 | FileCheck --check-prefix=DISASMSHARED %s
diff --git a/test/ELF/icf-non-mergeable.s b/test/ELF/icf-non-mergeable.s
index 378c7b336..3cb5a0484 100644
--- a/test/ELF/icf-non-mergeable.s
+++ b/test/ELF/icf-non-mergeable.s
@@ -1,7 +1,7 @@
// REQUIRES: x86
// This file contains two functions. They are themselves identical,
-// but because they have reloactions against different data section,
+// but because they have relocations against different data sections,
// they are not mergeable.
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1
diff --git a/test/ELF/icf-none.s b/test/ELF/icf-none.s
new file mode 100644
index 000000000..671f2085f
--- /dev/null
+++ b/test/ELF/icf-none.s
@@ -0,0 +1,22 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: ld.lld %t -o %t2 --icf=all --icf=none --verbose | FileCheck %s
+
+# CHECK-NOT: selected .text.f1
+
+.globl _start, f1, f2
+_start:
+ ret
+
+.section .text.f1, "ax"
+f1:
+ mov $60, %rax
+ mov $42, %rdi
+ syscall
+
+.section .text.f2, "ax"
+f2:
+ mov $60, %rax
+ mov $42, %rdi
+ syscall
diff --git a/test/ELF/icf-symbol-type.s b/test/ELF/icf-symbol-type.s
new file mode 100644
index 000000000..dbc5eab58
--- /dev/null
+++ b/test/ELF/icf-symbol-type.s
@@ -0,0 +1,20 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld --hash-style=sysv %t.o -o %t --icf=all -shared
+# RUN: llvm-readelf --dyn-symbols %t | FileCheck %s
+
+# We used to mark bar as absolute.
+
+# CHECK: [[ADDR:[0-9a-z]*]] 0 NOTYPE GLOBAL DEFAULT 4 foo
+# CHECK: [[ADDR]] 0 NOTYPE GLOBAL DEFAULT 4 bar
+
+ .section .text.f,"ax",@progbits
+ .globl foo
+foo:
+ retq
+
+ .section .text.g,"ax",@progbits
+ .globl bar
+bar:
+ retq
diff --git a/test/ELF/image-base.s b/test/ELF/image-base.s
index a6d160744..eb79acdce 100644
--- a/test/ELF/image-base.s
+++ b/test/ELF/image-base.s
@@ -6,6 +6,9 @@
# 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
+# Check alias.
+# RUN: ld.lld -image-base 0x1000000 %t -o %t1
+# RUN: llvm-readobj -program-headers %t1 | FileCheck %s
.global _start
_start:
@@ -41,8 +44,8 @@ _start:
# CHECK-NEXT: Offset: 0x1000
# CHECK-NEXT: VirtualAddress: 0x1001000
# CHECK-NEXT: PhysicalAddress: 0x1001000
-# CHECK-NEXT: FileSize: 1
-# CHECK-NEXT: MemSize: 1
+# CHECK-NEXT: FileSize: 4096
+# CHECK-NEXT: MemSize: 4096
# CHECK-NEXT: Flags [ (0x5)
# CHECK-NEXT: PF_R (0x4)
# CHECK-NEXT: PF_X (0x1)
diff --git a/test/ELF/init_fini_priority.s b/test/ELF/init_fini_priority.s
index 84e5dc35e..b10b92506 100644
--- a/test/ELF/init_fini_priority.s
+++ b/test/ELF/init_fini_priority.s
@@ -1,34 +1,46 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+// RUN: llvm-objdump -section-headers %t | FileCheck %s --check-prefix=OBJ
// RUN: ld.lld %t -o %t.exe
// RUN: llvm-objdump -s %t.exe | FileCheck %s
// REQUIRES: x86
+// OBJ: 3 .init_array
+// OBJ-NEXT: 4 .init_array.100
+// OBJ-NEXT: 5 .init_array.5
+// OBJ-NEXT: 6 .init_array
+// OBJ-NEXT: 7 .init_array
+// OBJ-NEXT: 8 .fini_array
+// OBJ-NEXT: 9 .fini_array.100
+// OBJ-NEXT: 10 .fini_array.5
+// OBJ-NEXT: 11 .fini_array
+// OBJ-NEXT: 12 .fini_array
+
.globl _start
_start:
nop
-.section .init_array, "aw", @init_array
+.section .init_array, "aw", @init_array, unique, 0
.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
+.section .init_array, "aw", @init_array, unique, 1
.byte 4
-.section .init_array, "aw", @init_array
+.section .init_array, "aw", @init_array, unique, 2
.byte 5
-.section .fini_array, "aw", @fini_array
+.section .fini_array, "aw", @fini_array, unique, 0
.align 8
.byte 0x11
.section .fini_array.100, "aw", @fini_array
.long 0x12
.section .fini_array.5, "aw", @fini_array
.byte 0x13
-.section .fini_array, "aw", @fini_array
+.section .fini_array, "aw", @fini_array, unique, 1
.byte 0x14
-.section .fini_array, "aw", @fini_array
+.section .fini_array, "aw", @fini_array, unique, 2
.byte 0x15
// CHECK: Contents of section .init_array:
diff --git a/test/ELF/invalid-z.s b/test/ELF/invalid-z.s
new file mode 100644
index 000000000..a5343c93e
--- /dev/null
+++ b/test/ELF/invalid-z.s
@@ -0,0 +1,9 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: not ld.lld %t.o -o %t -z max-page-size 2>&1 | FileCheck %s
+# CHECK: invalid max-page-size
+# CHECK-NOT: error
+
+.global _start
+_start:
+ nop
diff --git a/test/ELF/invalid/Inputs/invalid-relocation-x64.elf b/test/ELF/invalid/Inputs/invalid-relocation-x64.elf
deleted file mode 100644
index 25df29446..000000000
--- a/test/ELF/invalid/Inputs/invalid-relocation-x64.elf
+++ /dev/null
Binary files differ
diff --git a/test/ELF/invalid/invalid-debug-relocations.test b/test/ELF/invalid/invalid-debug-relocations.test
new file mode 100644
index 000000000..9c86b6743
--- /dev/null
+++ b/test/ELF/invalid/invalid-debug-relocations.test
@@ -0,0 +1,40 @@
+# REQUIRES: x86
+# RUN: yaml2obj %s -o %t.o
+# RUN: not ld.lld -gdb-index %t.o -o %t.exe 2>&1 | FileCheck %s
+
+# CHECK: error: {{.*}}invalid-debug-relocations.test.tmp.o:(.debug_info+0x0): has non-ABS relocation Unknown (255) against symbol '_start'
+
+!ELF
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_386
+Sections:
+ - Type: SHT_PROGBITS
+ Name: .text
+ Flags: [ ]
+ AddressAlign: 0x04
+ Content: "0000"
+ - Type: SHT_PROGBITS
+ Name: .debug_info
+ Flags: [ ]
+ AddressAlign: 0x04
+ Content: "0000"
+ - Type: SHT_REL
+ Name: .rel.debug_info
+ Link: .symtab
+ Info: .debug_info
+ Relocations:
+ - Offset: 0
+ Symbol: _start
+ Type: 0xFF
+ - Offset: 4
+ Symbol: _start
+ Type: 0xFF
+Symbols:
+ Global:
+ - Name: _start
+ Type: STT_FUNC
+ Section: .text
+ Value: 0x0
diff --git a/test/ELF/invalid/invalid-relocation-x64.test b/test/ELF/invalid/invalid-relocation-x64.test
index d52cf87c1..a9b316415 100644
--- a/test/ELF/invalid/invalid-relocation-x64.test
+++ b/test/ELF/invalid/invalid-relocation-x64.test
@@ -1,7 +1,11 @@
-## 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.
+# REQUIRES: x86
+# RUN: yaml2obj %s -o %t1.o
+# RUN: echo ".global foo; foo:" > %t2.s
+# RUN: llvm-mc %t2.s -o %t2.o -filetype=obj -triple x86_64-pc-linux
+# RUN: not ld.lld %t1.o %t2.o -o /dev/null 2>&1 | FileCheck %s
+# CHECK: error: unrecognized reloc 152
+# CHECK: error: unrecognized reloc 153
+
!ELF
FileHeader:
Class: ELFCLASS64
@@ -19,12 +23,11 @@ Sections:
Info: .text
Relocations:
- Offset: 0x0000000000000000
- Symbol: ''
- Type: R_X86_64_NONE
+ Symbol: foo
+ Type: 0x98
- 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: {{.*}}invalid-relocation-x64.elf: unknown relocation type: Unknown (152)
-# CHECK: {{.*}}invalid-relocation-x64.elf: unknown relocation type: Unknown (153)
+ Symbol: foo
+ Type: 0x99
+Symbols:
+ Global:
+ - Name: foo
diff --git a/test/ELF/invalid/tls-symbol.s b/test/ELF/invalid/tls-symbol.s
index 7c65c6c9f..354ca573d 100644
--- a/test/ELF/invalid/tls-symbol.s
+++ b/test/ELF/invalid/tls-symbol.s
@@ -1,5 +1,5 @@
# REQUIRES: x86
-## The test file contains a STT_TLS symbol but has no TLS section.
+## The test file contains an 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
+# CHECK: has an STT_TLS symbol but doesn't have an SHF_TLS section
diff --git a/test/ELF/libsearch.s b/test/ELF/libsearch.s
index 32be866a6..d21baf9dd 100644
--- a/test/ELF/libsearch.s
+++ b/test/ELF/libsearch.s
@@ -60,6 +60,7 @@
// Check long forms as well
// RUN: ld.lld -o %t3 %t.o --library-path=%t.dir --library=ls
+// RUN: ld.lld -o %t3 %t.o --library-path %t.dir --library ls
// Should not search for dynamic libraries if -Bstatic is specified
// RUN: ld.lld -o %t3 %t.o -L%t.dir -Bstatic -lls
diff --git a/test/ELF/linkerscript/Inputs/common-filespec1.s b/test/ELF/linkerscript/Inputs/common-filespec1.s
new file mode 100644
index 000000000..9e25d1291
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/common-filespec1.s
@@ -0,0 +1,2 @@
+.comm common_uniq_1,8,8
+.comm common_multiple,16,8
diff --git a/test/ELF/linkerscript/Inputs/common-filespec2.s b/test/ELF/linkerscript/Inputs/common-filespec2.s
new file mode 100644
index 000000000..ceac01793
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/common-filespec2.s
@@ -0,0 +1,2 @@
+.comm common_uniq_2,16,16
+.comm common_multiple,32,8
diff --git a/test/ELF/linkerscript/Inputs/symbol-reserved.script b/test/ELF/linkerscript/Inputs/symbol-reserved.script
new file mode 100644
index 000000000..269bb120d
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/symbol-reserved.script
@@ -0,0 +1,5 @@
+SECTIONS
+{
+ .text : { *(.text) }
+ PROVIDE_HIDDEN(_end = .);
+}
diff --git a/test/ELF/linkerscript/absolute2.s b/test/ELF/linkerscript/absolute2.s
new file mode 100644
index 000000000..513468d25
--- /dev/null
+++ b/test/ELF/linkerscript/absolute2.s
@@ -0,0 +1,17 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+
+# RUN: echo "SECTIONS { .text : { *(.text) } foo = ABSOLUTE(_start) + _start; };" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o
+# RUN: llvm-objdump -t %t | FileCheck %s
+
+# RUN: echo "SECTIONS { .text : { *(.text) } foo = _start + ABSOLUTE(_start); };" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o
+# RUN: llvm-objdump -t %t | FileCheck %s
+
+# CHECK: 0000000000000001 .text 00000000 _start
+# CHECK: 0000000000000002 .text 00000000 foo
+
+ .global _start
+ nop
+_start:
diff --git a/test/ELF/linkerscript/align-section-offset.s b/test/ELF/linkerscript/align-section-offset.s
new file mode 100644
index 000000000..9c1603a19
--- /dev/null
+++ b/test/ELF/linkerscript/align-section-offset.s
@@ -0,0 +1,11 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { .foo : ALIGN(2M) { *(.foo) } }" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o -shared
+# RUN: llvm-readelf -S -l %t | FileCheck %s
+
+# CHECK: .foo PROGBITS 0000000000200000 200000 000008 00 WA 0 0 2097152
+# CHECK: LOAD 0x200000 0x0000000000200000 0x0000000000200000 {{.*}} RW 0x200000
+
+ .section .foo, "aw"
+ .quad 42
diff --git a/test/ELF/linkerscript/align-section.s b/test/ELF/linkerscript/align-section.s
new file mode 100644
index 000000000..d26f15c87
--- /dev/null
+++ b/test/ELF/linkerscript/align-section.s
@@ -0,0 +1,6 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { .foo : ALIGN(2M) { } }" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o -shared
+
+# We would crash if an empty section had an ALIGN.
diff --git a/test/ELF/linkerscript/align.s b/test/ELF/linkerscript/align.s
index 357f54ce1..1d70fab45 100644
--- a/test/ELF/linkerscript/align.s
+++ b/test/ELF/linkerscript/align.s
@@ -66,6 +66,43 @@
# SYMBOLS-NEXT: 0000000000011000 .bbb 00000000 __start_bbb
# SYMBOLS-NEXT: 0000000000012000 .bbb 00000000 __end_bbb
+## Check that ALIGN zero do nothing and does not crash #1.
+# RUN: echo "SECTIONS { . = ALIGN(0x123, 0); .aaa : { *(.aaa) } }" > %t.script
+# RUN: ld.lld -o %t4 --script %t.script %t
+# RUN: llvm-objdump -section-headers %t4 | FileCheck %s -check-prefix=ZERO
+
+# ZERO: Sections:
+# ZERO-NEXT: Idx Name Size Address Type
+# ZERO-NEXT: 0 00000000 0000000000000000
+# ZERO-NEXT: 1 .aaa 00000008 0000000000000123 DATA
+
+## Check that ALIGN zero do nothing and does not crash #2.
+# RUN: echo "SECTIONS { . = 0x123; . = ALIGN(0); .aaa : { *(.aaa) } }" > %t.script
+# RUN: ld.lld -o %t5 --script %t.script %t
+# RUN: llvm-objdump -section-headers %t5 | FileCheck %s -check-prefix=ZERO
+
+
+# RUN: echo "SECTIONS { \
+# RUN: . = 0xff8; \
+# RUN: .aaa : { \
+# RUN: *(.aaa) \
+# RUN: foo = ALIGN(., 0x100); \
+# RUN: bar = .; \
+# RUN: zed1 = ALIGN(., 0x100) + 1; \
+# RUN: zed2 = ALIGN(., 0x100) - 1; \
+# RUN: } \
+# RUN: .bbb : { *(.bbb); } \
+# RUN: .ccc : { *(.ccc); } \
+# RUN: .text : { *(.text); } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=OFFSET %s
+
+# OFFSET: 0000000000001000 .aaa 00000000 foo
+# OFFSET: 0000000000001000 .aaa 00000000 bar
+# OFFSET: 0000000000001001 .aaa 00000000 zed1
+# OFFSET: 0000000000000fff .aaa 00000000 zed2
+
.global _start
_start:
nop
diff --git a/test/ELF/linkerscript/arm-exidx-order.s b/test/ELF/linkerscript/arm-exidx-order.s
new file mode 100644
index 000000000..3c6791f77
--- /dev/null
+++ b/test/ELF/linkerscript/arm-exidx-order.s
@@ -0,0 +1,19 @@
+# REQUIRES: arm
+# RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
+# RUN: echo "SECTIONS { . = SIZEOF_HEADERS; \
+# RUN: .ARM.exidx : { *(.ARM.exidx*) } \
+# RUN: .foo : { _foo = 0; } }" > %t.script
+# RUN: ld.lld -T %t.script %t.o -shared -o %t.so
+# RUN: llvm-readobj -s %t.so | FileCheck %s
+
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .foo
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: ]
+
+.fnstart
+.cantunwind
+.fnend
diff --git a/test/ELF/linkerscript/at-addr.s b/test/ELF/linkerscript/at-addr.s
index 0eddf3d9e..f0ab989c5 100644
--- a/test/ELF/linkerscript/at-addr.s
+++ b/test/ELF/linkerscript/at-addr.s
@@ -9,10 +9,6 @@
# RUN: llvm-readobj -program-headers %t2 | FileCheck %s
# CHECK: Type: PT_LOAD
-# CHECK-NEXT: Offset: 0x0
-# CHECK-NEXT: VirtualAddress: 0x0
-# CHECK-NEXT: PhysicalAddress: 0x0
-# CHECK: Type: PT_LOAD
# CHECK-NEXT: Offset: 0x1000
# CHECK-NEXT: VirtualAddress: 0x1000
# CHECK-NEXT: PhysicalAddress: 0xB00
diff --git a/test/ELF/linkerscript/at.s b/test/ELF/linkerscript/at.s
index 26441f1ff..430e68a53 100644
--- a/test/ELF/linkerscript/at.s
+++ b/test/ELF/linkerscript/at.s
@@ -13,31 +13,6 @@
# 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
diff --git a/test/ELF/linkerscript/common-exclude.s b/test/ELF/linkerscript/common-exclude.s
new file mode 100644
index 000000000..ef2d51b1b
--- /dev/null
+++ b/test/ELF/linkerscript/common-exclude.s
@@ -0,0 +1,86 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %tfile0.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/common-filespec1.s -o %tfile1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/common-filespec2.s -o %tfile2.o
+# RUN: echo "SECTIONS { .common.incl : { *(EXCLUDE_FILE (*file2.o) COMMON) } .common.excl : { *(COMMON) } }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %tfile0.o %tfile1.o %tfile2.o
+# RUN: llvm-readobj -s -t %t1 | FileCheck %s
+
+# Commons from file0 and file1 are not excluded, so they must be in .common.incl
+# Commons from file2 are excluded from the first rule and should be caught by
+# the second in .common.excl
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .common.incl
+# CHECK-NEXT: Type: SHT_NOBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x8
+# CHECK-NEXT: Offset: 0x
+# CHECK-NEXT: Size: 16
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 8
+# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: }
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .common.excl
+# CHECK-NEXT: Type: SHT_NOBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x20
+# CHECK-NEXT: Offset: 0x
+# CHECK-NEXT: Size: 48
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 16
+# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: }
+# CHECK: Symbol {
+# CHECK: Name: common_multiple
+# CHECK-NEXT: Value: 0x20
+# CHECK-NEXT: Size: 32
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .common.excl
+# CHECK-NEXT: }
+# CHECK: Symbol {
+# CHECK: Name: common_uniq_0
+# CHECK-NEXT: Value: 0x8
+# CHECK-NEXT: Size: 4
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .common.incl
+# CHECK-NEXT: }
+# CHECK: Symbol {
+# CHECK: Name: common_uniq_1
+# CHECK-NEXT: Value: 0x10
+# CHECK-NEXT: Size: 8
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .common.incl
+# CHECK-NEXT: }
+# CHECK: Symbol {
+# CHECK: Name: common_uniq_2
+# CHECK-NEXT: Value: 0x40
+# CHECK-NEXT: Size: 16
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .common.excl
+# CHECK-NEXT: }
+
+.globl _start
+_start:
+ jmp _start
+
+.comm common_uniq_0,4,4
+.comm common_multiple,8,8
diff --git a/test/ELF/linkerscript/common-filespec.s b/test/ELF/linkerscript/common-filespec.s
new file mode 100644
index 000000000..25bb486ed
--- /dev/null
+++ b/test/ELF/linkerscript/common-filespec.s
@@ -0,0 +1,105 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %tfile0.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/common-filespec1.s -o %tfile1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/common-filespec2.s -o %tfile2.o
+# RUN: echo "SECTIONS { .common_0 : { *file0.o(COMMON) } .common_1 : { *file1.o(COMMON) } .common_2 : { *file2.o(COMMON) } }" > %t.script
+# RUN: ld.lld -o %t1 --script %t.script %tfile0.o %tfile1.o %tfile2.o
+# RUN: llvm-readobj -s -t %t1 | FileCheck %s
+
+# Make sure all 3 sections are allocated and they have sizes and alignments
+# corresponding to the commons assigned to them
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .common_0
+# CHECK-NEXT: Type: SHT_NOBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x4
+# CHECK-NEXT: Offset: 0x
+# CHECK-NEXT: Size: 4
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 4
+# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: }
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .common_1
+# CHECK-NEXT: Type: SHT_NOBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x8
+# CHECK-NEXT: Offset: 0x
+# CHECK-NEXT: Size: 8
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 8
+# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: }
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .common_2
+# CHECK-NEXT: Type: SHT_NOBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x10
+# CHECK-NEXT: Offset: 0x
+# CHECK-NEXT: Size: 48
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 16
+# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: }
+
+# Commons with unique name in each file must be assigned to that file's section.
+# For a common with multiple definitions, the largest one wins and it must be
+# assigned to the section from the file which provided the winning def
+# CHECK: Symbol {
+# CHECK: Name: common_multiple
+# CHECK-NEXT: Value: 0x10
+# CHECK-NEXT: Size: 32
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .common_2
+# CHECK-NEXT: }
+# CHECK: Symbol {
+# CHECK: Name: common_uniq_0
+# CHECK-NEXT: Value: 0x4
+# CHECK-NEXT: Size: 4
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .common_0
+# CHECK-NEXT: }
+# CHECK: Symbol {
+# CHECK: Name: common_uniq_1
+# CHECK-NEXT: Value: 0x8
+# CHECK-NEXT: Size: 8
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .common_1
+# CHECK-NEXT: }
+# CHECK: Symbol {
+# CHECK: Name: common_uniq_2
+# CHECK-NEXT: Value: 0x30
+# CHECK-NEXT: Size: 16
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .common_2
+# CHECK-NEXT: }
+
+.globl _start
+_start:
+ jmp _start
+
+.comm common_uniq_0,4,4
+.comm common_multiple,8,8
diff --git a/test/ELF/linkerscript/common.s b/test/ELF/linkerscript/common.s
index 2e5972d52..52f037152 100644
--- a/test/ELF/linkerscript/common.s
+++ b/test/ELF/linkerscript/common.s
@@ -4,8 +4,6 @@
# 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
@@ -16,7 +14,7 @@
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x200
# CHECK-NEXT: Offset: 0x
-# CHECK-NEXT: Size: 256
+# CHECK-NEXT: Size: 384
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
# CHECK-NEXT: AddressAlignment: 256
@@ -24,7 +22,7 @@
# CHECK-NEXT: }
# CHECK: Symbol {
# CHECK: Name: q1
-# CHECK-NEXT: Value: 0x280
+# CHECK-NEXT: Value: 0x200
# CHECK-NEXT: Size: 128
# CHECK-NEXT: Binding: Global
# CHECK-NEXT: Type: Object
@@ -33,7 +31,7 @@
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
# CHECK-NEXT: Name: q2
-# CHECK-NEXT: Value: 0x200
+# CHECK-NEXT: Value: 0x300
# CHECK-NEXT: Size: 128
# CHECK-NEXT: Binding: Global
# CHECK-NEXT: Type: Object
diff --git a/test/ELF/linkerscript/compress-debug-sections.s b/test/ELF/linkerscript/compress-debug-sections.s
index 6798a217b..5e8cd0040 100644
--- a/test/ELF/linkerscript/compress-debug-sections.s
+++ b/test/ELF/linkerscript/compress-debug-sections.s
@@ -10,12 +10,12 @@
# RUN: echo "SECTIONS { }" > %t.script
# RUN: ld.lld -O0 %t1.o %t2.o %t.script -o %t1 --compress-debug-sections=zlib
-# RUN: llvm-dwarfdump %t1 | FileCheck %s
+# RUN: llvm-dwarfdump -a %t1 | FileCheck %s
# RUN: llvm-readobj -s %t1 | FileCheck %s --check-prefix=ZLIBFLAGS
# RUN: echo "SECTIONS { .debug_str 0 : { *(.debug_str) } }" > %t2.script
# RUN: ld.lld -O0 %t1.o %t2.o %t2.script -o %t2 --compress-debug-sections=zlib
-# RUN: llvm-dwarfdump %t2 | FileCheck %s
+# RUN: llvm-dwarfdump -a %t2 | FileCheck %s
# RUN: llvm-readobj -s %t2 | FileCheck %s --check-prefix=ZLIBFLAGS
# CHECK: .debug_str contents:
diff --git a/test/ELF/linkerscript/data-commands-gc.s b/test/ELF/linkerscript/data-commands-gc.s
index 46ce6a97c..1afcc9a3b 100644
--- a/test/ELF/linkerscript/data-commands-gc.s
+++ b/test/ELF/linkerscript/data-commands-gc.s
@@ -1,9 +1,10 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
# RUN: echo "SECTIONS { .text : { *(.text*) QUAD(bar) } }" > %t.script
-# RUN: ld.lld --gc-sections -o %t %t.o --script %t.script | FileCheck -allow-empty %s
+# RUN: ld.lld --gc-sections -o %t %t.o --script %t.script
+# RUN: llvm-objdump -t %t | FileCheck %s
-# CHECK-NOT: unable to evaluate expression: input section .rodata.bar has no output section assigned
+# CHECK: 0000000000000011 .rodata 00000000 bar
.section .rodata.bar
.quad 0x1122334455667788
diff --git a/test/ELF/linkerscript/data-segment-relro.s b/test/ELF/linkerscript/data-segment-relro.s
index 7f69319dd..e835f42e2 100644
--- a/test/ELF/linkerscript/data-segment-relro.s
+++ b/test/ELF/linkerscript/data-segment-relro.s
@@ -19,9 +19,9 @@
## 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: ld.lld --hash-style=sysv -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: ld.lld --hash-style=sysv -z relro %t1.o %t2.so --script %t.script -o %t2
# RUN: llvm-readobj -s %t2 | FileCheck %s
# CHECK: Section {
diff --git a/test/ELF/linkerscript/diagnostic.s b/test/ELF/linkerscript/diagnostic.s
index bd1ebd01b..af185729c 100644
--- a/test/ELF/linkerscript/diagnostic.s
+++ b/test/ELF/linkerscript/diagnostic.s
@@ -50,9 +50,9 @@
# 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: ^
+# ERR6: error: {{.*}}.script:1: unknown directive: UNKNOWN_TAG
+# ERR6-NEXT: >>> UNKNOWN_TAG {
+# ERR6-NEXT: >>> ^
## One more check that text of lines and pointer to 'bad' token are working ok.
# RUN: echo "SECTIONS {" > %t.script
@@ -62,8 +62,8 @@
# 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: ^
+# ERR7-NEXT: >>> boom .temp : { *(.temp) } }
+# ERR7-NEXT: >>> ^
## Check tokenize() error
# RUN: echo "SECTIONS {}" > %t.script
@@ -89,8 +89,8 @@
# 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: ^
+# ERR10-NEXT: >>> boom .temp : { *(.temp) } }
+# ERR10-NEXT: >>> ^
## Check error reporting in script with INCLUDE directive.
# RUN: echo "SECTIONS {" > %t.script.inc
diff --git a/test/ELF/linkerscript/discard-section-err.s b/test/ELF/linkerscript/discard-section-err.s
index 5d9955545..8ad5b486c 100644
--- a/test/ELF/linkerscript/discard-section-err.s
+++ b/test/ELF/linkerscript/discard-section-err.s
@@ -21,3 +21,5 @@
# RUN: not ld.lld -pie -o %t --script %t.script %t.o 2>&1 | \
# RUN: FileCheck -check-prefix=DYNSTR %s
# DYNSTR: discarding .dynstr section is not allowed
+
+.comm foo,4,4
diff --git a/test/ELF/linkerscript/early-assign-symbol.s b/test/ELF/linkerscript/early-assign-symbol.s
index 0626e66e6..5f6117863 100644
--- a/test/ELF/linkerscript/early-assign-symbol.s
+++ b/test/ELF/linkerscript/early-assign-symbol.s
@@ -1,14 +1,28 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
-# RUN: echo "SECTIONS { aaa = 1 + ABSOLUTE(foo - 1); .text : { *(.text*) } }" > %t1.script
-# RUN: not ld.lld -o %t --script %t1.script %t.o 2>&1 | FileCheck %s
-
-# RUN: echo "SECTIONS { aaa = ABSOLUTE(foo - 1) + 1; .text : { *(.text*) } }" > %t2.script
-# RUN: not ld.lld -o %t --script %t2.script %t.o 2>&1 | FileCheck %s
+# RUN: echo "SECTIONS { aaa = foo | 1; .text : { *(.text*) } }" > %t3.script
+# RUN: not ld.lld -o %t --script %t3.script %t.o 2>&1 | FileCheck %s
# CHECK: error: {{.*}}.script:1: unable to evaluate expression: input section .text has no output section assigned
+# Simple cases that we can handle.
+
+# RUN: echo "SECTIONS { aaa = ABSOLUTE(foo - 1) + 1; .text : { *(.text*) } }" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o
+# RUN: llvm-objdump -t %t | FileCheck --check-prefix=VAL %s
+
+# RUN: echo "SECTIONS { aaa = 1 + ABSOLUTE(foo - 1); .text : { *(.text*) } }" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o
+# RUN: llvm-objdump -t %t | FileCheck --check-prefix=VAL %s
+
+# RUN: echo "SECTIONS { aaa = ABSOLUTE(foo); .text : { *(.text*) } }" > %t4.script
+# RUN: ld.lld -o %t --script %t4.script %t.o
+# RUN: llvm-objdump -t %t | FileCheck --check-prefix=VAL %s
+
+# VAL: 0000000000000000 .text 00000000 foo
+# VAL: 0000000000000000 *ABS* 00000000 aaa
+
.section .text
.globl foo
foo:
diff --git a/test/ELF/linkerscript/emit-reloc.s b/test/ELF/linkerscript/emit-reloc.s
index 725f314a9..2fe127af8 100644
--- a/test/ELF/linkerscript/emit-reloc.s
+++ b/test/ELF/linkerscript/emit-reloc.s
@@ -1,7 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
# RUN: echo "SECTIONS { .rela.dyn : { *(.rela.data) } }" > %t.script
-# RUN: ld.lld -T %t.script --emit-relocs %t.o -o %t.so -shared
+# RUN: ld.lld --hash-style=sysv -T %t.script --emit-relocs %t.o -o %t.so -shared
# RUN: llvm-readobj -r %t.so | FileCheck %s
.data
diff --git a/test/ELF/linkerscript/exidx-crash.s b/test/ELF/linkerscript/exidx-crash.s
new file mode 100644
index 000000000..c29d01354
--- /dev/null
+++ b/test/ELF/linkerscript/exidx-crash.s
@@ -0,0 +1,7 @@
+# REQUIRES: aarch64
+
+# We used to crash on this.
+
+# RUN: llvm-mc %s -o %t.o -filetype=obj -triple=aarch64-pc-linux
+# RUN: echo "SECTIONS { .ARM.exidx : { *(.foo) } }" > %t.script
+# RUN: ld.lld -T %t.script %t.o -o %t
diff --git a/test/ELF/linkerscript/extend-pt-load.s b/test/ELF/linkerscript/extend-pt-load.s
index f9a77c8c1..fb9ea6fc0 100644
--- a/test/ELF/linkerscript/extend-pt-load.s
+++ b/test/ELF/linkerscript/extend-pt-load.s
@@ -15,7 +15,7 @@
# 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: ld.lld --hash-style=sysv -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
@@ -37,7 +37,7 @@
# 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: ld.lld --hash-style=sysv -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
@@ -60,7 +60,7 @@
# 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: ld.lld --hash-style=sysv -o %t3 --script %t.script %t.o -shared
# RUN: llvm-readobj --elf-output-style=GNU -l -s %t3 | FileCheck --check-prefix=CHECK1 %s
nop
diff --git a/test/ELF/linkerscript/filename-spec.s b/test/ELF/linkerscript/filename-spec.s
index d4bc495ef..5f075a8e5 100644
--- a/test/ELF/linkerscript/filename-spec.s
+++ b/test/ELF/linkerscript/filename-spec.s
@@ -33,24 +33,63 @@
# 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 %s -o 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: %p/Inputs/filename-spec.s -o 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: filename-spec1.o 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: filename-spec1.o filename-spec2.o
# RUN: llvm-objdump -s %t6 | FileCheck --check-prefix=FIRSTSECOND %s
+# RUN: mkdir -p %t.testdir1 %t.testdir2
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.testdir1/filename-spec1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \
+# RUN: %p/Inputs/filename-spec.s -o %t.testdir2/filename-spec2.o
+# RUN: llvm-ar rsc %t.testdir1/lib1.a %t.testdir1/filename-spec1.o
+# RUN: llvm-ar rsc %t.testdir2/lib2.a %t.testdir2/filename-spec2.o
+
+# Verify matching of archive library names.
+# RUN: echo "SECTIONS { .foo : { \
+# RUN: *lib2*(.foo) \
+# RUN: *lib1*(.foo) } }" > %t7.script
+# RUN: ld.lld -o %t7 --script %t7.script --whole-archive \
+# RUN: %t.testdir1/lib1.a %t.testdir2/lib2.a
+# RUN: llvm-objdump -s %t7 | FileCheck --check-prefix=SECONDFIRST %s
+
+# Verify matching directories.
+# RUN: echo "SECTIONS { .foo : { \
+# RUN: *testdir2*(.foo) \
+# RUN: *testdir1*(.foo) } }" > %t8.script
+# RUN: ld.lld -o %t8 --script %t8.script --whole-archive \
+# RUN: %t.testdir1/lib1.a %t.testdir2/lib2.a
+# RUN: llvm-objdump -s %t8 | FileCheck --check-prefix=SECONDFIRST %s
+
+# Verify matching of archive library names in KEEP.
+# RUN: echo "SECTIONS { .foo : { \
+# RUN: KEEP(*lib2*(.foo)) \
+# RUN: KEEP(*lib1*(.foo)) } }" > %t9.script
+# RUN: ld.lld -o %t9 --script %t9.script --whole-archive \
+# RUN: %t.testdir1/lib1.a %t.testdir2/lib2.a
+# RUN: llvm-objdump -s %t9 | FileCheck --check-prefix=SECONDFIRST %s
+
+# Verify matching directories in KEEP.
+# RUN: echo "SECTIONS { .foo : { \
+# RUN: KEEP(*testdir2*(.foo)) \
+# RUN: KEEP(*testdir1*(.foo)) } }" > %t10.script
+# RUN: ld.lld -o %t10 --script %t10.script --whole-archive \
+# RUN: %t.testdir1/lib1.a %t.testdir2/lib2.a
+# RUN: llvm-objdump -s %t10 | FileCheck --check-prefix=SECONDFIRST %s
+
.global _start
_start:
nop
diff --git a/test/ELF/linkerscript/got-write-offset.s b/test/ELF/linkerscript/got-write-offset.s
new file mode 100644
index 000000000..323da7b4a
--- /dev/null
+++ b/test/ELF/linkerscript/got-write-offset.s
@@ -0,0 +1,23 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux-gnu %s -o %t
+# RUN: echo "SECTIONS { \
+# RUN: .data 0x1000 : { *(.data) } \
+# RUN: .got 0x2000 : { \
+# RUN: LONG(0) \
+# RUN: *(.got) \
+# RUN: } \
+# RUN: };" > %t.script
+# RUN: ld.lld -shared -o %t.out --script %t.script %t
+# RUN: llvm-objdump -s %t.out | FileCheck %s
+.text
+.global foo
+foo:
+ movl bar@GOT, %eax
+.data
+.local bar
+bar:
+ .zero 4
+# CHECK: Contents of section .data:
+# CHECK-NEXT: 1000 00000000
+# CHECK: Contents of section .got:
+# CHECK-NEXT: 2000 00000000 00100000
diff --git a/test/ELF/linkerscript/header-addr.s b/test/ELF/linkerscript/header-addr.s
index f37d319aa..70e6f674b 100644
--- a/test/ELF/linkerscript/header-addr.s
+++ b/test/ELF/linkerscript/header-addr.s
@@ -2,20 +2,20 @@
# 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: . = 0x2000 + SIZEOF_HEADERS; \
# RUN: .text : {*(.text)} :all \
# RUN: }" > %t.script
-# RUN: ld.lld -o %t.so --script %t.script %t.o -shared
+# RUN: ld.lld --hash-style=sysv -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: VirtualAddress: 0x2040
+# CHECK-NEXT: PhysicalAddress: 0x2040
+# CHECK-NEXT: FileSize: 200
+# CHECK-NEXT: MemSize: 200
# CHECK-NEXT: Flags [
# CHECK-NEXT: PF_R (0x4)
# CHECK-NEXT: PF_W (0x2)
@@ -25,7 +25,7 @@
# CHECK-NEXT: }
# CHECK-NEXT: ]
-# RUN: ld.lld -o %t2.so --script %t.script %t.o -shared -z max-page-size=0x2000
+# RUN: ld.lld --hash-style=sysv -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
@@ -33,10 +33,10 @@
# 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: VirtualAddress: 0x2040
+# MAXPAGE-NEXT: PhysicalAddress: 0x2040
+# MAXPAGE-NEXT: FileSize: 200
+# MAXPAGE-NEXT: MemSize: 200
# MAXPAGE-NEXT: Flags [
# MAXPAGE-NEXT: PF_R
# MAXPAGE-NEXT: PF_W
diff --git a/test/ELF/linkerscript/header-phdr.s b/test/ELF/linkerscript/header-phdr.s
new file mode 100644
index 000000000..8c9097d8d
--- /dev/null
+++ b/test/ELF/linkerscript/header-phdr.s
@@ -0,0 +1,13 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "PHDRS { foobar PT_LOAD FILEHDR PHDRS; } \
+# RUN: SECTIONS { . = 0x1000; .abc : { *(.zed) } : foobar }" > %t.script
+# RUN: ld.lld --script %t.script %t.o -o %t
+# RUN: llvm-readelf -l -S -W %t | FileCheck %s
+
+.section .zed, "a"
+.zero 4
+
+
+# CHECK: [ 1] .abc PROGBITS 0000000000001000 001000 000004 00 A 0 0 1
+# CHECK: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x001004 0x001004 R E 0x1000
diff --git a/test/ELF/linkerscript/image-base.s b/test/ELF/linkerscript/image-base.s
new file mode 100644
index 000000000..34ed4efda
--- /dev/null
+++ b/test/ELF/linkerscript/image-base.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { mysym = .; }" > %t.script
+
+# RUN: ld.lld %t.o -o %t-default.elf -T %t.script
+# RUN: llvm-readobj --symbols %t-default.elf | FileCheck %s --check-prefix=DEFAULT
+# DEFAULT: Name: mysym
+# DEFAULT-NEXT: Value: 0x0
+
+# RUN: ld.lld %t.o -o %t-switch.elf -T %t.script --image-base=0x100000
+# RUN: llvm-readobj --symbols %t-switch.elf | FileCheck %s --check-prefix=SWITCH
+# SWITCH: Name: mysym
+# SWITCH-NEXT: Value: 0x100000
+
+.global _start
+_start:
+ nop
diff --git a/test/ELF/linkerscript/implicit-program-header.s b/test/ELF/linkerscript/implicit-program-header.s
index 9598a6abe..95cdf142f 100644
--- a/test/ELF/linkerscript/implicit-program-header.s
+++ b/test/ELF/linkerscript/implicit-program-header.s
@@ -1,6 +1,6 @@
# 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: ld.lld --hash-style=sysv -o %t1 --script %S/Inputs/implicit-program-header.script \
# RUN: %t.o -shared
# RUN: llvm-readobj -elf-output-style=GNU -l %t1 | FileCheck %s
diff --git a/test/ELF/linkerscript/include-cycle.s b/test/ELF/linkerscript/include-cycle.s
new file mode 100644
index 000000000..e93ed904f
--- /dev/null
+++ b/test/ELF/linkerscript/include-cycle.s
@@ -0,0 +1,15 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+
+# RUN: echo "INCLUDE \"%t1.script\"" > %t1.script
+# RUN: not ld.lld %t.o %t1.script 2>&1 | FileCheck %s
+
+# RUN: echo "INCLUDE \"%t2.script\"" > %t1.script
+# RUN: echo "INCLUDE \"%t1.script\"" > %t2.script
+# RUN: not ld.lld %t.o %t1.script 2>&1 | FileCheck %s
+
+# CHECK: there is a cycle in linker script INCLUDEs
+
+.globl _start
+_start:
+ ret
diff --git a/test/ELF/linkerscript/linkerscript.s b/test/ELF/linkerscript/linkerscript.s
index cac902af4..7c8a5cbed 100644
--- a/test/ELF/linkerscript/linkerscript.s
+++ b/test/ELF/linkerscript/linkerscript.s
@@ -38,7 +38,7 @@
# 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"
+# INCLUDE_ERR-NEXT: INCLUDE "foo.script"
# RUN: ld.lld -L %T %t.script %t
# RUN: echo "FOO(BAR)" > %t.script
diff --git a/test/ELF/linkerscript/locationcountererr2.s b/test/ELF/linkerscript/locationcountererr2.s
index 54ee4a34d..8968f6740 100644
--- a/test/ELF/linkerscript/locationcountererr2.s
+++ b/test/ELF/linkerscript/locationcountererr2.s
@@ -2,8 +2,10 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
# RUN: echo "SECTIONS {" > %t.script
# RUN: echo ". = 0x20; . = 0x10; .text : {} }" >> %t.script
-# RUN: not ld.lld %t.o --script %t.script -o %t -shared 2>&1 | FileCheck %s
-# CHECK: {{.*}}.script:2: unable to move location counter backward
+# RUN: ld.lld %t.o --script %t.script -o %t -shared
+# RUN: llvm-objdump -section-headers %t | FileCheck %s
+# CHECK: Idx Name Size Address
+# CHECK: 1 .text 00000000 0000000000000010
# RUN: echo "SECTIONS { . = 0x20; . = ASSERT(0x1, "foo"); }" > %t2.script
# RUN: ld.lld %t.o --script %t2.script -o %t -shared
diff --git a/test/ELF/linkerscript/memory-at.s b/test/ELF/linkerscript/memory-at.s
new file mode 100644
index 000000000..9e56dbdbd
--- /dev/null
+++ b/test/ELF/linkerscript/memory-at.s
@@ -0,0 +1,46 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "MEMORY { \
+# RUN: FLASH (rx) : ORIGIN = 0x1000, LENGTH = 0x100 \
+# RUN: RAM (rwx) : ORIGIN = 0x2000, LENGTH = 0x100 } \
+# RUN: SECTIONS { \
+# RUN: .text : { *(.text*) } > FLASH \
+# RUN: __etext = .; \
+# RUN: .data : AT (__etext) { *(.data*) } > RAM \
+# RUN: }" > %t.script
+# RUN: ld.lld %t --script %t.script -o %t2
+# RUN: llvm-readobj -program-headers %t2 | FileCheck %s
+
+# CHECK: ProgramHeaders [
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x1000
+# CHECK-NEXT: VirtualAddress: 0x1000
+# CHECK-NEXT: PhysicalAddress: 0x1000
+# CHECK-NEXT: FileSize: 8
+# CHECK-NEXT: MemSize: 8
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: PF_X
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment:
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x2000
+# CHECK-NEXT: VirtualAddress: 0x2000
+# CHECK-NEXT: PhysicalAddress: 0x1008
+# CHECK-NEXT: FileSize: 8
+# CHECK-NEXT: MemSize: 8
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: PF_W
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment:
+# CHECK-NEXT: }
+
+.section .text, "ax"
+.quad 0
+
+.section .data, "aw"
+.quad 0
diff --git a/test/ELF/linkerscript/memory-err.s b/test/ELF/linkerscript/memory-err.s
new file mode 100644
index 000000000..19517687b
--- /dev/null
+++ b/test/ELF/linkerscript/memory-err.s
@@ -0,0 +1,16 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "MEMORY { name : ORIGIN = DATA_SEGMENT_RELRO_END; }" > %t.script
+# RUN: not ld.lld -shared -o %t2 --script %t.script %t 2>&1 | FileCheck %s
+# CHECK: error: {{.*}}.script:1: unable to calculate page size
+
+# RUN: echo "MEMORY { name : ORIGIN = CONSTANT(COMMONPAGESIZE); }" > %t.script
+# RUN: not ld.lld -shared -o %t2 --script %t.script %t 2>&1 |\
+# RUN: FileCheck %s --check-prefix=ERR2
+# ERR2: error: {{.*}}.script:1: unable to calculate page size
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "MEMORY { name : ORIGIN = .; }" > %t.script
+# RUN: not ld.lld -shared -o %t2 --script %t.script %t 2>&1 |\
+# RUN: FileCheck %s --check-prefix=ERR3
+# ERR3: error: {{.*}}.script:1: unable to get location counter value
diff --git a/test/ELF/linkerscript/memory.s b/test/ELF/linkerscript/memory.s
index 774a6f92a..172768394 100644
--- a/test/ELF/linkerscript/memory.s
+++ b/test/ELF/linkerscript/memory.s
@@ -21,8 +21,8 @@
# RUN: rom (rx) : org = (0x80 * 0x1000 * 0x1000), len = 64M \
# RUN: } \
# RUN: SECTIONS { \
-# RUN: .text : { *(.text) } > rom \
-# RUN: .data : { *(.data) } > ram \
+# RUN: .text : { *(.text) } >rom \
+# RUN: .data : { *(.data) } >ram \
# RUN: }" > %t.script
# RUN: ld.lld -o %t1 --script %t.script %t
# RUN: llvm-objdump -section-headers %t1 | FileCheck -check-prefix=RAMROM %s
diff --git a/test/ELF/linkerscript/memory2.s b/test/ELF/linkerscript/memory2.s
new file mode 100644
index 000000000..2e7381fb8
--- /dev/null
+++ b/test/ELF/linkerscript/memory2.s
@@ -0,0 +1,14 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "MEMORY { ram (rwx) : ORIGIN = 0, LENGTH = 2K } \
+# RUN: SECTIONS { .text : { *(.text*) } > ram }" > %t.script
+# RUN: ld.lld -o %t2 --script %t.script %t
+
+.text
+.global _start
+_start:
+ .zero 1024
+
+.section .text.foo,"ax",%progbits
+foo:
+ nop
diff --git a/test/ELF/linkerscript/no-space.s b/test/ELF/linkerscript/no-space.s
index fc9e5b133..21a38e42b 100644
--- a/test/ELF/linkerscript/no-space.s
+++ b/test/ELF/linkerscript/no-space.s
@@ -2,11 +2,11 @@
# 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: ld.lld --hash-style=sysv -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: ld.lld --hash-style=sysv -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
diff --git a/test/ELF/linkerscript/non-alloc-segment.s b/test/ELF/linkerscript/non-alloc-segment.s
new file mode 100644
index 000000000..229f028a1
--- /dev/null
+++ b/test/ELF/linkerscript/non-alloc-segment.s
@@ -0,0 +1,44 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+
+################################################################################
+## Test that non-alloc section .foo can be assigned to a segment. Check that
+## the values of the offset and file size of this segment's PHDR are correct.
+##
+## This functionality allows non-alloc metadata, which is not required at
+## run-time, to be added to a custom segment in a file. This metadata may be
+## read/edited by tools/loader using the values of the offset and file size from
+## the custom segment's PHDR. This is particularly important if section headers
+## have been stripped.
+# RUN: echo "PHDRS {text PT_LOAD; foo 0x12345678;} \
+# RUN: SECTIONS { \
+# RUN: .text : {*(.text .text*)} :text \
+# RUN: .foo : {*(.foo)} :foo \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o
+# RUN: llvm-readobj -elf-output-style=GNU -s -l %t | FileCheck %s
+# RUN: llvm-readobj -l %t | FileCheck --check-prefix=PHDR %s
+
+# CHECK: Program Headers:
+# CHECK-NEXT: Type
+# CHECK-NEXT: LOAD
+# CHECK-NEXT: <unknown>: 0x12345678
+
+# CHECK: Section to Segment mapping:
+# CHECK-NEXT: Segment Sections...
+# CHECK-NEXT: 00 .text
+# CHECK-NEXT: 01 .foo
+
+# PHDR: Type: (0x12345678)
+# PHDR-NEXT: Offset: 0x1004
+# PHDR-NEXT: VirtualAddress
+# PHDR-NEXT: PhysicalAddress
+# PHDR-NEXT: FileSize: 4
+
+.global _start
+_start:
+ nop
+
+.section .foo
+ .align 4
+ .long 0
diff --git a/test/ELF/linkerscript/non-alloc.s b/test/ELF/linkerscript/non-alloc.s
index 861c74996..3257cb965 100644
--- a/test/ELF/linkerscript/non-alloc.s
+++ b/test/ELF/linkerscript/non-alloc.s
@@ -2,7 +2,7 @@
# 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: ld.lld --hash-style=sysv -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
diff --git a/test/ELF/linkerscript/operators.s b/test/ELF/linkerscript/operators.s
index 470558d29..868805b34 100644
--- a/test/ELF/linkerscript/operators.s
+++ b/test/ELF/linkerscript/operators.s
@@ -25,6 +25,7 @@
# RUN: commonpagesize = CONSTANT (COMMONPAGESIZE); \
# RUN: . = 0xfff0; \
# RUN: datasegmentalign = DATA_SEGMENT_ALIGN (0xffff, 0); \
+# RUN: datasegmentalign2 = DATA_SEGMENT_ALIGN (0, 0); \
# RUN: }" > %t.script
# RUN: ld.lld %t --script %t.script -o %t2
# RUN: llvm-objdump -t %t2 | FileCheck %s
@@ -51,6 +52,7 @@
# CHECK: 00000000001000 *ABS* 00000000 maxpagesize
# CHECK: 00000000001000 *ABS* 00000000 commonpagesize
# CHECK: 0000000000ffff *ABS* 00000000 datasegmentalign
+# CHECK: 0000000000fff0 *ABS* 00000000 datasegmentalign2
## Mailformed number error.
# RUN: echo "SECTIONS { . = 0x12Q41; }" > %t.script
diff --git a/test/ELF/linkerscript/orphan-end.s b/test/ELF/linkerscript/orphan-end.s
new file mode 100644
index 000000000..5f56d8f48
--- /dev/null
+++ b/test/ELF/linkerscript/orphan-end.s
@@ -0,0 +1,57 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+
+# Test that .orphan_rx is placed after __stack_end. This matches bfd's
+# behavior when the orphan section is the last one.
+
+# RUN: echo "SECTIONS { \
+# RUN: __start_text = .; \
+# RUN: .text : { *(.text*) } \
+# RUN: __end_text = .; \
+# RUN: __stack_start = .; \
+# RUN: . = . + 0x1000; \
+# RUN: __stack_end = .; \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o
+# RUN: llvm-readelf -S --symbols %t | FileCheck %s
+
+# CHECK-DAG: .text PROGBITS 0000000000000000
+# CHECK-DAG: .orphan_rx PROGBITS 0000000000001004
+
+# CHECK-DAG: 0000000000000000 {{.*}} __start_text
+# CHECK-DAG: 0000000000000004 {{.*}} __end_text
+# CHECK-DAG: 0000000000000004 {{.*}} __stack_start
+# CHECK-DAG: 0000000000001004 {{.*}} __stack_end
+
+# Test that .orphan_rx is now placed before __stack_end. This matches bfd's
+# behavior when the orphan section is not the last one.
+
+# RUN: echo "SECTIONS { \
+# RUN: __start_text = .; \
+# RUN: .text : { *(.text*) } \
+# RUN: __end_text = .; \
+# RUN: __stack_start = .; \
+# RUN: . = . + 0x1000; \
+# RUN: __stack_end = .; \
+# RUN: .orphan_rw : { *(.orphan_rw*) } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o
+# RUN: llvm-readelf -S --symbols %t | FileCheck --check-prefix=MIDDLE %s
+
+# MIDDLE-DAG: .text PROGBITS 0000000000000000
+# MIDDLE-DAG: .orphan_rx PROGBITS 0000000000000004
+
+# MIDDLE-DAG: 0000000000000000 {{.*}} __start_text
+# MIDDLE-DAG: 0000000000000004 {{.*}} __end_text
+# MIDDLE-DAG: 0000000000000004 {{.*}} __stack_start
+# MIDDLE-DAG: 0000000000001008 {{.*}} __stack_end
+
+ .global _start
+_start:
+ .zero 4
+
+ .section .orphan_rx,"ax"
+ .zero 4
+
+ .section .orphan_rw,"aw"
+ .zero 4
diff --git a/test/ELF/linkerscript/orphan-phdrs.s b/test/ELF/linkerscript/orphan-phdrs.s
new file mode 100644
index 000000000..648911162
--- /dev/null
+++ b/test/ELF/linkerscript/orphan-phdrs.s
@@ -0,0 +1,34 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: echo "PHDRS { \
+# RUN: exec PT_LOAD FLAGS(0x4 | 0x1); \
+# RUN: rw PT_LOAD FLAGS(0x4 | 0x2); \
+# RUN: } \
+# RUN: SECTIONS { \
+# RUN: .text : { *(.text) } :exec \
+# RUN: .empty : { *(.empty) } :rw \
+# RUN: .rw : { *(.rw) } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o
+# RUN: llvm-readobj -elf-output-style=GNU -s -l %t | FileCheck %s
+
+## Check that the orphan section is placed correctly and belongs to
+## the correct segment.
+
+# CHECK: Section Headers
+# CHECK: .text
+# CHECK-NEXT: .orphan
+# CHECK-NEXT: .rw
+
+# CHECK: Segment Sections
+# CHECK-NEXT: .text .orphan
+# CHECK-NEXT: .rw
+
+.section .text, "ax"
+ ret
+
+.section .rw, "aw"
+ .quad 0
+
+.section .orphan, "ax"
+ ret
diff --git a/test/ELF/linkerscript/orphan-report.s b/test/ELF/linkerscript/orphan-report.s
new file mode 100644
index 000000000..a5a179d50
--- /dev/null
+++ b/test/ELF/linkerscript/orphan-report.s
@@ -0,0 +1,34 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS { .text : { *(.text.1) } }" > %t.script
+# RUN: ld.lld --hash-style=sysv -shared -o %t.out --script %t.script %t.o --verbose | FileCheck %s
+
+# CHECK: {{.*}}.o:(.text) is being placed in '.text'
+# CHECK-NEXT: {{.*}}.o:(.text.2) is being placed in '.text'
+# CHECK-NEXT: <internal>:(.comment) is being placed in '.comment'
+# CHECK-NEXT: <internal>:(.bss) is being placed in '.bss'
+# CHECK-NEXT: <internal>:(.bss.rel.ro) is being placed in '.bss.rel.ro'
+# CHECK-NEXT: <internal>:(.dynsym) is being placed in '.dynsym'
+# CHECK-NEXT: <internal>:(.gnu.version) is being placed in '.gnu.version'
+# CHECK-NEXT: <internal>:(.gnu.version_r) is being placed in '.gnu.version_r'
+# CHECK-NEXT: <internal>:(.hash) is being placed in '.hash'
+# CHECK-NEXT: <internal>:(.dynamic) is being placed in '.dynamic'
+# CHECK-NEXT: <internal>:(.dynstr) is being placed in '.dynstr'
+# CHECK-NEXT: <internal>:(.rela.dyn) is being placed in '.rela.dyn'
+# CHECK-NEXT: <internal>:(.got) is being placed in '.got'
+# CHECK-NEXT: <internal>:(.got.plt) is being placed in '.got.plt'
+# CHECK-NEXT: <internal>:(.got.plt) is being placed in '.got.plt'
+# CHECK-NEXT: <internal>:(.rela.plt) is being placed in '.rela.plt'
+# CHECK-NEXT: <internal>:(.rela.plt) is being placed in '.rela.plt'
+# CHECK-NEXT: <internal>:(.plt) is being placed in '.plt'
+# CHECK-NEXT: <internal>:(.plt) is being placed in '.plt'
+# CHECK-NEXT: <internal>:(.eh_frame) is being placed in '.eh_frame'
+# CHECK-NEXT: <internal>:(.symtab) is being placed in '.symtab'
+# CHECK-NEXT: <internal>:(.shstrtab) is being placed in '.shstrtab'
+# CHECK-NEXT: <internal>:(.strtab) is being placed in '.strtab'
+
+.section .text.1,"a"
+ nop
+
+.section .text.2,"a"
+ nop
diff --git a/test/ELF/linkerscript/out-of-order.s b/test/ELF/linkerscript/out-of-order.s
index 9c6547a68..c43df43e5 100644
--- a/test/ELF/linkerscript/out-of-order.s
+++ b/test/ELF/linkerscript/out-of-order.s
@@ -1,9 +1,18 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-linux %s -o %t.o
# RUN: echo "SECTIONS { .data 0x4000 : { *(.data) } .text 0x2000 : { *(.text) } }" > %t.script
-# RUN: not ld.lld -o %t.so --script %t.script %t.o -shared 2>&1 | FileCheck %s
+# RUN: ld.lld --hash-style=sysv -o %t.so --script %t.script %t.o -shared
+# RUN: llvm-objdump -section-headers %t.so | FileCheck %s
-# CHECK: error: {{.*}}.script:1: unable to move location counter backward
+# CHECK: Sections:
+# CHECK-NEXT: Idx Name Size Address Type
+# CHECK-NEXT: 0 00000000 0000000000000000
+# CHECK-NEXT: 1 .data 00000008 0000000000004000 DATA
+# CHECK-NEXT: 2 .dynamic 00000060 0000000000004008
+# CHECK-NEXT: 3 .text 00000008 0000000000002000 TEXT DATA
+# CHECK-NEXT: 4 .dynsym 00000018 0000000000002008
+# CHECK-NEXT: 5 .hash 00000010 0000000000002020
+# CHECK-NEXT: 6 .dynstr 00000001 0000000000002030
.quad 0
.data
diff --git a/test/ELF/linkerscript/output-too-large.s b/test/ELF/linkerscript/output-too-large.s
index db021eaa9..c892a88a9 100644
--- a/test/ELF/linkerscript/output-too-large.s
+++ b/test/ELF/linkerscript/output-too-large.s
@@ -1,3 +1,4 @@
+# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %s -o %t.o
# RUN: echo "SECTIONS { .text : { . = 0xffffffff; *(.text*); } }" > %t.script
# RUN: not ld.lld --script %t.script %t.o -o %t 2>&1 | FileCheck %s
diff --git a/test/ELF/linkerscript/phdr-check.s b/test/ELF/linkerscript/phdr-check.s
index c7229ed33..b35256be6 100644
--- a/test/ELF/linkerscript/phdr-check.s
+++ b/test/ELF/linkerscript/phdr-check.s
@@ -1,14 +1,14 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: echo "SECTIONS { . = 0x10000000; .text : {*(.text.*)} }" > %t.script
+# RUN: echo "SECTIONS { . = 0x10000000 + SIZEOF_HEADERS; .text : {*(.text.*)} }" > %t.script
# RUN: ld.lld -o %t1 --script %t.script %t
# RUN: llvm-readobj -program-headers %t1 | FileCheck %s
# CHECK: ProgramHeaders [
# CHECK-NEXT: ProgramHeader {
# CHECK-NEXT: Type: PT_PHDR (0x6)
# CHECK-NEXT: Offset: 0x40
-# CHECK-NEXT: VirtualAddress: 0xFFFF040
+# CHECK-NEXT: VirtualAddress: 0x10000040
.global _start
_start:
diff --git a/test/ELF/linkerscript/region-alias.s b/test/ELF/linkerscript/region-alias.s
new file mode 100644
index 000000000..8a88f6f5a
--- /dev/null
+++ b/test/ELF/linkerscript/region-alias.s
@@ -0,0 +1,54 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "MEMORY { \
+# RUN: ROM (rwx): ORIGIN = 0x1000, LENGTH = 0x100 \
+# RUN: RAM (rwx): ORIGIN = 0x2000, LENGTH = 0x100 \
+# RUN: } \
+# RUN: INCLUDE \"%t.script.inc\" \
+# RUN: SECTIONS { \
+# RUN: .text : { *(.text*) } > ALIAS_TEXT \
+# RUN: .data : { *(.data*) } > ALIAS_DATA \
+# RUN: }" > %t.script
+
+## .text to ROM, .data to RAM.
+# RUN: echo "REGION_ALIAS (\"ALIAS_TEXT\", ROM);" > %t.script.inc
+# RUN: echo "REGION_ALIAS (\"ALIAS_DATA\", RAM);" >> %t.script.inc
+# RUN: ld.lld %t --script %t.script -o %t2
+# RUN: llvm-objdump -section-headers %t2 | FileCheck %s
+# CHECK: .text 00000001 0000000000001000 TEXT DATA
+# CHECK: .data 00000008 0000000000002000 DATA
+
+## All to ROM.
+# RUN: echo "REGION_ALIAS (\"ALIAS_TEXT\", ROM);" > %t.script.inc
+# RUN: echo "REGION_ALIAS (\"ALIAS_DATA\", ROM);" >> %t.script.inc
+# RUN: ld.lld %t --script %t.script -o %t2
+# RUN: llvm-objdump -section-headers %t2 | FileCheck %s --check-prefix=RAM
+# RAM: .text 00000001 0000000000001000 TEXT DATA
+# RAM: .data 00000008 0000000000001001 DATA
+
+## Redefinition of region.
+# RUN: echo "REGION_ALIAS (\"ROM\", ROM);" > %t.script.inc
+# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \
+# RUN: FileCheck %s --check-prefix=ERR1
+# ERR1: {{.*}}script.inc:1: redefinition of memory region 'ROM'
+
+## Redefinition of alias.
+# RUN: echo "REGION_ALIAS (\"ALIAS_TEXT\", ROM);" > %t.script.inc
+# RUN: echo "REGION_ALIAS (\"ALIAS_TEXT\", ROM);" >> %t.script.inc
+# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \
+# RUN: FileCheck %s --check-prefix=ERR2
+# ERR2: {{.*}}script.inc:2: redefinition of memory region 'ALIAS_TEXT'
+
+## Attemp to create an alias for undefined region.
+# RUN: echo "REGION_ALIAS (\"ALIAS_TEXT\", FOO);" > %t.script.inc
+# RUN: not ld.lld %t --script %t.script -o %t2 2>&1 | \
+# RUN: FileCheck %s --check-prefix=ERR3
+# ERR3: {{.*}}script.inc:1: memory region 'FOO' is not defined
+
+.text
+.global _start
+_start:
+ nop
+
+.section .data,"aw"
+.quad 0
diff --git a/test/ELF/linkerscript/repsection-symbol.s b/test/ELF/linkerscript/repsection-symbol.s
index d2d8c9dd5..195380be9 100644
--- a/test/ELF/linkerscript/repsection-symbol.s
+++ b/test/ELF/linkerscript/repsection-symbol.s
@@ -6,7 +6,7 @@
# RUN: .text : { *(.text) } \
# RUN: .foo : {foo1 = .; *(.foo.*) foo2 = .; *(.bar) foo3 = .;} \
# RUN: }" > %t.script
-# RUN: ld.lld -o %t1 --script %t.script %t -shared
+# RUN: ld.lld --hash-style=sysv -o %t1 --script %t.script %t -shared
# RUN: llvm-readobj -t %t1 | FileCheck %s
# CHECK: Name: foo1
diff --git a/test/ELF/linkerscript/sections-sort.s b/test/ELF/linkerscript/sections-sort.s
index 0e9985191..99bbbead9 100644
--- a/test/ELF/linkerscript/sections-sort.s
+++ b/test/ELF/linkerscript/sections-sort.s
@@ -2,7 +2,7 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
# RUN: echo "SECTIONS { .text : {*(.text)} foo : {*(foo)}}" > %t.script
-# RUN: ld.lld -o %t --script %t.script %t.o -shared
+# RUN: ld.lld --hash-style=sysv -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
diff --git a/test/ELF/linkerscript/segment-headers.s b/test/ELF/linkerscript/segment-headers.s
new file mode 100644
index 000000000..b3bdad9ea
--- /dev/null
+++ b/test/ELF/linkerscript/segment-headers.s
@@ -0,0 +1,26 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS { . = 0x2000; .text : AT(0x100000) { *(.text) } }" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o
+# RUN: llvm-readobj -l %t | FileCheck %s
+
+# CHECK: ProgramHeaders [
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD (0x1)
+# CHECK-NEXT: Offset: 0x1000
+# CHECK-NEXT: VirtualAddress: 0x2000
+# CHECK-NEXT: PhysicalAddress: 0x100000
+# CHECK-NEXT: FileSize: 1
+# CHECK-NEXT: MemSize: 1
+# CHECK-NEXT: Flags [ (0x5)
+# CHECK-NEXT: PF_R (0x4)
+# CHECK-NEXT: PF_X (0x1)
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment: 4096
+# CHECK-NEXT: }
+
+.section .text
+.global _start
+
+_start:
+ ret
diff --git a/test/ELF/linkerscript/segment-none.s b/test/ELF/linkerscript/segment-none.s
new file mode 100644
index 000000000..d54e835a0
--- /dev/null
+++ b/test/ELF/linkerscript/segment-none.s
@@ -0,0 +1,39 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+
+## Test that section .foo is not placed in any segment when assigned to segment
+## NONE in the linker script and segment NONE is not defined.
+# RUN: echo "PHDRS {text PT_LOAD;} \
+# RUN: SECTIONS { \
+# RUN: .text : {*(.text .text*)} :text \
+# RUN: .foo : {*(.foo)} :NONE \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o
+# RUN: llvm-readobj -elf-output-style=GNU -s -l %t | FileCheck %s
+
+## Test that section .foo is placed in segment NONE when assigned to segment
+## NONE in the linker script and segment NONE is defined.
+# RUN: echo "PHDRS {text PT_LOAD; NONE PT_LOAD;} \
+# RUN: SECTIONS { \
+# RUN: .text : {*(.text .text*)} :text \
+# RUN: .foo : {*(.foo)} :NONE \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o
+# RUN: llvm-readobj -elf-output-style=GNU -s -l %t | FileCheck --check-prefix=DEFINED %s
+
+# CHECK: Section to Segment mapping:
+# CHECK-NEXT: Segment Sections...
+# CHECK-NOT: .foo
+
+# DEFINED: Section to Segment mapping:
+# DEFINED-NEXT: Segment Sections...
+# DEFINED-NEXT: 00 .text
+# DEFINED-NEXT: 01 .foo
+
+.global _start
+_start:
+ nop
+
+.section .foo,"a"
+foo:
+ .long 0
diff --git a/test/ELF/linkerscript/segment-start.s b/test/ELF/linkerscript/segment-start.s
index e46c398f6..69897d604 100644
--- a/test/ELF/linkerscript/segment-start.s
+++ b/test/ELF/linkerscript/segment-start.s
@@ -1,6 +1,6 @@
// 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: ld.lld --hash-style=sysv %t.o %S/Inputs/segment-start.script -shared -o %t.so
// RUN: llvm-readobj --dyn-symbols %t.so | FileCheck %s
// CHECK: Name: foobar1
diff --git a/test/ELF/linkerscript/sort-non-script.s b/test/ELF/linkerscript/sort-non-script.s
index b0517608d..4611b18d5 100644
--- a/test/ELF/linkerscript/sort-non-script.s
+++ b/test/ELF/linkerscript/sort-non-script.s
@@ -2,7 +2,7 @@
# 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: ld.lld --hash-style=sysv -o %t1 --script %t.script %t -shared
# RUN: llvm-readobj -elf-output-style=GNU -s %t1 | FileCheck %s
# CHECK: .text {{.*}} AX
diff --git a/test/ELF/linkerscript/symbol-assignexpr.s b/test/ELF/linkerscript/symbol-assignexpr.s
index 14a1f0890..9ab03a173 100644
--- a/test/ELF/linkerscript/symbol-assignexpr.s
+++ b/test/ELF/linkerscript/symbol-assignexpr.s
@@ -6,7 +6,7 @@
# RUN: symbol2 = symbol + 0x1234; \
# RUN: symbol3 = symbol2; \
# RUN: symbol4 = symbol + -4; \
-# RUN: symbol5 = symbol - ~ 0xfffb; \
+# RUN: symbol5 = symbol - ~0xfffb; \
# RUN: symbol6 = symbol - ~(0xfff0 + 0xb); \
# RUN: symbol7 = symbol - ~ 0xfffb + 4; \
# RUN: symbol8 = ~ 0xffff + 4; \
@@ -15,6 +15,9 @@
# RUN: symbol11 = ((0x28000 + 0x1fff) & ~(0x1000 + -1)); \
# RUN: symbol12 = 0x1234; \
# RUN: symbol12 += 1; \
+# RUN: symbol13 = !1; \
+# RUN: symbol14 = !0; \
+# RUN: symbol15 = 0!=1; \
# RUN: bar = 0x5678; \
# RUN: baz = 0x9abc; \
# RUN: }" > %t.script
@@ -39,6 +42,9 @@
# CHECK-NEXT: fedcba9876543210 *ABS* 00000000 symbol10
# CHECK-NEXT: 0000000000029000 *ABS* 00000000 symbol11
# CHECK-NEXT: 0000000000001235 *ABS* 00000000 symbol12
+# CHECK-NEXT: 0000000000000000 *ABS* 00000000 symbol13
+# CHECK-NEXT: 0000000000000001 *ABS* 00000000 symbol14
+# CHECK-NEXT: 0000000000000001 *ABS* 00000000 symbol15
# RUN: echo "SECTIONS { symbol2 = symbol; }" > %t2.script
# RUN: not ld.lld -o %t2 --script %t2.script %t 2>&1 \
diff --git a/test/ELF/linkerscript/symbol-only-flags.s b/test/ELF/linkerscript/symbol-only-flags.s
new file mode 100644
index 000000000..5ad7befee
--- /dev/null
+++ b/test/ELF/linkerscript/symbol-only-flags.s
@@ -0,0 +1,20 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS { . = SIZEOF_HEADERS; \
+# RUN: .tbss : { *(.tbss) } \
+# RUN: .foo : { bar = .; } }" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o
+# RUN: llvm-readobj -s %t | FileCheck %s
+
+## Check .foo does not get SHF_TLS flag.
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .foo
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+
+.section .tbss,"awT",@nobits
+.quad 0
diff --git a/test/ELF/linkerscript/symbol-ordering-file.s b/test/ELF/linkerscript/symbol-ordering-file.s
new file mode 100644
index 000000000..ad6916cdf
--- /dev/null
+++ b/test/ELF/linkerscript/symbol-ordering-file.s
@@ -0,0 +1,23 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { .foo : { *(.foo) } }" > %t.script
+
+# RUN: ld.lld %t.o --script %t.script -o %t.out
+# RUN: llvm-objdump -s %t.out| FileCheck %s --check-prefix=BEFORE
+# BEFORE: Contents of section .foo:
+# BEFORE-NEXT: 1122
+
+# RUN: echo "_foo2" > %t.ord
+# RUN: echo "_foo1" >> %t.ord
+# RUN: ld.lld --symbol-ordering-file %t.ord %t.o --script %t.script -o %t2.out
+# RUN: llvm-objdump -s %t2.out| FileCheck %s --check-prefix=AFTER
+# AFTER: Contents of section .foo:
+# AFTER-NEXT: 2211
+
+.section .foo,"ax",@progbits,unique,1
+_foo1:
+ .byte 0x11
+
+.section .foo,"ax",@progbits,unique,2
+_foo2:
+ .byte 0x22
diff --git a/test/ELF/linkerscript/symbol-reserved.s b/test/ELF/linkerscript/symbol-reserved.s
index e0b259597..8ae9d0cd6 100644
--- a/test/ELF/linkerscript/symbol-reserved.s
+++ b/test/ELF/linkerscript/symbol-reserved.s
@@ -17,6 +17,34 @@
# ALIGNED: 0000000000200005 .text 00000000 .hidden newsym
+# RUN: echo "PROVIDE_HIDDEN(newsym = ALIGN(3, 8) + 10);" > %t.script
+# RUN: ld.lld -o %t1 %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=ALIGN-ADD %s
+# ALIGN-ADD: 0000000000000012 *ABS* 00000000 .hidden newsym
+
+# RUN: echo "PROVIDE_HIDDEN(newsym = ALIGN(11, 8) - 10);" > %t.script
+# RUN: ld.lld -o %t1 %t.script %t
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=ALIGN-SUB %s
+# ALIGN-SUB: 0000000000000006 *ABS* 00000000 .hidden newsym
+
+# RUN: echo "PROVIDE_HIDDEN(newsym = ALIGN(_end, CONSTANT(MAXPAGESIZE)) + 5);" > %t.script
+# RUN: ld.lld -o %t1 %t %t.script
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=RELATIVE %s
+# RELATIVE: 0000000000202005 .text 00000000 .hidden newsym
+# RELATIVE: 0000000000201007 .text 00000000 _end
+
+# RUN: echo "PROVIDE_HIDDEN(newsym = ALIGN(_end, CONSTANT(MAXPAGESIZE)) + 5);" > %t.script
+# RUN: ld.lld -o %t1 --script %p/Inputs/symbol-reserved.script %t %t.script
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=RELATIVE-ADD %s
+# RELATIVE-ADD: 0000000000001005 .text 00000000 .hidden newsym
+# RELATIVE-ADD: 0000000000000007 .text 00000000 .hidden _end
+
+# RUN: echo "PROVIDE_HIDDEN(newsym = ALIGN(_end, CONSTANT(MAXPAGESIZE)) - 5);" > %t.script
+# RUN: ld.lld -o %t1 --script %p/Inputs/symbol-reserved.script %t %t.script
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=RELATIVE-SUB %s
+# RELATIVE-SUB: 0000000000000ffb .text 00000000 .hidden newsym
+# RELATIVE-SUB: 0000000000000007 .text 00000000 .hidden _end
+
.global _start
_start:
lea newsym(%rip),%rax
diff --git a/test/ELF/linkerscript/symbols.s b/test/ELF/linkerscript/symbols.s
index 465663517..a6d797584 100644
--- a/test/ELF/linkerscript/symbols.s
+++ b/test/ELF/linkerscript/symbols.s
@@ -12,14 +12,13 @@
# 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
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=DONTPROVIDE %s
+# DONTPROVIDE-NOT: 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
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=DONTPROVIDE %s
# Provide existing symbol. The value should be 0, even though we
# have value of 1 in PROVIDE()
@@ -44,14 +43,12 @@
# 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
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=DONTPROVIDE %s
# 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
+# RUN: llvm-objdump -t %t1 | FileCheck --check-prefix=DONTPROVIDE %s
# Provide existing symbol. The value should be 0, even though we
# have value of 1 in PROVIDE()
diff --git a/test/ELF/linkerscript/ttext-script.s b/test/ELF/linkerscript/ttext-script.s
new file mode 100644
index 000000000..9dcde9bb1
--- /dev/null
+++ b/test/ELF/linkerscript/ttext-script.s
@@ -0,0 +1,11 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { .text 0x200000 : { *(.text) } }" > %t.script
+# RUN: ld.lld -T %t.script -Ttext 0x100000 %t.o -o %t
+# RUN: llvm-readobj --elf-output-style=GNU -s %t | FileCheck %s
+
+# CHECK: .text PROGBITS 0000000000100000
+
+.global _start
+_start:
+nop
diff --git a/test/ELF/linkerscript/unused-synthetic.s b/test/ELF/linkerscript/unused-synthetic.s
new file mode 100644
index 000000000..c9295fff7
--- /dev/null
+++ b/test/ELF/linkerscript/unused-synthetic.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS { \
+# RUN: .got : { *(.got) } \
+# RUN: .plt : { *(.plt) } \
+# RUN: .text : { *(.text) } \
+# RUN: }" > %t.script
+# RUN: ld.lld -shared -o %t.so --script %t.script %t.o
+
+# RUN: llvm-objdump -section-headers %t.so | FileCheck %s
+# CHECK-NOT: .got
+# CHECK-NOT: .plt
+# CHECK: .text
+# CHECK-NEXT: .dynsym
+
+.global _start
+_start:
+ nop
diff --git a/test/ELF/lit.local.cfg b/test/ELF/lit.local.cfg
index b93a36d2b..284077d68 100644
--- a/test/ELF/lit.local.cfg
+++ b/test/ELF/lit.local.cfg
@@ -1,2 +1 @@
config.suffixes = ['.test', '.s', '.ll']
-
diff --git a/test/ELF/local-got-pie.s b/test/ELF/local-got-pie.s
index 417f9d002..b1b213af6 100644
--- a/test/ELF/local-got-pie.s
+++ b/test/ELF/local-got-pie.s
@@ -1,5 +1,5 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
-// RUN: ld.lld %t.o -o %t -pie
+// RUN: ld.lld --hash-style=sysv %t.o -o %t -pie
// RUN: llvm-readobj -s -r -d %t | FileCheck %s
// RUN: llvm-objdump -d %t | FileCheck --check-prefix=DISASM %s
diff --git a/test/ELF/local-got-shared.s b/test/ELF/local-got-shared.s
index e4fd46997..c858424cf 100644
--- a/test/ELF/local-got-shared.s
+++ b/test/ELF/local-got-shared.s
@@ -1,5 +1,5 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
-// RUN: ld.lld %t.o -o %t -shared
+// RUN: ld.lld --hash-style=sysv %t.o -o %t -shared
// RUN: llvm-readobj -s -r -d %t | FileCheck %s
// RUN: llvm-objdump -d %t | FileCheck --check-prefix=DISASM %s
diff --git a/test/ELF/local-got.s b/test/ELF/local-got.s
index 7e6ef9e0b..17517f6a7 100644
--- a/test/ELF/local-got.s
+++ b/test/ELF/local-got.s
@@ -1,7 +1,7 @@
// 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 %t.o %t2.so -o %t
+// RUN: ld.lld --hash-style=sysv %t.o %t2.so -o %t
// RUN: llvm-readobj -s -r -section-data %t | FileCheck %s
// RUN: llvm-objdump -d %t | FileCheck --check-prefix=DISASM %s
diff --git a/test/ELF/lto-plugin-ignore.s b/test/ELF/lto-plugin-ignore.s
new file mode 100644
index 000000000..2f45a43b2
--- /dev/null
+++ b/test/ELF/lto-plugin-ignore.s
@@ -0,0 +1,11 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: ld.lld %t -plugin-opt=/foo/bar -plugin-opt=-fresolution=zed \
+# RUN: -plugin-opt=-pass-through=-lgcc -plugin-opt=-function-sections \
+# RUN: -plugin-opt=-data-sections -o /dev/null
+
+# RUN: not ld.lld %t -plugin-opt=-data-sectionssss \
+# RUN: -plugin-opt=-function-sectionsss 2>&1 | FileCheck %s
+# CHECK: unknown option: -data-sectionsss
+# CHECK: unknown option: -function-sectionsss
diff --git a/test/ELF/lto/Inputs/data-ordering-lto.ll b/test/ELF/lto/Inputs/data-ordering-lto.ll
new file mode 100644
index 000000000..a95fa6d5a
--- /dev/null
+++ b/test/ELF/lto/Inputs/data-ordering-lto.ll
@@ -0,0 +1,6 @@
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-scei-ps4"
+
+@pat = global i32 33, align 4
+@tin = global i32 33, align 4
+@dipsy = global i32 33, align 4
diff --git a/test/ELF/lto/Inputs/linker-script-symbols-ipo.ll b/test/ELF/lto/Inputs/linker-script-symbols-ipo.ll
new file mode 100644
index 000000000..c872f9e1d
--- /dev/null
+++ b/test/ELF/lto/Inputs/linker-script-symbols-ipo.ll
@@ -0,0 +1,9 @@
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare i32 @bar()
+
+define i32 @_start() {
+ %1 = tail call i32 @bar()
+ ret i32 %1
+}
diff --git a/test/ELF/lto/Inputs/symbol-ordering-lto.ll b/test/ELF/lto/Inputs/symbol-ordering-lto.ll
new file mode 100644
index 000000000..164659ce2
--- /dev/null
+++ b/test/ELF/lto/Inputs/symbol-ordering-lto.ll
@@ -0,0 +1,10 @@
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-scei-ps4"
+
+define i32 @tin() {
+ ret i32 22
+}
+
+define i32 @pat() {
+ ret i32 42
+}
diff --git a/test/ELF/lto/available-externally.ll b/test/ELF/lto/available-externally.ll
index 181042b9d..315e710ec 100644
--- a/test/ELF/lto/available-externally.ll
+++ b/test/ELF/lto/available-externally.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; 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
diff --git a/test/ELF/lto/cache.ll b/test/ELF/lto/cache.ll
index 55e3a3d6f..6731f5226 100644
--- a/test/ELF/lto/cache.ll
+++ b/test/ELF/lto/cache.ll
@@ -12,6 +12,17 @@
; Two cached objects, plus a timestamp file and "foo", minus the file we removed.
; RUN: ls %t.cache | count 4
+; Create a file of size 64KB.
+; RUN: %python -c "print(' ' * 65536)" > %t.cache/llvmcache-foo
+
+; This should leave the file in place.
+; RUN: ld.lld --thinlto-cache-dir=%t.cache --thinlto-cache-policy cache_size_bytes=128k -o %t3 %t2.o %t.o
+; RUN: ls %t.cache | count 5
+
+; This should remove it.
+; RUN: ld.lld --thinlto-cache-dir=%t.cache --thinlto-cache-policy cache_size_bytes=32k -o %t3 %t2.o %t.o
+; RUN: ls %t.cache | count 4
+
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/comdat2.ll b/test/ELF/lto/comdat2.ll
index 1509585f1..283182155 100644
--- a/test/ELF/lto/comdat2.ll
+++ b/test/ELF/lto/comdat2.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: llvm-mc -triple=x86_64-pc-linux %p/Inputs/comdat.s -o %t2.o -filetype=obj
; RUN: ld.lld -m elf_x86_64 %t.o %t2.o -o %t.so -shared
diff --git a/test/ELF/lto/common2.ll b/test/ELF/lto/common2.ll
index b44bbac4f..2345a203b 100644
--- a/test/ELF/lto/common2.ll
+++ b/test/ELF/lto/common2.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; 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.0.2.internalize.bc | FileCheck %s
diff --git a/test/ELF/lto/common3.ll b/test/ELF/lto/common3.ll
index 6d40de547..aea33f443 100644
--- a/test/ELF/lto/common3.ll
+++ b/test/ELF/lto/common3.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; 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
diff --git a/test/ELF/lto/data-ordering-lto.s b/test/ELF/lto/data-ordering-lto.s
new file mode 100644
index 000000000..0364e587b
--- /dev/null
+++ b/test/ELF/lto/data-ordering-lto.s
@@ -0,0 +1,27 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-scei-ps4 %s -o %t.o
+# RUN: llvm-as %p/Inputs/data-ordering-lto.ll -o %t.bc
+
+# Set up the symbol file
+# RUN: echo "tin " > %t_order_lto.txt
+# RUN: echo "dipsy " >> %t_order_lto.txt
+# RUN: echo "pat " >> %t_order_lto.txt
+
+# RUN: ld.lld --symbol-ordering-file %t_order_lto.txt %t.o %t.bc -o %t2.out
+# RUN: llvm-readobj -elf-output-style=GNU -t %t2.out| FileCheck %s
+
+# Check that the order is tin -> dipsy -> pat.
+
+# CHECK: Symbol table '.symtab' contains 5 entries:
+# CHECK-NEXT: Num: Value Size Type Bind Vis Ndx Name
+# CHECK-NEXT: 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
+# CHECK-NEXT: 1: 0000000000201000 0 NOTYPE GLOBAL DEFAULT 1 _start
+# CHECK-NEXT: 2: 0000000000202004 4 OBJECT GLOBAL DEFAULT 2 dipsy
+# CHECK-NEXT: 3: 0000000000202008 4 OBJECT GLOBAL DEFAULT 2 pat
+# CHECK-NEXT: 4: 0000000000202000 4 OBJECT GLOBAL DEFAULT 2 tin
+
+.globl _start
+_start:
+ movl $pat, %ecx
+ movl $dipsy, %ebx
+ movl $tin, %eax
diff --git a/test/ELF/lto/defsym.ll b/test/ELF/lto/defsym.ll
index 4c2fe45b3..2ce8570f9 100644
--- a/test/ELF/lto/defsym.ll
+++ b/test/ELF/lto/defsym.ll
@@ -1,9 +1,16 @@
; REQUIRES: x86
+; LTO
; RUN: llvm-as %s -o %t.o
; RUN: llvm-as %S/Inputs/defsym-bar.ll -o %t1.o
; RUN: ld.lld %t.o %t1.o -shared -o %t.so -defsym=bar2=bar3
; RUN: llvm-objdump -d %t.so | FileCheck %s
+; ThinLTO
+; RUN: opt -module-summary %s -o %t.o
+; RUN: opt -module-summary %S/Inputs/defsym-bar.ll -o %t1.o
+; RUN: ld.lld %t.o %t1.o -shared -o %t.so -defsym=bar2=bar3
+; RUN: llvm-objdump -d %t.so | FileCheck %s --check-prefix=THIN
+
; Call to bar2() should not be inlined and should be routed to bar3()
; Symbol bar3 should not be eliminated
@@ -13,6 +20,13 @@
; CHECK-NEXT: callq{{.*}}<bar3>
; CHECK-NEXT: callq
+; THIN: foo
+; THIN-NEXT: pushq %rax
+; THIN-NEXT: callq
+; THIN-NEXT: callq{{.*}}<bar3>
+; THIN-NEXT: popq %rax
+; THIN-NEXT: jmp
+
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/discard-value-names.ll b/test/ELF/lto/discard-value-names.ll
index c71dc3861..f1e95fe75 100644
--- a/test/ELF/lto/discard-value-names.ll
+++ b/test/ELF/lto/discard-value-names.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 -shared -save-temps %t.o -o %t2.o
diff --git a/test/ELF/lto/keep-undefined.ll b/test/ELF/lto/keep-undefined.ll
new file mode 100644
index 000000000..cb0f4ce49
--- /dev/null
+++ b/test/ELF/lto/keep-undefined.ll
@@ -0,0 +1,20 @@
+; REQUIRES: x86
+; This test checks that symbols which are specified in "-u" switches
+; are kept over LTO if we link an executable.
+; RUN: llvm-as %s -o %t.o
+; RUN: ld.lld -m elf_x86_64 %t.o -o %tout -u foo
+; RUN: llvm-nm %tout | FileCheck %s
+
+; CHECK: T foo
+
+target triple = "x86_64-unknown-linux-gnu"
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+define void @foo() {
+ ret void
+}
+
+define void @_start() {
+ call void @foo()
+ ret void
+}
diff --git a/test/ELF/lto/linker-script-symbols-assign.ll b/test/ELF/lto/linker-script-symbols-assign.ll
new file mode 100644
index 000000000..2ffdc823a
--- /dev/null
+++ b/test/ELF/lto/linker-script-symbols-assign.ll
@@ -0,0 +1,48 @@
+; REQUIRES: x86
+; RUN: llvm-as %s -o %t.o
+
+; RUN: echo "foo = 1;" > %t.script
+; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 --script %t.script -save-temps
+; RUN: llvm-readobj -symbols %t2.lto.o | FileCheck %s
+
+; CHECK-NOT: bar
+; CHECK: Symbol {
+; CHECK: Name: foo
+; CHECK-NEXT: Value: 0x0
+; CHECK-NEXT: Size: 4
+; CHECK-NEXT: Binding: Weak
+; CHECK-NEXT: Type: Object
+; CHECK-NEXT: Other: 0
+; CHECK-NEXT: Section: .bss.foo
+; CHECK-NEXT: }
+; CHECK-NEXT:]
+
+; RUN: llvm-readobj -symbols %t2 | FileCheck %s --check-prefix=VAL
+; VAL: Symbol {
+; VAL: Name: foo
+; VAL-NEXT: Value: 0x1
+; VAL-NEXT: Size:
+; VAL-NEXT: Binding: Global
+; VAL-NEXT: Type: None
+; VAL-NEXT: Other:
+; VAL-NEXT: Section: Absolute
+; VAL-NEXT: }
+
+; RUN: echo "zed = 1;" > %t2.script
+; RUN: ld.lld -m elf_x86_64 %t.o -o %t3 --script %t2.script
+; RUN: llvm-readobj -symbols %t3 | FileCheck %s --check-prefix=ABS
+; ABS: Symbol {
+; ABS: Name: zed
+; ABS-NEXT: Value: 0x1
+; ABS-NEXT: Size: 0
+; ABS-NEXT: Binding: Global
+; ABS-NEXT: Type: None
+; ABS-NEXT: Other: 0
+; ABS-NEXT: Section: Absolute
+; ABS-NEXT: }
+
+target triple = "x86_64-unknown-linux-gnu"
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+@foo = global i32 0
+@bar = global i32 0
diff --git a/test/ELF/lto/linker-script-symbols-ipo.ll b/test/ELF/lto/linker-script-symbols-ipo.ll
new file mode 100644
index 000000000..6ac1a83e1
--- /dev/null
+++ b/test/ELF/lto/linker-script-symbols-ipo.ll
@@ -0,0 +1,32 @@
+; REQUIRES: x86
+; RUN: llvm-as %s -o %t1.o
+; RUN: llvm-as %S/Inputs/linker-script-symbols-ipo.ll -o %t2.o
+; RUN: echo "bar = foo;" > %t.script
+
+;; Check that without linkerscript bar is inlined.
+; RUN: ld.lld -m elf_x86_64 %t1.o %t2.o -o %t3 -save-temps
+; RUN: llvm-objdump -d %t3 | FileCheck %s --check-prefix=IPO
+; IPO: Disassembly of section .text:
+; IPO: _start:
+; IPO-NEXT: 201000: {{.*}} movl $1, %eax
+; IPO-NEXT: 201005: {{.*}} retq
+
+;; Check that LTO does not do IPO for symbols assigned by script.
+; RUN: ld.lld -m elf_x86_64 %t1.o %t2.o -o %t4 --script %t.script -save-temps
+; RUN: llvm-objdump -d %t4 | FileCheck %s --check-prefix=NOIPO
+; NOIPO: Disassembly of section .text:
+; NOIPO: foo:
+; NOIPO-NEXT: 201010: {{.*}} movl $2, %eax
+; NOIPO: _start:
+; NOIPO-NEXT: 201020: {{.*}} jmp -21 <foo>
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define i32 @bar() {
+ ret i32 1
+}
+
+define i32 @foo() {
+ ret i32 2
+}
diff --git a/test/ELF/lto/linker-script-symbols.ll b/test/ELF/lto/linker-script-symbols.ll
new file mode 100644
index 000000000..c2a58b6e8
--- /dev/null
+++ b/test/ELF/lto/linker-script-symbols.ll
@@ -0,0 +1,29 @@
+; REQUIRES: x86
+; RUN: llvm-as %s -o %t.o
+; RUN: echo "foo = bar;" > %t.script
+
+; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 --script %t.script -save-temps
+; RUN: llvm-readobj -symbols %t2.lto.o | FileCheck %s
+
+; CHECK-NOT: zed
+; CHECK: Symbol {
+; CHECK: Name: bar
+; CHECK-NEXT: Value:
+; CHECK-NEXT: Size:
+; CHECK-NEXT: Binding: Global
+; CHECK-NEXT: Type: Function
+; CHECK-NEXT: Other:
+; CHECK-NEXT: Section:
+; CHECK-NEXT: }
+; CHECK-NOT: zed
+
+target triple = "x86_64-unknown-linux-gnu"
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+define void @bar() {
+ ret void
+}
+
+define void @zed() {
+ ret void
+}
diff --git a/test/ELF/lto/opt-level.ll b/test/ELF/lto/opt-level.ll
index 934bf01b6..57fa3041a 100644
--- a/test/ELF/lto/opt-level.ll
+++ b/test/ELF/lto/opt-level.ll
@@ -1,18 +1,32 @@
+; REQUIRES: x86
; RUN: llvm-as -o %t.o %s
; RUN: ld.lld -o %t0 -m elf_x86_64 -e main --lto-O0 %t.o
; RUN: llvm-nm %t0 | FileCheck --check-prefix=CHECK-O0 %s
+; RUN: ld.lld -o %t0 -m elf_x86_64 -e main --plugin-opt=O0 %t.o
+; RUN: llvm-nm %t0 | FileCheck --check-prefix=CHECK-O0 %s
; RUN: ld.lld -o %t2 -m elf_x86_64 -e main --lto-O2 %t.o
; RUN: llvm-nm %t2 | FileCheck --check-prefix=CHECK-O2 %s
; RUN: ld.lld -o %t2a -m elf_x86_64 -e main %t.o
; RUN: llvm-nm %t2a | FileCheck --check-prefix=CHECK-O2 %s
+; RUN: ld.lld -o %t2 -m elf_x86_64 -e main --plugin-opt=O2 %t.o
+; RUN: llvm-nm %t2 | FileCheck --check-prefix=CHECK-O2 %s
; Reject invalid optimization levels.
; RUN: not ld.lld -o %t3 -m elf_x86_64 -e main --lto-O6 %t.o 2>&1 | \
-; RUN: FileCheck --check-prefix=INVALID %s
-; INVALID: invalid optimization level for LTO: 6
+; RUN: FileCheck --check-prefix=INVALID1 %s
+; INVALID1: invalid optimization level for LTO: 6
+; RUN: not ld.lld -o %t3 -m elf_x86_64 -e main --plugin-opt=O6 %t.o 2>&1 | \
+; RUN: FileCheck --check-prefix=INVALID1 %s
+; RUN: not ld.lld -o %t3 -m elf_x86_64 -e main --plugin-opt=Ofoo %t.o 2>&1 | \
+; RUN: FileCheck --check-prefix=INVALID2 %s
+; INVALID2: --plugin-opt: number expected, but got 'foo'
+
; RUN: not ld.lld -o %t3 -m elf_x86_64 -e main --lto-O-1 %t.o 2>&1 | \
-; RUN: FileCheck --check-prefix=INVALIDNEGATIVE %s
-; INVALIDNEGATIVE: invalid optimization level for LTO: -1
+; RUN: FileCheck --check-prefix=INVALIDNEGATIVE1 %s
+; INVALIDNEGATIVE1: invalid optimization level for LTO: 4294967295
+; RUN: not ld.lld -o %t3 -m elf_x86_64 -e main --plugin-opt=O-1 %t.o 2>&1 | \
+; RUN: FileCheck --check-prefix=INVALIDNEGATIVE2 %s
+; INVALIDNEGATIVE2: invalid optimization level for LTO: 4294967295
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/opt-remarks.ll b/test/ELF/lto/opt-remarks.ll
index 88304205c..19b141feb 100644
--- a/test/ELF/lto/opt-remarks.ll
+++ b/test/ELF/lto/opt-remarks.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: rm -f %t.yaml
@@ -14,13 +15,13 @@
; CHECK-NEXT: ret i32 %a.i
; CHECK-NEXT: }
-; YAML: --- !Analysis
+; YAML: --- !Passed
; YAML-NEXT: Pass: inline
-; YAML-NEXT: Name: CanBeInlined
+; YAML-NEXT: Name: Inlined
; YAML-NEXT: Function: main
; YAML-NEXT: Args:
; YAML-NEXT: - Callee: tinkywinky
-; YAML-NEXT: - String: ' can be inlined into '
+; YAML-NEXT: - String: ' inlined into '
; YAML-NEXT: - Caller: main
; YAML-NEXT: - String: ' with cost='
; YAML-NEXT: - Cost: '0'
@@ -28,19 +29,9 @@
; YAML-NEXT: - Threshold: '337'
; YAML-NEXT: - String: ')'
; YAML-NEXT: ...
-; YAML-NEXT: --- !Passed
-; YAML-NEXT: Pass: inline
-; YAML-NEXT: Name: Inlined
-; YAML-NEXT: Function: main
-; YAML-NEXT: Args:
-; YAML-NEXT: - Callee: tinkywinky
-; YAML-NEXT: - String: ' inlined into '
-; YAML-NEXT: - Caller: main
-; YAML-NEXT: ...
-; YAML-HOT: ...
-; YAML-HOT: --- !Passed
-; YAML-HOT: Pass: inline
+; YAML-HOT: --- !Passed
+; YAML-HOT-NEXT: Pass: inline
; YAML-HOT-NEXT: Name: Inlined
; YAML-HOT-NEXT: Function: main
; YAML-HOT-NEXT: Hotness: 300
@@ -48,6 +39,11 @@
; YAML-HOT-NEXT: - Callee: tinkywinky
; YAML-HOT-NEXT: - String: ' inlined into '
; YAML-HOT-NEXT: - Caller: main
+; YAML-HOT-NEXT: - String: ' with cost='
+; YAML-HOT-NEXT: - Cost: '0'
+; YAML-HOT-NEXT: - String: ' (threshold='
+; YAML-HOT-NEXT: - Threshold: '337'
+; YAML-HOT-NEXT: - String: ')'
; YAML-HOT-NEXT: ...
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
diff --git a/test/ELF/lto/relax-relocs.ll b/test/ELF/lto/relax-relocs.ll
index 929e124b2..8e8d9d165 100644
--- a/test/ELF/lto/relax-relocs.ll
+++ b/test/ELF/lto/relax-relocs.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 -save-temps -shared %t.o -o %t.so
; RUN: llvm-readobj -r %t.so.lto.o | FileCheck %s
diff --git a/test/ELF/lto/relocatable.ll b/test/ELF/lto/relocatable.ll
new file mode 100644
index 000000000..ef21f84a6
--- /dev/null
+++ b/test/ELF/lto/relocatable.ll
@@ -0,0 +1,55 @@
+; REQUIRES: x86
+; RUN: llvm-as %s -o %t1.o
+; RUN: ld.lld %t1.o -r -o %t
+; RUN: llvm-readobj -symbols %t | FileCheck %s
+
+; 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:
+; CHECK-NEXT: Value: 0x0
+; CHECK-NEXT: Size: 0
+; CHECK-NEXT: Binding: Local
+; CHECK-NEXT: Type: Section
+; CHECK-NEXT: Other: 0
+; CHECK-NEXT: Section: .text
+; CHECK-NEXT: }
+; CHECK-NEXT: Symbol {
+; CHECK-NEXT: Name:
+; CHECK-NEXT: Value: 0x0
+; CHECK-NEXT: Size: 0
+; CHECK-NEXT: Binding: Local
+; CHECK-NEXT: Type: Section
+; CHECK-NEXT: Other: 0
+; CHECK-NEXT: Section: .text.foo
+; CHECK-NEXT: }
+; CHECK-NEXT: Symbol {
+; CHECK-NEXT: Name: foo
+; CHECK-NEXT: Value: 0x0
+; CHECK-NEXT: Size: 1
+; CHECK-NEXT: Binding: Global
+; CHECK-NEXT: Type: Function
+; CHECK-NEXT: Other: 0
+; CHECK-NEXT: Section: .text.foo
+; CHECK-NEXT: }
+; CHECK-NEXT: ]
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define void @foo() {
+ call void @bar()
+ ret void
+}
+
+define internal void @bar() {
+ ret void
+}
diff --git a/test/ELF/lto/save-temps.ll b/test/ELF/lto/save-temps.ll
index f7af99ed4..c8e52ff4b 100644
--- a/test/ELF/lto/save-temps.ll
+++ b/test/ELF/lto/save-temps.ll
@@ -9,6 +9,13 @@
; RUN: llvm-nm a.out.lto.o | FileCheck %s
; RUN: llvm-dis a.out.0.0.preopt.bc
+; RUN: rm -f a.out a.out.lto.bc a.out.lto.o
+; RUN: ld.lld -shared -m elf_x86_64 %t.o %t2.o --plugin-opt=save-temps
+; RUN: llvm-nm a.out | 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.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/section-name.ll b/test/ELF/lto/section-name.ll
new file mode 100644
index 000000000..483184716
--- /dev/null
+++ b/test/ELF/lto/section-name.ll
@@ -0,0 +1,35 @@
+; REQUIRES: x86
+; RUN: llvm-as %s -o %t.o
+; RUN: ld.lld %t.o -o %t.so -shared
+; RUN: llvm-readelf -s %t.so | FileCheck %s
+; RUN: ld.lld %t.o -o %t.so -shared --gc-sections
+; RUN: llvm-readelf -s %t.so | FileCheck --check-prefix=GC %s
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+@foo = hidden global i32 42, section "foo_section"
+@bar = hidden global i32 42, section "bar_section"
+@zed = hidden global i32 42, section "zed_section"
+
+@__start_foo_section = external global i32
+@__stop_bar_section = external global i32
+
+define hidden i32* @use1() {
+ ret i32* @__start_foo_section
+}
+
+define i32* @use2() {
+ ret i32* @__stop_bar_section
+}
+
+; CHECK-NOT: zed_section
+; CHECK: foo_section PROGBITS
+; CHECK-NEXT: bar_section PROGBITS
+; CHECK-NOT: zed_section
+
+; GC-NOT: zed_section
+; GC-NOT: foo_section
+; GC: bar_section PROGBITS
+; GC-NOT: zed_section
+; GC-NOT: foo_section
diff --git a/test/ELF/lto/symbol-ordering-lto.s b/test/ELF/lto/symbol-ordering-lto.s
new file mode 100644
index 000000000..4c29e54c4
--- /dev/null
+++ b/test/ELF/lto/symbol-ordering-lto.s
@@ -0,0 +1,25 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-scei-ps4 %s -o %t.o
+# RUN: llvm-as %p/Inputs/symbol-ordering-lto.ll -o %t.bc
+
+# Set up the symbol file
+# RUN: echo "tin " > %t_order_lto.txt
+# RUN: echo "_start " >> %t_order_lto.txt
+# RUN: echo "pat " >> %t_order_lto.txt
+
+# RUN: ld.lld --symbol-ordering-file %t_order_lto.txt %t.o %t.bc -o %t2.out
+# RUN: llvm-readobj -elf-output-style=GNU -t %t2.out| FileCheck %s
+
+# Check that the order is tin -> _start -> pat.
+
+# CHECK: Symbol table '.symtab' contains 4 entries:
+# CHECK-NEXT: Num: Value Size Type Bind Vis Ndx Name
+# CHECK-NEXT: 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
+# CHECK-NEXT: 1: 0000000000201008 0 NOTYPE GLOBAL DEFAULT 1 _start
+# CHECK-NEXT: 2: 0000000000201020 6 FUNC GLOBAL DEFAULT 1 pat
+# CHECK-NEXT: 3: 0000000000201000 6 FUNC GLOBAL DEFAULT 1 tin
+
+.globl _start
+_start:
+ call pat
+ call tin
diff --git a/test/ELF/lto/thin-archivecollision.ll b/test/ELF/lto/thin-archivecollision.ll
index f1dd5ae4d..554c2b02f 100644
--- a/test/ELF/lto/thin-archivecollision.ll
+++ b/test/ELF/lto/thin-archivecollision.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; RUN: opt -module-summary %s -o %t.o
; RUN: mkdir -p %t1 %t2
; RUN: opt -module-summary %p/Inputs/thin1.ll -o %t1/t.coll.o
diff --git a/test/ELF/lto/thinlto.ll b/test/ELF/lto/thinlto.ll
index 2036c554a..99dd19130 100644
--- a/test/ELF/lto/thinlto.ll
+++ b/test/ELF/lto/thinlto.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; Basic ThinLTO tests.
; RUN: opt -module-summary %s -o %t.o
; RUN: opt -module-summary %p/Inputs/thinlto.ll -o %t2.o
diff --git a/test/ELF/lto/type-merge2.ll b/test/ELF/lto/type-merge2.ll
index 45777a7e6..6ebbf778d 100644
--- a/test/ELF/lto/type-merge2.ll
+++ b/test/ELF/lto/type-merge2.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; 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
diff --git a/test/ELF/lto/unnamed-addr-comdat.ll b/test/ELF/lto/unnamed-addr-comdat.ll
index ed48f3ba5..29a594158 100644
--- a/test/ELF/lto/unnamed-addr-comdat.ll
+++ b/test/ELF/lto/unnamed-addr-comdat.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; 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.0.2.internalize.bc -o - | FileCheck %s
diff --git a/test/ELF/lto/unnamed-addr-drop.ll b/test/ELF/lto/unnamed-addr-drop.ll
index 9142537fe..e827cbb43 100644
--- a/test/ELF/lto/unnamed-addr-drop.ll
+++ b/test/ELF/lto/unnamed-addr-drop.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; 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
diff --git a/test/ELF/lto/unnamed-addr.ll b/test/ELF/lto/unnamed-addr.ll
index 6a6dd73da..56fe148b0 100644
--- a/test/ELF/lto/unnamed-addr.ll
+++ b/test/ELF/lto/unnamed-addr.ll
@@ -1,3 +1,4 @@
+; REQUIRES: x86
; 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.0.4.opt.bc -o - | FileCheck %s
diff --git a/test/ELF/lto/verify-invalid.ll b/test/ELF/lto/verify-invalid.ll
index 16d6a3e54..e6138a3cc 100644
--- a/test/ELF/lto/verify-invalid.ll
+++ b/test/ELF/lto/verify-invalid.ll
@@ -4,6 +4,8 @@
; RUN: 2>&1 | FileCheck -check-prefix=DEFAULT %s
; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 -mllvm -debug-pass=Arguments \
; RUN: -disable-verify 2>&1 | FileCheck -check-prefix=DISABLE %s
+; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 -mllvm -debug-pass=Arguments \
+; RUN: --plugin-opt=disable-verify 2>&1 | FileCheck -check-prefix=DISABLE %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/wrap-1.ll b/test/ELF/lto/wrap-1.ll
index b61dfaeb5..83e09493f 100644
--- a/test/ELF/lto/wrap-1.ll
+++ b/test/ELF/lto/wrap-1.ll
@@ -1,9 +1,16 @@
; REQUIRES: x86
+; LTO
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld %t.o -o %t.out -wrap=bar -save-temps
; RUN: llvm-readobj -t %t.out | FileCheck %s
; RUN: cat %t.out.resolution.txt | FileCheck -check-prefix=RESOLS %s
+; ThinLTO
+; RUN: opt -module-summary %s -o %t.o
+; RUN: ld.lld %t.o -o %t.out -wrap=bar -save-temps
+; RUN: llvm-readobj -t %t.out | FileCheck %s
+; RUN: cat %t.out.resolution.txt | FileCheck -check-prefix=RESOLS %s
+
; CHECK: Name: __wrap_bar
; CHECK-NEXT: Value:
; CHECK-NEXT: Size:
@@ -12,7 +19,7 @@
; Make sure that the 'r' (linker redefined) bit is set for bar and __wrap_bar
; in the resolutions file.
-; RESOLS: ,bar,r
+; RESOLS: ,bar,xr
; RESOLS: ,__wrap_bar,px
; RESOLS: ,__real_bar,pxr
diff --git a/test/ELF/lto/wrap-2.ll b/test/ELF/lto/wrap-2.ll
index b2e33f831..4e82d4a0e 100644
--- a/test/ELF/lto/wrap-2.ll
+++ b/test/ELF/lto/wrap-2.ll
@@ -1,10 +1,18 @@
; REQUIRES: x86
+; LTO
; RUN: llvm-as %s -o %t.o
; RUN: llvm-as %S/Inputs/wrap-bar.ll -o %t1.o
; RUN: ld.lld %t.o %t1.o -shared -o %t.so -wrap=bar
; RUN: llvm-objdump -d %t.so | FileCheck %s
; RUN: llvm-readobj -t %t.so | FileCheck -check-prefix=BIND %s
+; ThinLTO
+; RUN: opt -module-summary %s -o %t.o
+; RUN: opt -module-summary %S/Inputs/wrap-bar.ll -o %t1.o
+; RUN: ld.lld %t.o %t1.o -shared -o %t.so -wrap=bar
+; RUN: llvm-objdump -d %t.so | FileCheck %s -check-prefix=THIN
+; RUN: llvm-readobj -t %t.so | FileCheck -check-prefix=BIND %s
+
; Make sure that calls in foo() are not eliminated and that bar is
; routed to __wrap_bar and __real_bar is routed to bar.
@@ -13,12 +21,18 @@
; CHECK-NEXT: callq{{.*}}<__wrap_bar>
; CHECK-NEXT: callq{{.*}}<bar>
+; THIN: foo:
+; THIN-NEXT: pushq %rax
+; THIN-NEXT: callq{{.*}}<__wrap_bar>
+; THIN-NEXT: popq %rax
+; THIN-NEXT: jmp{{.*}}<bar>
+
; Check that bar and __wrap_bar retain their original binding.
-; BIND: Name: bar
+; BIND: Name: __wrap_bar
; BIND-NEXT: Value:
; BIND-NEXT: Size:
; BIND-NEXT: Binding: Local
-; BIND: Name: __wrap_bar
+; BIND: Name: bar
; BIND-NEXT: Value:
; BIND-NEXT: Size:
; BIND-NEXT: Binding: Local
diff --git a/test/ELF/many-alloc-sections.s b/test/ELF/many-alloc-sections.s
index 441e5ff32..a022b9275 100644
--- a/test/ELF/many-alloc-sections.s
+++ b/test/ELF/many-alloc-sections.s
@@ -1,7 +1,7 @@
+// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o %t.o
// RUN: echo "SECTIONS { . = SIZEOF_HEADERS; .text : { *(.text) } }" > %t.script
-// FIXME: threads are disable because the test is too slow with them (PR32942).
-// RUN: ld.lld -T %t.script %t.o -o %t --no-threads
+// RUN: ld.lld -T %t.script %t.o -o %t
// RUN: llvm-readobj -t %t | FileCheck %s
// Test that _start is in the correct section.
diff --git a/test/ELF/many-sections.s b/test/ELF/many-sections.s
index ae923889d..e3b845f30 100644
--- a/test/ELF/many-sections.s
+++ b/test/ELF/many-sections.s
@@ -1,3 +1,4 @@
+// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o %t
// RUN: llvm-readobj -t %t | FileCheck %s
@@ -10,15 +11,12 @@
// CHECK-NEXT: Other: 0
// CHECK-NEXT: Section: dm (0xFF00)
-
-// FIXME: threads are disable because the test is too slow with them (PR32942).
-// RUN: ld.lld %t -o %t2 --no-threads
+// RUN: ld.lld %t -o %t2
// RUN: llvm-readobj -t %t2 | FileCheck --check-prefix=LINKED %s
// Test also with a linker script.
// RUN: echo "SECTIONS { . = SIZEOF_HEADERS; .text : { *(.text) } }" > %t.script
-// FIXME: threads are disable because the test is too slow with them (PR32942).
-// RUN: ld.lld -T %t.script %t -o %t2 --no-threads
+// RUN: ld.lld -T %t.script %t -o %t2
// RUN: llvm-readobj -t %t2 | FileCheck --check-prefix=LINKED %s
// Test that _start is in the correct section.
diff --git a/test/ELF/map-file.s b/test/ELF/map-file.s
index 9dbbda0ff..148894c3c 100644
--- a/test/ELF/map-file.s
+++ b/test/ELF/map-file.s
@@ -25,6 +25,9 @@ bar:
.long zed - .
local:
.comm common,4,16
+.global abs
+abs = 0xAB5
+labs = 0x1AB5
// CHECK: Address Size Align Out In Symbol
// CHECK-NEXT: 0000000000200158 0000000000000030 8 .eh_frame
@@ -44,15 +47,16 @@ local:
// CHECK-NEXT: 0000000000201014 0000000000000001 4 {{.*}}{{/|\\}}map-file.s.tmp4.a(map-file.s.tmp4.o):(.text)
// CHECK-NEXT: 0000000000201014 0000000000000000 0 baz
// CHECK-NEXT: 0000000000202000 0000000000000004 16 .bss
-// CHECK-NEXT: 0000000000202000 0000000000000004 16 <internal>:(COMMON)
+// CHECK-NEXT: 0000000000202000 0000000000000004 16 {{.*}}{{/|\\}}map-file.s.tmp1.o:(COMMON)
+// CHECK-NEXT: 0000000000202000 0000000000000004 0 common
// CHECK-NEXT: 0000000000000000 0000000000000008 1 .comment
// CHECK-NEXT: 0000000000000000 0000000000000008 1 <internal>:(.comment)
-// CHECK-NEXT: 0000000000000000 00000000000000f0 8 .symtab
-// CHECK-NEXT: 0000000000000000 00000000000000f0 8 <internal>:(.symtab)
+// CHECK-NEXT: 0000000000000000 0000000000000120 8 .symtab
+// CHECK-NEXT: 0000000000000000 0000000000000120 8 <internal>:(.symtab)
// CHECK-NEXT: 0000000000000000 0000000000000039 1 .shstrtab
// CHECK-NEXT: 0000000000000000 0000000000000039 1 <internal>:(.shstrtab)
-// CHECK-NEXT: 0000000000000000 000000000000002f 1 .strtab
-// CHECK-NEXT: 0000000000000000 000000000000002f 1 <internal>:(.strtab)
+// CHECK-NEXT: 0000000000000000 0000000000000038 1 .strtab
+// CHECK-NEXT: 0000000000000000 0000000000000038 1 <internal>:(.strtab)
// RUN: not ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -Map=/ 2>&1 \
// RUN: | FileCheck -check-prefix=FAIL %s
diff --git a/test/ELF/map-gc-sections.s b/test/ELF/map-gc-sections.s
new file mode 100644
index 000000000..717ab8198
--- /dev/null
+++ b/test/ELF/map-gc-sections.s
@@ -0,0 +1,9 @@
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: ld.lld %t.o -o %t -Map=- --gc-sections | FileCheck %s
+
+.section .tbss,"awT",@nobits
+// CHECK-NOT: foo
+.globl foo
+foo:
+.align 8
+.long 0
diff --git a/test/ELF/merge-section-types.s b/test/ELF/merge-section-types.s
index ee80fe177..f84628246 100644
--- a/test/ELF/merge-section-types.s
+++ b/test/ELF/merge-section-types.s
@@ -1,3 +1,4 @@
+// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
// RUN: ld.lld -shared %t.o -o %t
// RUN: llvm-readobj -s %t | FileCheck %s
diff --git a/test/ELF/merge-string.s b/test/ELF/merge-string.s
index 13c89f029..5baa6334e 100644
--- a/test/ELF/merge-string.s
+++ b/test/ELF/merge-string.s
@@ -1,10 +1,10 @@
// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-// RUN: ld.lld -O2 %t.o -o %t.so -shared
+// RUN: ld.lld -O 2 %t.o -o %t.so -shared
// RUN: llvm-readobj -s -section-data -t %t.so | FileCheck %s
-// RUN: ld.lld -O1 %t.o -o %t.so -shared
+// RUN: ld.lld -O 1 %t.o -o %t.so -shared
// RUN: llvm-readobj -s -section-data -t %t.so | FileCheck --check-prefix=NOTAIL %s
-// RUN: ld.lld -O0 %t.o -o %t.so -shared
+// RUN: ld.lld -O 0 %t.o -o %t.so -shared
// RUN: llvm-readobj -s -section-data -t %t.so | FileCheck --check-prefix=NOMERGE %s
.section .rodata1,"aMS",@progbits,1
@@ -54,7 +54,7 @@ zed:
// NOTAIL-NEXT: AddressAlignment: 1
// NOTAIL-NEXT: EntrySize: 0
// NOTAIL-NEXT: SectionData (
-// NOTAIL-NEXT: 0000: 61626300 626300 |abc.bc.|
+// NOTAIL-NEXT: 0000: 62630061 626300 |bc.abc.|
// NOTAIL-NEXT: )
// NOMERGE: Name: .rodata1
diff --git a/test/ELF/mips-64-gprel-so.s b/test/ELF/mips-64-gprel-so.s
index a390ec082..437238ef5 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 *ABS* 00000000 .hidden _gp
+# CHECK: 0000000000027ff0 .got 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 93d893aac..a9fb2d9a9 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 *ABS* 00000000 .hidden _gp
+# CHECK: 0000000000037ff0 .got 00000000 .hidden _gp
# CHECK: 0000000000020000 .text 00000000 __start
# REL: Relocations [
diff --git a/test/ELF/mips-elf-flags.s b/test/ELF/mips-elf-flags.s
index f8f916c93..d2b3d929e 100644
--- a/test/ELF/mips-elf-flags.s
+++ b/test/ELF/mips-elf-flags.s
@@ -35,6 +35,12 @@
# RUN: llvm-readobj -h -mips-abi-flags %t.exe \
# RUN: | FileCheck -check-prefix=OCTEON %s
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mattr=micromips %S/Inputs/mips-fpic.s -o %t-mm.o
+# RUN: ld.lld %t.o %t-mm.o -o %t.exe
+# RUN: llvm-readobj -h -mips-abi-flags %t.exe | FileCheck -check-prefix=MICRO %s
+
# REQUIRES: mips
.text
@@ -170,3 +176,26 @@ __start:
# OCTEON-NEXT: ]
# OCTEON-NEXT: Flags 2: 0x0
# OCTEON-NEXT: }
+
+# MICRO: Flags [
+# MICRO-NEXT: EF_MIPS_ABI_O32
+# MICRO-NEXT: EF_MIPS_ARCH_32
+# MICRO-NEXT: EF_MIPS_CPIC
+# MICRO-NEXT: EF_MIPS_MICROMIPS
+# MICRO-NEXT: ]
+# MICRO: MIPS ABI Flags {
+# MICRO-NEXT: Version: 0
+# MICRO-NEXT: ISA: MIPS32
+# MICRO-NEXT: ISA Extension: None
+# MICRO-NEXT: ASEs [
+# MICRO-NEXT: microMIPS
+# MICRO-NEXT: ]
+# MICRO-NEXT: FP ABI: Hard float (double precision)
+# MICRO-NEXT: GPR size: 32
+# MICRO-NEXT: CPR1 size: 32
+# MICRO-NEXT: CPR2 size: 0
+# MICRO-NEXT: Flags 1 [
+# MICRO-NEXT: ODDSPREG
+# MICRO-NEXT: ]
+# MICRO-NEXT: Flags 2: 0x0
+# MICRO-NEXT: }
diff --git a/test/ELF/mips-got-relocs.s b/test/ELF/mips-got-relocs.s
index 4471bc210..5b443e519 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 0000000000030010 DATA
# EXE_SYM: SYMBOL TABLE:
-# EXE_SYM: 00038000 *ABS* 00000000 .hidden _gp
+# EXE_SYM: 00038000 .got 00000000 .hidden _gp
# ^-- .got + GP offset (0x7ff0)
# EXE_SYM: 00030000 g .data 00000004 v1
@@ -71,7 +71,7 @@ v1:
# DSO_SYM: Sections:
# DSO_SYM: .got 0000000c 0000000000020010 DATA
# DSO_SYM: SYMBOL TABLE:
-# DSO_SYM: 00028000 *ABS* 00000000 .hidden _gp
+# DSO_SYM: 00028000 .got 00000000 .hidden _gp
# ^-- .got + GP offset (0x7ff0)
# DSO_SYM: 00020000 g .data 00000004 v1
diff --git a/test/ELF/mips-got16-relocatable.s b/test/ELF/mips-got16-relocatable.s
index 963efeb46..bbacfdbaa 100644
--- a/test/ELF/mips-got16-relocatable.s
+++ b/test/ELF/mips-got16-relocatable.s
@@ -15,8 +15,8 @@
# OBJ-NEXT: 00000000: R_MIPS_GOT16 .data
# OBJ-NEXT: 4: 27 24 00 00 addiu $4, $25, 0
# OBJ-NEXT: 00000004: R_MIPS_LO16 .data
-# OBJ-NEXT: 8: 00 00 00 00 nop
-# OBJ-NEXT: c: 00 00 00 00 nop
+# OBJ-NEXT: 8: ef ef ef ef <unknown>
+# OBJ-NEXT: c: ef ef ef ef <unknown>
# OBJ-NEXT: 10: 8f 99 00 00 lw $25, 0($gp)
# OBJ-NEXT: 00000010: R_MIPS_GOT16 .data
# OBJ-NEXT: 14: 27 24 00 10 addiu $4, $25, 16
@@ -26,8 +26,8 @@
# SO-NEXT: .text:
# SO-NEXT: 10000: 8f 99 80 18 lw $25, -32744($gp)
# SO-NEXT: 10004: 27 24 00 00 addiu $4, $25, 0
-# SO-NEXT: 10008: 00 00 00 00 nop
-# SO-NEXT: 1000c: 00 00 00 00 nop
+# SO-NEXT: 10008: ef ef ef ef <unknown>
+# SO-NEXT: 1000c: ef ef ef ef <unknown>
# SO-NEXT: 10010: 8f 99 80 18 lw $25, -32744($gp)
# SO-NEXT: 10014: 27 24 00 10 addiu $4, $25, 16
diff --git a/test/ELF/mips-gp-disp.s b/test/ELF/mips-gp-disp.s
index 62a2b1084..7a0fd6409 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 *ABS* 00000000 .hidden _gp
+# DIS: 00027ff0 .got 00000000 .hidden _gp
# REL: Relocations [
# REL-NEXT: ]
diff --git a/test/ELF/mips-gp-ext.s b/test/ELF/mips-gp-ext.s
index 98b9cbc06..2fd21b7a9 100644
--- a/test/ELF/mips-gp-ext.s
+++ b/test/ELF/mips-gp-ext.s
@@ -12,6 +12,13 @@
# RUN: echo "SECTIONS { \
# RUN: .text : { *(.text) } \
+# RUN: _gp = 0x100 + ABSOLUTE(.); \
+# 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
diff --git a/test/ELF/mips-gp-local.s b/test/ELF/mips-gp-local.s
index b77dbb836..8bb3c236e 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 *ABS* 00000000 .hidden _gp
+# CHECK: 00037ff0 .got 00000000 .hidden _gp
.text
.globl __start
diff --git a/test/ELF/mips-gprel32-relocs-gp0.s b/test/ELF/mips-gprel32-relocs-gp0.s
index 4f1962bd6..3173e7d06 100644
--- a/test/ELF/mips-gprel32-relocs-gp0.s
+++ b/test/ELF/mips-gprel32-relocs-gp0.s
@@ -29,7 +29,7 @@
# DUMP: SYMBOL TABLE:
# DUMP: 00010008 .text 00000000 bar
# DUMP: 00010004 .text 00000000 foo
-# DUMP: 00027ff0 *ABS* 00000000 .hidden _gp
+# DUMP: 00027ff0 .got 00000000 .hidden _gp
# ERR: error: {{.*}}mips-gp0-non-zero.o: unsupported non-zero ri_gp_value
diff --git a/test/ELF/mips-gprel32-relocs.s b/test/ELF/mips-gprel32-relocs.s
index 1c877b12b..2750fbb01 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 *ABS* 00000000 .hidden _gp
+# CHECK: 00027ff0 .got 00000000 .hidden _gp
diff --git a/test/ELF/mips-hilo-gp-disp.s b/test/ELF/mips-hilo-gp-disp.s
index 62e03c7b6..c7229ee0d 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: 00038000 *ABS* 00000000 .hidden _gp
+# EXE: 00038000 .got 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: 00028000 *ABS* 00000000 .hidden _gp
+# SO: 00028000 .got 00000000 .hidden _gp
# SO: 00010000 .text 00000000 __start
diff --git a/test/ELF/mips-hilo-hi-only.s b/test/ELF/mips-hilo-hi-only.s
index 97808b515..0858e3f6c 100644
--- a/test/ELF/mips-hilo-hi-only.s
+++ b/test/ELF/mips-hilo-hi-only.s
@@ -18,7 +18,7 @@ _label:
# CHECK: Disassembly of section .text:
# CHECK-NEXT: __start:
-# CHECK-NEXT: 20000: 3c 08 00 02 lui $8, 2
+# CHECK-NEXT: 20000: 3c 08 00 03 lui $8, 3
# ^-- %hi(__start) w/o addend
# CHECK-NEXT 20004: 21 08 00 08 addi $8, $8, 8
# ^-- %lo(_label)
diff --git a/test/ELF/mips-micro-got.s b/test/ELF/mips-micro-got.s
new file mode 100644
index 000000000..8d077f280
--- /dev/null
+++ b/test/ELF/mips-micro-got.s
@@ -0,0 +1,46 @@
+# Check microMIPS GOT relocations for O32 ABI.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux -mattr=micromips \
+# RUN: %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux -mattr=micromips \
+# RUN: %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-readobj -mips-plt-got %t.exe | FileCheck %s
+
+# REQUIRES: mips
+
+# CHECK: Local entries [
+# CHECK-NEXT: Entry {
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Access: -32744
+# CHECK-NEXT: Initial: 0x30000
+# CHECK-NEXT: }
+# CHECK-NEXT: Entry {
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Access: -32740
+# CHECK-NEXT: Initial: 0x40000
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+# CHECK-NEXT: Global entries [
+# CHECK-NEXT: Entry {
+# CHECK-NEXT: Address:
+# 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: ]
+
+ .text
+ .global __start
+__start:
+ lw $4, %got(data)($28)
+ addiu $4, $4, %lo(data)
+ lw $25, %call16(foo0)($28)
+
+ .data
+data:
+ .word 0
diff --git a/test/ELF/mips-micro-got64.s b/test/ELF/mips-micro-got64.s
new file mode 100644
index 000000000..653bfbfbe
--- /dev/null
+++ b/test/ELF/mips-micro-got64.s
@@ -0,0 +1,48 @@
+# Check microMIPS GOT relocations for N64 ABI.
+
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux -mattr=micromips \
+# RUN: %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux -mattr=micromips \
+# RUN: %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-readobj -mips-plt-got %t.exe | FileCheck %s
+
+# REQUIRES: mips
+
+# CHECK: Local entries [
+# CHECK-NEXT: Entry {
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Access: -32736
+# CHECK-NEXT: Initial: 0x30000
+# CHECK-NEXT: }
+# CHECK-NEXT: Entry {
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Access: -32728
+# CHECK-NEXT: Initial: 0x40000
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+# CHECK-NEXT: Global entries [
+# CHECK-NEXT: Entry {
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Access: -32720
+# CHECK-NEXT: Initial: 0x0
+# CHECK-NEXT: Value: 0x0
+# CHECK-NEXT: Type: Function
+# CHECK-NEXT: Section: Undefined
+# CHECK-NEXT: Name: foo0
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+ .text
+ .global __start
+__start:
+ lui $28, %hi(%neg(%gp_rel(foo0)))
+ addiu $28, $28, %lo(%neg(%gp_rel(foo0)))
+ lw $4, %got_page(data)($28)
+ addiu $4, $4, %got_ofst(data)
+ lw $25, %call16(foo0)($28)
+
+ .data
+data:
+ .word 0
diff --git a/test/ELF/mips-micro-jal.s b/test/ELF/mips-micro-jal.s
new file mode 100644
index 000000000..83826126f
--- /dev/null
+++ b/test/ELF/mips-micro-jal.s
@@ -0,0 +1,155 @@
+# Check PLT creation for microMIPS to microMIPS calls.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mattr=micromips %S/Inputs/mips-micro.s -o %t1eb.o
+# RUN: ld.lld -shared -o %teb.so %t1eb.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mattr=micromips %s -o %t2eb.o
+# RUN: ld.lld -o %teb.exe %t2eb.o %teb.so
+# RUN: llvm-objdump -d -mattr=micromips %teb.exe | FileCheck --check-prefix=EB %s
+# RUN: llvm-readobj -mips-plt-got %teb.exe | FileCheck --check-prefix=PLT %s
+
+# RUN: llvm-mc -filetype=obj -triple=mipsel-unknown-linux \
+# RUN: -mattr=micromips %S/Inputs/mips-micro.s -o %t1el.o
+# RUN: ld.lld -shared -o %tel.so %t1el.o
+# RUN: llvm-mc -filetype=obj -triple=mipsel-unknown-linux \
+# RUN: -mattr=micromips %s -o %t2el.o
+# RUN: ld.lld -o %tel.exe %t2el.o %tel.so
+# RUN: llvm-objdump -d -mattr=micromips %tel.exe | FileCheck --check-prefix=EL %s
+# RUN: llvm-readobj -mips-plt-got %tel.exe | FileCheck --check-prefix=PLT %s
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mattr=micromips -mcpu=mips32r6 %S/Inputs/mips-micro.s -o %t1eb.o
+# RUN: ld.lld -shared -o %teb.so %t1eb.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mattr=micromips -mcpu=mips32r6 %s -o %t2eb.o
+# RUN: ld.lld -o %teb.exe %t2eb.o %teb.so
+# RUN: llvm-objdump -d -mattr=micromips %teb.exe | FileCheck --check-prefix=EBR6 %s
+
+# RUN: llvm-mc -filetype=obj -triple=mipsel-unknown-linux \
+# RUN: -mattr=micromips -mcpu=mips32r6 %S/Inputs/mips-micro.s -o %t1el.o
+# RUN: ld.lld -shared -o %tel.so %t1el.o
+# RUN: llvm-mc -filetype=obj -triple=mipsel-unknown-linux \
+# RUN: -mattr=micromips -mcpu=mips32r6 %s -o %t2el.o
+# RUN: ld.lld -o %tel.exe %t2el.o %tel.so
+# RUN: llvm-objdump -d -mattr=micromips %tel.exe | FileCheck --check-prefix=ELR6 %s
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mattr=micromips %S/Inputs/mips-micro.s -o %t1eb.o
+# RUN: ld.lld -shared -o %teb.so %t1eb.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: %S/Inputs/mips-fpic.s -o %t-reg.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mattr=micromips %s -o %t2eb.o
+# RUN: ld.lld --no-threads -o %teb.exe %t-reg.o %t2eb.o %teb.so
+# RUN: llvm-objdump -d -mattr=micromips %teb.exe \
+# RUN: | FileCheck --check-prefix=MIXED %s
+
+# REQUIRES: mips
+
+# EB: Disassembly of section .plt:
+# EB-NEXT: .plt:
+# EB-NEXT: 20010: 79 80 3f fd addiupc $3, 65524
+# EB-NEXT: 20014: ff 23 00 00 lw $25, 0($3)
+# EB-NEXT: 20018: 05 35 subu16 $2, $2, $3
+# EB-NEXT: 2001a: 25 25 srl16 $2, $2, 2
+# EB-NEXT: 2001c: 33 02 ff fe addiu $24, $2, -2
+# EB-NEXT: 20020: 0d ff move $15, $ra
+# EB-NEXT: 20022: 45 f9 jalrs16 $25
+# EB-NEXT: 20024: 0f 83 move $gp, $3
+# EB-NEXT: 20026: 0c 00 nop
+# EB-NEXT: 20028: 00 00 00 00 nop
+# EB-NEXT: 2002c: 00 00 00 00 nop
+
+# EB-NEXT: 20030: 79 00 3f f7 addiupc $2, 65500
+# EB-NEXT: 20034: ff 22 00 00 lw $25, 0($2)
+# EB-NEXT: 20038: 45 99 jr16 $25
+# EB-NEXT: 2003a: 0f 02 move $24, $2
+
+# EL: Disassembly of section .plt:
+# EL-NEXT: .plt:
+# EL-NEXT: 20010: 80 79 fd 3f addiupc $3, 65524
+# EL-NEXT: 20014: 23 ff 00 00 lw $25, 0($3)
+# EL-NEXT: 20018: 35 05 subu16 $2, $2, $3
+# EL-NEXT: 2001a: 25 25 srl16 $2, $2, 2
+# EL-NEXT: 2001c: 02 33 fe ff addiu $24, $2, -2
+# EL-NEXT: 20020: ff 0d move $15, $ra
+# EL-NEXT: 20022: f9 45 jalrs16 $25
+# EL-NEXT: 20024: 83 0f move $gp, $3
+# EL-NEXT: 20026: 00 0c nop
+# EL-NEXT: 20028: 00 00 00 00 nop
+# EL-NEXT: 2002c: 00 00 00 00 nop
+
+# EL-NEXT: 20030: 00 79 f7 3f addiupc $2, 65500
+# EL-NEXT: 20034: 22 ff 00 00 lw $25, 0($2)
+# EL-NEXT: 20038: 99 45 jr16 $25
+# EL-NEXT: 2003a: 02 0f move $24, $2
+
+# EBR6: Disassembly of section .plt:
+# EBR6-NEXT: .plt:
+# EBR6-NEXT: 20010: 78 60 3f fd lapc $3, 65524
+# EBR6-NEXT: 20014: ff 23 00 00 lw $25, 0($3)
+# EBR6-NEXT: 20018: 05 35 subu16 $2, $2, $3
+# EBR6-NEXT: 2001a: 25 25 srl16 $2, $2, 2
+# EBR6-NEXT: 2001c: 33 02 ff fe addiu $24, $2, -2
+# EBR6-NEXT: 20020: 0d ff move16 $15, $ra
+# EBR6-NEXT: 20022: 0f 83 move16 $gp, $3
+# EBR6-NEXT: 20024: 47 2b jalr $25
+
+# EBR6: 20030: 78 40 3f f7 lapc $2, 65500
+# EBR6-NEXT: 20034: ff 22 00 00 lw $25, 0($2)
+# EBR6-NEXT: 20038: 0f 02 move16 $24, $2
+# EBR6-NEXT: 2003a: 47 23 jrc16 $25
+
+# ELR6: Disassembly of section .plt:
+# ELR6-NEXT: .plt:
+# ELR6-NEXT: 20010: 60 78 fd 3f lapc $3, 65524
+# ELR6-NEXT: 20014: 23 ff 00 00 lw $25, 0($3)
+# ELR6-NEXT: 20018: 35 05 subu16 $2, $2, $3
+# ELR6-NEXT: 2001a: 25 25 srl16 $2, $2, 2
+# ELR6-NEXT: 2001c: 02 33 fe ff addiu $24, $2, -2
+# ELR6-NEXT: 20020: ff 0d move16 $15, $ra
+# ELR6-NEXT: 20022: 83 0f move16 $gp, $3
+# ELR6-NEXT: 20024: 2b 47 jalr $25
+
+# ELR6: 20030: 40 78 f7 3f lapc $2, 65500
+# ELR6-NEXT: 20034: 22 ff 00 00 lw $25, 0($2)
+# ELR6-NEXT: 20038: 02 0f move16 $24, $2
+# ELR6-NEXT: 2003a: 23 47 jrc16 $25
+
+# MIXED: Disassembly of section .plt:
+# MIXED-NEXT: .plt:
+# MIXED-NEXT: 20020: 79 80 3f f9 addiupc $3, 65508
+# MIXED-NEXT: 20024: ff 23 00 00 lw $25, 0($3)
+# MIXED-NEXT: 20028: 05 35 subu16 $2, $2, $3
+# MIXED-NEXT: 2002a: 25 25 srl16 $2, $2, 2
+# MIXED-NEXT: 2002c: 33 02 ff fe addiu $24, $2, -2
+# MIXED-NEXT: 20030: 0d ff move $15, $ra
+# MIXED-NEXT: 20032: 45 f9 jalrs16 $25
+# MIXED-NEXT: 20034: 0f 83 move $gp, $3
+# MIXED-NEXT: 20036: 0c 00 nop
+# MIXED-NEXT: 20038: 00 00 00 00 nop
+# MIXED-NEXT: 2003c: 00 00 00 00 nop
+
+# MIXED-NEXT: 20040: 79 00 3f f3 addiupc $2, 65484
+# MIXED-NEXT: 20044: ff 22 00 00 lw $25, 0($2)
+# MIXED-NEXT: 20048: 45 99 jr16 $25
+# MIXED-NEXT: 2004a: 0f 02 move $24, $2
+
+# PLT: Entries [
+# PLT-NEXT: Entry {
+# PLT-NEXT: Address: 0x3000C
+# ^ 0x20030 + 65500
+# PLT-NEXT: Initial:
+# PLT-NEXT: Value: 0x0
+# PLT-NEXT: Type: Function
+# PLT-NEXT: Section: Undefined
+# PLT-NEXT: Name: foo
+# PLT-NEXT: }
+# PLT-NEXT: ]
+
+ .text
+ .set micromips
+ .global __start
+__start:
+ jal foo
diff --git a/test/ELF/mips-micro-relocs.s b/test/ELF/mips-micro-relocs.s
new file mode 100644
index 000000000..f018b9ae6
--- /dev/null
+++ b/test/ELF/mips-micro-relocs.s
@@ -0,0 +1,61 @@
+# Check handling of microMIPS relocations.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mattr=micromips %S/Inputs/mips-micro.s -o %t1eb.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mattr=micromips %s -o %t2eb.o
+# RUN: ld.lld -o %teb.exe %t1eb.o %t2eb.o
+# RUN: llvm-objdump -d -t -mattr=micromips %teb.exe \
+# RUN: | FileCheck --check-prefixes=EB,SYM %s
+
+# RUN: llvm-mc -filetype=obj -triple=mipsel-unknown-linux \
+# RUN: -mattr=micromips %S/Inputs/mips-micro.s -o %t1el.o
+# RUN: llvm-mc -filetype=obj -triple=mipsel-unknown-linux \
+# RUN: -mattr=micromips %s -o %t2el.o
+# RUN: ld.lld -o %tel.exe %t1el.o %t2el.o
+# RUN: llvm-objdump -d -t -mattr=micromips %tel.exe \
+# RUN: | FileCheck --check-prefixes=EL,SYM %s
+
+# REQUIRES: mips
+
+# Check disassembled code when LLD starts to setup
+# the least-significant bit for microMIPS symbols.
+# EB: __start:
+# EB-NEXT: 20010: 41 a3 00 01 lui $3, 1
+# EB-NEXT: 20014: 30 63 7f e0 addiu $3, $3, 32736
+# EB-NEXT: 20018: fc 7c 80 18 lw $3, -32744($gp)
+# EB-NEXT: 2001c: fc 63 80 18 lw $3, -32744($3)
+# EB-NEXT: 20020: 8f 70 beqz16 $6, -32
+# EB-NEXT: 20022: 00 7e 00 00 sll $3, $fp, 0
+# EB-NEXT: 20026: cf ec b16 -40
+# EB-NEXT: 20028: 00 00 00 00 nop
+# EB-NEXT: 2002c: 94 00 ff e8 b -44
+
+# EL: __start:
+# EL-NEXT: 20010: a3 41 01 00 lui $3, 1
+# EL-NEXT: 20014: 63 30 e0 7f addiu $3, $3, 32736
+# EL-NEXT: 20018: 7c fc 18 80 lw $3, -32744($gp)
+# EL-NEXT: 2001c: 63 fc 18 80 lw $3, -32744($3)
+# EL-NEXT: 20020: 70 8f beqz16 $6, -32
+# EL-NEXT: 20022: 7e 00 00 00 sll $3, $fp, 0
+# EL-NEXT: 20026: ec cf b16 -40
+# EL-NEXT: 20028: 00 00 00 00 nop
+# EL-NEXT: 2002c: 00 94 e8 ff b -44
+
+# SYM: 00037ff0 .got 00000000 .hidden _gp
+# SYM: 00020000 g F .text 00000000 foo
+# SYM: 00020010 .text 00000000 __start
+
+ .text
+ .set micromips
+ .global __start
+__start:
+ lui $3, %hi(_gp_disp) # R_MICROMIPS_HI16
+ addiu $3, $3, %lo(_gp_disp) # R_MICROMIPS_LO16
+
+ lw $3, %call16(foo)($gp) # R_MICROMIPS_CALL16
+ lw $3, %got(foo)($3) # R_MICROMIPS_GOT16
+
+ beqz16 $6, foo # R_MICROMIPS_PC7_S1
+ b16 foo # R_MICROMIPS_PC10_S1
+ b foo # R_MICROMIPS_PC16_S1
diff --git a/test/ELF/mips-micro-thunks.s b/test/ELF/mips-micro-thunks.s
new file mode 100644
index 000000000..d536ae3a3
--- /dev/null
+++ b/test/ELF/mips-micro-thunks.s
@@ -0,0 +1,47 @@
+# Check microMIPS thunk generation.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mattr=micromips %s -o %t-eb.o
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -position-independent -mattr=micromips \
+# RUN: %S/Inputs/mips-micro.s -o %t-eb-pic.o
+# RUN: ld.lld -o %t-eb.exe %t-eb.o %t-eb-pic.o
+# RUN: llvm-objdump -d -mattr=+micromips %t-eb.exe \
+# RUN: | FileCheck --check-prefix=EB %s
+
+# RUN: llvm-mc -filetype=obj -triple=mipsel-unknown-linux \
+# RUN: -mattr=micromips %s -o %t-el.o
+# RUN: llvm-mc -filetype=obj -triple=mipsel-unknown-linux \
+# RUN: -position-independent -mattr=micromips \
+# RUN: %S/Inputs/mips-micro.s -o %t-el-pic.o
+# RUN: ld.lld -o %t-el.exe %t-el.o %t-el-pic.o
+# RUN: llvm-objdump -d -mattr=+micromips %t-el.exe \
+# RUN: | FileCheck --check-prefix=EL %s
+
+# REQUIRES: mips
+
+# EB: __start:
+# EB-NEXT: 20000: f4 01 00 04 jal 131080 <__microLA25Thunk_foo>
+# EB-NEXT: 20004: 00 00 00 00 nop
+
+# EB: __microLA25Thunk_foo:
+# EB-NEXT: 20008: 41 b9 00 02 lui $25, 2
+# EB-NEXT: 2000c: d4 01 00 10 j 131104
+# EB-NEXT: 20010: 33 39 00 20 addiu $25, $25, 32
+# EB-NEXT: 20014: 0c 00 nop
+
+# EL: __start:
+# EL-NEXT: 20000: 01 f4 04 00 jal 131080 <__microLA25Thunk_foo>
+# EL-NEXT: 20004: 00 00 00 00 nop
+
+# EL: __microLA25Thunk_foo:
+# EL-NEXT: 20008: b9 41 02 00 lui $25, 2
+# EL-NEXT: 2000c: 01 d4 10 00 j 131104
+# EL-NEXT: 20010: 39 33 20 00 addiu $25, $25, 32
+# EL-NEXT: 20014: 00 0c nop
+
+ .text
+ .set micromips
+ .global __start
+__start:
+ jal foo
diff --git a/test/ELF/mips-n32-rels.s b/test/ELF/mips-n32-rels.s
index 7706e2591..5a3aab2d8 100644
--- a/test/ELF/mips-n32-rels.s
+++ b/test/ELF/mips-n32-rels.s
@@ -42,7 +42,7 @@
# ^-- loc
# CHECK: 00020004 .text 00000000 loc
-# CHECK: 00037ff0 *ABS* 00000000 .hidden _gp
+# CHECK: 00037ff0 .got 00000000 .hidden _gp
# CHECK: 00020000 g F .text 00000000 __start
# ELF: Format: ELF32-mips
diff --git a/test/ELF/mips-npic-call-pic-os.s b/test/ELF/mips-npic-call-pic-os.s
index 131289e59..aea0fa122 100644
--- a/test/ELF/mips-npic-call-pic-os.s
+++ b/test/ELF/mips-npic-call-pic-os.s
@@ -33,8 +33,8 @@
# CHECK-NEXT: 2002c: 08 00 80 10 j 131136 <foo2>
# CHECK-NEXT: 20030: 27 39 00 40 addiu $25, $25, 64
# CHECK-NEXT: 20034: 00 00 00 00 nop
-# CHECK-NEXT: 20038: 00 00 00 00 nop
-# CHECK-NEXT: 2003c: 00 00 00 00 nop
+# CHECK-NEXT: 20038: ef ef ef ef <unknown>
+# CHECK-NEXT: 2003c: ef ef ef ef <unknown>
# CHECK: foo2:
# CHECK-NEXT: 20040: 00 00 00 00 nop
# CHECK: __LA25Thunk_fpic:
@@ -42,14 +42,14 @@
# CHECK-NEXT: 20048: 08 00 80 18 j 131168 <fpic>
# CHECK-NEXT: 2004c: 27 39 00 60 addiu $25, $25, 96
# CHECK-NEXT: 20050: 00 00 00 00 nop
-# CHECK-NEXT: 20054: 00 00 00 00 nop
-# CHECK-NEXT: 20058: 00 00 00 00 nop
-# CHECK-NEXT: 2005c: 00 00 00 00 nop
+# CHECK-NEXT: 20054: ef ef ef ef <unknown>
+# CHECK-NEXT: 20058: ef ef ef ef <unknown>
+# CHECK-NEXT: 2005c: ef ef ef ef <unknown>
# CHECK: fpic:
# CHECK-NEXT: 20060: 00 00 00 00 nop
-# CHECK-NEXT: 20064: 00 00 00 00 nop
-# CHECK-NEXT: 20068: 00 00 00 00 nop
-# CHECK-NEXT: 2006c: 00 00 00 00 nop
+# CHECK-NEXT: 20064: ef ef ef ef <unknown>
+# CHECK-NEXT: 20068: ef ef ef ef <unknown>
+# CHECK-NEXT: 2006c: ef ef ef ef <unknown>
# CHECK: fnpic:
# CHECK-NEXT: 20070: 00 00 00 00 nop
# CHECK-NEXT: Disassembly of section differentos:
@@ -93,13 +93,13 @@
# REVERSE-NEXT: 2002c: 08 00 80 10 j 131136 <foo2>
# REVERSE-NEXT: 20030: 27 39 00 40 addiu $25, $25, 64
# REVERSE-NEXT: 20034: 00 00 00 00 nop
-# REVERSE-NEXT: 20038: 00 00 00 00 nop
-# REVERSE-NEXT: 2003c: 00 00 00 00 nop
+# REVERSE-NEXT: 20038: ef ef ef ef <unknown>
+# REVERSE-NEXT: 2003c: ef ef ef ef <unknown>
# REVERSE: foo2:
# REVERSE-NEXT: 20040: 00 00 00 00 nop
-# REVERSE-NEXT: 20044: 00 00 00 00 nop
-# REVERSE-NEXT: 20048: 00 00 00 00 nop
-# REVERSE-NEXT: 2004c: 00 00 00 00 nop
+# REVERSE-NEXT: 20044: ef ef ef ef <unknown>
+# REVERSE-NEXT: 20048: ef ef ef ef <unknown>
+# REVERSE-NEXT: 2004c: ef ef ef ef <unknown>
# REVERSE: __LA25Thunk_fpic:
# REVERSE-NEXT: 20050: 3c 19 00 02 lui $25, 2
# REVERSE-NEXT: 20054: 08 00 80 18 j 131168 <fpic>
@@ -107,9 +107,9 @@
# REVERSE-NEXT: 2005c: 00 00 00 00 nop
# REVERSE: fpic:
# REVERSE-NEXT: 20060: 00 00 00 00 nop
-# REVERSE-NEXT: 20064: 00 00 00 00 nop
-# REVERSE-NEXT: 20068: 00 00 00 00 nop
-# REVERSE-NEXT: 2006c: 00 00 00 00 nop
+# REVERSE-NEXT: 20064: ef ef ef ef <unknown>
+# REVERSE-NEXT: 20068: ef ef ef ef <unknown>
+# REVERSE-NEXT: 2006c: ef ef ef ef <unknown>
# REVERSE: fnpic:
# REVERSE-NEXT: 20070: 00 00 00 00 nop
# REVERSE-NEXT: Disassembly of section differentos:
diff --git a/test/ELF/mips-npic-call-pic-script.s b/test/ELF/mips-npic-call-pic-script.s
index 6028989ee..230704459 100644
--- a/test/ELF/mips-npic-call-pic-script.s
+++ b/test/ELF/mips-npic-call-pic-script.s
@@ -33,77 +33,77 @@
# CHECK-NEXT: 2002c: 08 00 80 10 j 131136 <foo2>
# CHECK-NEXT: 20030: 27 39 00 40 addiu $25, $25, 64
# CHECK-NEXT: 20034: 00 00 00 00 nop
-# CHECK-NEXT: 20038: 00 00 00 00 nop
-# CHECK-NEXT: 2003c: 00 00 00 00 nop
+# CHECK-NEXT: 20038: ef ef ef ef <unknown>
+# CHECK-NEXT: 2003c: ef ef ef ef <unknown>
# CHECK: foo2:
# CHECK-NEXT: 20040: 00 00 00 00 nop
-# CHECK-NEXT: 20044: 00 00 00 00 nop
-# CHECK-NEXT: 20048: 00 00 00 00 nop
-# CHECK-NEXT: 2004c: 00 00 00 00 nop
-# CHECK-NEXT: 20050: 00 00 00 00 nop
-# CHECK-NEXT: 20054: 00 00 00 00 nop
-# CHECK-NEXT: 20058: 00 00 00 00 nop
-# CHECK-NEXT: 2005c: 00 00 00 00 nop
-# CHECK-NEXT: 20060: 00 00 00 00 nop
-# CHECK-NEXT: 20064: 00 00 00 00 nop
-# CHECK-NEXT: 20068: 00 00 00 00 nop
-# CHECK-NEXT: 2006c: 00 00 00 00 nop
-# CHECK-NEXT: 20070: 00 00 00 00 nop
-# CHECK-NEXT: 20074: 00 00 00 00 nop
-# CHECK-NEXT: 20078: 00 00 00 00 nop
-# CHECK-NEXT: 2007c: 00 00 00 00 nop
-# 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-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: 00 00 00 00 nop
-# CHECK-NEXT: 200a4: 00 00 00 00 nop
-# CHECK-NEXT: 200a8: 00 00 00 00 nop
-# CHECK-NEXT: 200ac: 00 00 00 00 nop
-# CHECK-NEXT: 200b0: 00 00 00 00 nop
-# CHECK-NEXT: 200b4: 00 00 00 00 nop
-# CHECK-NEXT: 200b8: 00 00 00 00 nop
-# CHECK-NEXT: 200bc: 00 00 00 00 nop
-# CHECK-NEXT: 200c0: 00 00 00 00 nop
-# CHECK-NEXT: 200c4: 00 00 00 00 nop
-# CHECK-NEXT: 200c8: 00 00 00 00 nop
-# CHECK-NEXT: 200cc: 00 00 00 00 nop
-# CHECK-NEXT: 200d0: 00 00 00 00 nop
-# CHECK-NEXT: 200d4: 00 00 00 00 nop
-# CHECK-NEXT: 200d8: 00 00 00 00 nop
-# CHECK-NEXT: 200dc: 00 00 00 00 nop
-# CHECK-NEXT: 200e0: 00 00 00 00 nop
-# CHECK-NEXT: 200e4: 00 00 00 00 nop
-# CHECK-NEXT: 200e8: 00 00 00 00 nop
-# CHECK-NEXT: 200ec: 00 00 00 00 nop
-# CHECK-NEXT: 200f0: 00 00 00 00 nop
-# CHECK-NEXT: 200f4: 00 00 00 00 nop
-# CHECK-NEXT: 200f8: 00 00 00 00 nop
-# CHECK-NEXT: 200fc: 00 00 00 00 nop
-# CHECK-NEXT: 20100: 00 00 00 00 nop
-# CHECK-NEXT: 20104: 00 00 00 00 nop
-# CHECK-NEXT: 20108: 00 00 00 00 nop
-# CHECK-NEXT: 2010c: 00 00 00 00 nop
-# CHECK-NEXT: 20110: 00 00 00 00 nop
-# CHECK-NEXT: 20114: 00 00 00 00 nop
-# CHECK-NEXT: 20118: 00 00 00 00 nop
-# CHECK-NEXT: 2011c: 00 00 00 00 nop
-# CHECK-NEXT: 20120: 00 00 00 00 nop
-# CHECK-NEXT: 20124: 00 00 00 00 nop
-# CHECK-NEXT: 20128: 00 00 00 00 nop
-# CHECK-NEXT: 2012c: 00 00 00 00 nop
-# CHECK-NEXT: 20130: 00 00 00 00 nop
-# CHECK-NEXT: 20134: 00 00 00 00 nop
-# CHECK-NEXT: 20138: 00 00 00 00 nop
-# CHECK-NEXT: 2013c: 00 00 00 00 nop
-# CHECK-NEXT: 20140: 00 00 00 00 nop
-# CHECK-NEXT: 20144: 00 00 00 00 nop
-# CHECK-NEXT: 20148: 00 00 00 00 nop
-# CHECK-NEXT: 2014c: 00 00 00 00 nop
+# CHECK-NEXT: 20044: ef ef ef ef <unknown>
+# CHECK-NEXT: 20048: ef ef ef ef <unknown>
+# CHECK-NEXT: 2004c: ef ef ef ef <unknown>
+# CHECK-NEXT: 20050: ef ef ef ef <unknown>
+# CHECK-NEXT: 20054: ef ef ef ef <unknown>
+# CHECK-NEXT: 20058: ef ef ef ef <unknown>
+# CHECK-NEXT: 2005c: ef ef ef ef <unknown>
+# CHECK-NEXT: 20060: ef ef ef ef <unknown>
+# CHECK-NEXT: 20064: ef ef ef ef <unknown>
+# CHECK-NEXT: 20068: ef ef ef ef <unknown>
+# CHECK-NEXT: 2006c: ef ef ef ef <unknown>
+# CHECK-NEXT: 20070: ef ef ef ef <unknown>
+# CHECK-NEXT: 20074: ef ef ef ef <unknown>
+# CHECK-NEXT: 20078: ef ef ef ef <unknown>
+# CHECK-NEXT: 2007c: ef ef ef ef <unknown>
+# CHECK-NEXT: 20080: ef ef ef ef <unknown>
+# CHECK-NEXT: 20084: ef ef ef ef <unknown>
+# CHECK-NEXT: 20088: ef ef ef ef <unknown>
+# CHECK-NEXT: 2008c: ef ef ef ef <unknown>
+# CHECK-NEXT: 20090: ef ef ef ef <unknown>
+# CHECK-NEXT: 20094: ef ef ef ef <unknown>
+# CHECK-NEXT: 20098: ef ef ef ef <unknown>
+# CHECK-NEXT: 2009c: ef ef ef ef <unknown>
+# CHECK-NEXT: 200a0: ef ef ef ef <unknown>
+# CHECK-NEXT: 200a4: ef ef ef ef <unknown>
+# CHECK-NEXT: 200a8: ef ef ef ef <unknown>
+# CHECK-NEXT: 200ac: ef ef ef ef <unknown>
+# CHECK-NEXT: 200b0: ef ef ef ef <unknown>
+# CHECK-NEXT: 200b4: ef ef ef ef <unknown>
+# CHECK-NEXT: 200b8: ef ef ef ef <unknown>
+# CHECK-NEXT: 200bc: ef ef ef ef <unknown>
+# CHECK-NEXT: 200c0: ef ef ef ef <unknown>
+# CHECK-NEXT: 200c4: ef ef ef ef <unknown>
+# CHECK-NEXT: 200c8: ef ef ef ef <unknown>
+# CHECK-NEXT: 200cc: ef ef ef ef <unknown>
+# CHECK-NEXT: 200d0: ef ef ef ef <unknown>
+# CHECK-NEXT: 200d4: ef ef ef ef <unknown>
+# CHECK-NEXT: 200d8: ef ef ef ef <unknown>
+# CHECK-NEXT: 200dc: ef ef ef ef <unknown>
+# CHECK-NEXT: 200e0: ef ef ef ef <unknown>
+# CHECK-NEXT: 200e4: ef ef ef ef <unknown>
+# CHECK-NEXT: 200e8: ef ef ef ef <unknown>
+# CHECK-NEXT: 200ec: ef ef ef ef <unknown>
+# CHECK-NEXT: 200f0: ef ef ef ef <unknown>
+# CHECK-NEXT: 200f4: ef ef ef ef <unknown>
+# CHECK-NEXT: 200f8: ef ef ef ef <unknown>
+# CHECK-NEXT: 200fc: ef ef ef ef <unknown>
+# CHECK-NEXT: 20100: ef ef ef ef <unknown>
+# CHECK-NEXT: 20104: ef ef ef ef <unknown>
+# CHECK-NEXT: 20108: ef ef ef ef <unknown>
+# CHECK-NEXT: 2010c: ef ef ef ef <unknown>
+# CHECK-NEXT: 20110: ef ef ef ef <unknown>
+# CHECK-NEXT: 20114: ef ef ef ef <unknown>
+# CHECK-NEXT: 20118: ef ef ef ef <unknown>
+# CHECK-NEXT: 2011c: ef ef ef ef <unknown>
+# CHECK-NEXT: 20120: ef ef ef ef <unknown>
+# CHECK-NEXT: 20124: ef ef ef ef <unknown>
+# CHECK-NEXT: 20128: ef ef ef ef <unknown>
+# CHECK-NEXT: 2012c: ef ef ef ef <unknown>
+# CHECK-NEXT: 20130: ef ef ef ef <unknown>
+# CHECK-NEXT: 20134: ef ef ef ef <unknown>
+# CHECK-NEXT: 20138: ef ef ef ef <unknown>
+# CHECK-NEXT: 2013c: ef ef ef ef <unknown>
+# CHECK-NEXT: 20140: ef ef ef ef <unknown>
+# CHECK-NEXT: 20144: ef ef ef ef <unknown>
+# CHECK-NEXT: 20148: ef ef ef ef <unknown>
+# CHECK-NEXT: 2014c: ef ef ef ef <unknown>
# CHECK: __start:
# CHECK-NEXT: 20150: 0c 00 80 00 jal 131072 <__LA25Thunk_foo1a>
# CHECK-NEXT: 20154: 00 00 00 00 nop
@@ -124,9 +124,9 @@
# CHECK-NEXT: 2018c: 00 00 00 00 nop
# CHECK: fpic:
# CHECK-NEXT: 20190: 00 00 00 00 nop
-# CHECK-NEXT: 20194: 00 00 00 00 nop
-# CHECK-NEXT: 20198: 00 00 00 00 nop
-# CHECK-NEXT: 2019c: 00 00 00 00 nop
+# CHECK-NEXT: 20194: ef ef ef ef <unknown>
+# CHECK-NEXT: 20198: ef ef ef ef <unknown>
+# CHECK-NEXT: 2019c: ef ef ef ef <unknown>
# CHECK: fnpic:
# CHECK-NEXT: 201a0: 00 00 00 00 nop
@@ -166,9 +166,9 @@ __start:
# ORPH1-NEXT: 2003c: 00 00 00 00 nop
# ORPH1: fpic:
# ORPH1-NEXT: 20040: 00 00 00 00 nop
-# ORPH1-NEXT: 20044: 00 00 00 00 nop
-# ORPH1-NEXT: 20048: 00 00 00 00 nop
-# ORPH1-NEXT: 2004c: 00 00 00 00 nop
+# ORPH1-NEXT: 20044: ef ef ef ef <unknown>
+# ORPH1-NEXT: 20048: ef ef ef ef <unknown>
+# ORPH1-NEXT: 2004c: ef ef ef ef <unknown>
# ORPH1: fnpic:
# ORPH1-NEXT: 20050: 00 00 00 00 nop
# ORPH1: __LA25Thunk_foo1a:
@@ -181,9 +181,9 @@ __start:
# ORPH1-NEXT: 20068: 08 00 80 21 j 131204 <foo1b>
# ORPH1-NEXT: 2006c: 27 39 00 84 addiu $25, $25, 132
# ORPH1-NEXT: 20070: 00 00 00 00 nop
-# ORPH1-NEXT: 20074: 00 00 00 00 nop
-# ORPH1-NEXT: 20078: 00 00 00 00 nop
-# ORPH1-NEXT: 2007c: 00 00 00 00 nop
+# ORPH1-NEXT: 20074: ef ef ef ef <unknown>
+# ORPH1-NEXT: 20078: ef ef ef ef <unknown>
+# ORPH1-NEXT: 2007c: ef ef ef ef <unknown>
# ORPH1: foo1a:
# ORPH1-NEXT: 20080: 00 00 00 00 nop
# ORPH1: foo1b:
@@ -193,8 +193,8 @@ __start:
# ORPH1-NEXT: 2008c: 08 00 80 28 j 131232 <foo2>
# ORPH1-NEXT: 20090: 27 39 00 a0 addiu $25, $25, 160
# ORPH1-NEXT: 20094: 00 00 00 00 nop
-# ORPH1-NEXT: 20098: 00 00 00 00 nop
-# ORPH1-NEXT: 2009c: 00 00 00 00 nop
+# ORPH1-NEXT: 20098: ef ef ef ef <unknown>
+# ORPH1-NEXT: 2009c: ef ef ef ef <unknown>
# ORPH1: foo2:
# ORPH1-NEXT: 200a0: 00 00 00 00 nop
@@ -224,9 +224,9 @@ __start:
# ORPH2-NEXT: 2003c: 00 00 00 00 nop
# ORPH2: fpic:
# ORPH2-NEXT: 20040: 00 00 00 00 nop
-# ORPH2-NEXT: 20044: 00 00 00 00 nop
-# ORPH2-NEXT: 20048: 00 00 00 00 nop
-# ORPH2-NEXT: 2004c: 00 00 00 00 nop
+# ORPH2-NEXT: 20044: ef ef ef ef <unknown>
+# ORPH2-NEXT: 20048: ef ef ef ef <unknown>
+# ORPH2-NEXT: 2004c: ef ef ef ef <unknown>
# ORPH2: fnpic:
# ORPH2-NEXT: 20050: 00 00 00 00 nop
# ORPH2-NEXT: Disassembly of section .text:
@@ -249,7 +249,7 @@ __start:
# ORPH2-NEXT: 2008c: 08 00 80 28 j 131232 <foo2>
# ORPH2-NEXT: 20090: 27 39 00 a0 addiu $25, $25, 160
# ORPH2-NEXT: 20094: 00 00 00 00 nop
-# ORPH2-NEXT: 20098: 00 00 00 00 nop
-# ORPH2-NEXT: 2009c: 00 00 00 00 nop
+# ORPH2-NEXT: 20098: ef ef ef ef <unknown>
+# ORPH2-NEXT: 2009c: ef ef ef ef <unknown>
# ORPH2: foo2:
# ORPH2-NEXT: 200a0: 00 00 00 00 nop
diff --git a/test/ELF/mips-npic-call-pic.s b/test/ELF/mips-npic-call-pic.s
index a5a99d719..c3c94d783 100644
--- a/test/ELF/mips-npic-call-pic.s
+++ b/test/ELF/mips-npic-call-pic.s
@@ -51,8 +51,8 @@
# CHECK-NEXT: 2005c: 08 00 80 1c j 131184 <foo2>
# CHECK-NEXT: 20060: 27 39 00 70 addiu $25, $25, 112
# CHECK-NEXT: 20064: 00 00 00 00 nop
-# CHECK-NEXT: 20068: 00 00 00 00 nop
-# CHECK-NEXT: 2006c: 00 00 00 00 nop
+# CHECK-NEXT: 20068: ef ef ef ef <unknown>
+# CHECK-NEXT: 2006c: ef ef ef ef <unknown>
# CHECK: foo2:
# CHECK-NEXT: 20070: 00 00 00 00 nop
@@ -62,15 +62,15 @@
# CHECK-NEXT: 20078: 08 00 80 24 j 131216 <fpic>
# CHECK-NEXT: 2007c: 27 39 00 90 addiu $25, $25, 144
# 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-NEXT: 20084: ef ef ef ef <unknown>
+# CHECK-NEXT: 20088: ef ef ef ef <unknown>
+# CHECK-NEXT: 2008c: ef ef ef ef <unknown>
# CHECK: fpic:
# 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: 20094: ef ef ef ef <unknown>
+# CHECK-NEXT: 20098: ef ef ef ef <unknown>
+# CHECK-NEXT: 2009c: ef ef ef ef <unknown>
# CHECK: fnpic:
# CHECK-NEXT: 200a0: 00 00 00 00 nop
@@ -101,13 +101,13 @@
# REVERSE-NEXT: 2002c: 08 00 80 10 j 131136 <foo2>
# REVERSE-NEXT: 20030: 27 39 00 40 addiu $25, $25, 64
# REVERSE-NEXT: 20034: 00 00 00 00 nop
-# REVERSE-NEXT: 20038: 00 00 00 00 nop
-# REVERSE-NEXT: 2003c: 00 00 00 00 nop
+# REVERSE-NEXT: 20038: ef ef ef ef <unknown>
+# REVERSE-NEXT: 2003c: ef ef ef ef <unknown>
# REVERSE: foo2:
# REVERSE-NEXT: 20040: 00 00 00 00 nop
-# REVERSE-NEXT: 20044: 00 00 00 00 nop
-# REVERSE-NEXT: 20048: 00 00 00 00 nop
-# REVERSE-NEXT: 2004c: 00 00 00 00 nop
+# REVERSE-NEXT: 20044: ef ef ef ef <unknown>
+# REVERSE-NEXT: 20048: ef ef ef ef <unknown>
+# REVERSE-NEXT: 2004c: ef ef ef ef <unknown>
# REVERSE: __start:
# REVERSE-NEXT: 20050: 0c 00 80 00 jal 131072 <__LA25Thunk_foo1a>
# REVERSE-NEXT: 20054: 00 00 00 00 nop
@@ -128,9 +128,9 @@
# REVERSE-NEXT: 2008c: 00 00 00 00 nop
# REVERSE: fpic:
# 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: 20094: ef ef ef ef <unknown>
+# REVERSE-NEXT: 20098: ef ef ef ef <unknown>
+# REVERSE-NEXT: 2009c: ef ef ef ef <unknown>
# REVERSE: fnpic:
# REVERSE-NEXT: 200a0: 00 00 00 00 nop
diff --git a/test/ELF/mips-out-of-bounds-call16-reloc.s b/test/ELF/mips-out-of-bounds-call16-reloc.s
new file mode 100644
index 000000000..e5ac59c95
--- /dev/null
+++ b/test/ELF/mips-out-of-bounds-call16-reloc.s
@@ -0,0 +1,29 @@
+# Check that we create an error on an out-of-bounds R_MIPS_CALL_16
+
+# REQUIRES: mips
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t1.o
+# RUN: not ld.lld %t1.o -o %t.exe 2>&1 | FileCheck %s
+
+# CHECK: relocation R_MIPS_CALL16 out of range
+
+.macro generate_values
+ .irp i, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ .irp j, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ .irp k, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ .irp l, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ .text
+ .globl sym_\i\j\k\l
+ sym_\i\j\k\l:
+ nop
+ lw $25,%call16(sym_\i\j\k\l)($28)
+ .endr
+ .endr
+ .endr
+ .endr
+.endm
+
+generate_values
+
+.globl __start
+__start:
+ nop
diff --git a/test/ELF/mips-plt-r6.s b/test/ELF/mips-plt-r6.s
index ccf233cc7..4bab21c32 100644
--- a/test/ELF/mips-plt-r6.s
+++ b/test/ELF/mips-plt-r6.s
@@ -18,7 +18,7 @@
#
# CHECK-NEXT: Disassembly of section .plt:
# CHECK-NEXT: .plt:
-# CHECK-NEXT: 20010: 3c 1c 00 03 aui $zero, $gp, 3
+# CHECK-NEXT: 20010: 3c 1c 00 03 aui $gp, $zero, 3
# 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
@@ -27,7 +27,7 @@
# 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 03 aui $zero, $15, 3
+# CHECK-NEXT: 20030: 3c 0f 00 03 aui $15, $zero, 3
# 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
diff --git a/test/ELF/new-dtags.test b/test/ELF/new-dtags.test
index 334d47762..1ae328c6b 100644
--- a/test/ELF/new-dtags.test
+++ b/test/ELF/new-dtags.test
@@ -1,3 +1,4 @@
+# REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
// RUN: ld.lld %t.o -rpath=/somepath -shared --disable-new-dtags -o %t
// RUN: ld.lld %t.o -rpath=/somepath -shared --enable-new-dtags -o %t2
diff --git a/test/ELF/no-inhibit-exec.s b/test/ELF/no-inhibit-exec.s
index d0970d93f..afb7aed94 100644
--- a/test/ELF/no-inhibit-exec.s
+++ b/test/ELF/no-inhibit-exec.s
@@ -2,12 +2,16 @@
# RUN: not ld.lld %t -o %t2
# RUN: ld.lld %t --noinhibit-exec -o %t2
# RUN: llvm-objdump -d %t2 | FileCheck %s
+# RUN: llvm-readobj -r %t2 | FileCheck %s --check-prefix=RELOC
# REQUIRES: x86
# CHECK: Disassembly of section .text:
# CHECK-NEXT: _start
# CHECK-NEXT: 201000: {{.*}} callq -2101253
+# RELOC: Relocations [
+# RELOC-NEXT: ]
+
# next code will not link without noinhibit-exec flag
# because of undefined symbol _bar
.globl _start
diff --git a/test/ELF/no-obj.s b/test/ELF/no-obj.s
index eea10a45d..693cdf1e9 100644
--- a/test/ELF/no-obj.s
+++ b/test/ELF/no-obj.s
@@ -1,3 +1,4 @@
+// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
// RUN: llvm-ar rcs %t.a %t.o
// RUN: not ld.lld -o %t2 -u _start %t.a 2>&1 | FileCheck %s
diff --git a/test/ELF/no-soname.s b/test/ELF/no-soname.s
index d2c15ed8f..e3869ff5a 100644
--- a/test/ELF/no-soname.s
+++ b/test/ELF/no-soname.s
@@ -1,3 +1,4 @@
+// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
// RUN: mkdir -p %T/no-soname
// RUN: ld.lld %t.o -shared -o %T/no-soname/libfoo.so
@@ -5,26 +6,26 @@
// RUN: ld.lld %t.o %T/no-soname/libfoo.so -o %t
// RUN: llvm-readobj --dynamic-table %t | FileCheck %s
-// CHECK: 0x0000000000000001 NEEDED SharedLibrary ({{.*}}/no-soname/libfoo.so)
+// CHECK: 0x0000000000000001 NEEDED Shared library: [{{.*}}/no-soname/libfoo.so]
// CHECK-NOT: NEEDED
// RUN: ld.lld %t.o %T/no-soname/../no-soname/libfoo.so -o %t
// RUN: llvm-readobj --dynamic-table %t | FileCheck %s --check-prefix=CHECK2
-// CHECK2: 0x0000000000000001 NEEDED SharedLibrary ({{.*}}/no-soname/../no-soname/libfoo.so)
+// CHECK2: 0x0000000000000001 NEEDED Shared library: [{{.*}}/no-soname/../no-soname/libfoo.so]
// CHECK2-NOT: NEEDED
// RUN: ld.lld %t.o -L%T/no-soname/../no-soname -lfoo -o %t
// RUN: llvm-readobj --dynamic-table %t | FileCheck %s --check-prefix=CHECK3
-// CHECK3: 0x0000000000000001 NEEDED SharedLibrary (libfoo.so)
+// CHECK3: 0x0000000000000001 NEEDED Shared library: [libfoo.so]
// CHECK3-NOT: NEEDED
// RUN: ld.lld %t.o -shared -soname libbar.so -o %T/no-soname/libbar.so
// RUN: ld.lld %t.o %T/no-soname/libbar.so -o %t
// RUN: llvm-readobj --dynamic-table %t | FileCheck %s --check-prefix=CHECK4
-// CHECK4: 0x0000000000000001 NEEDED SharedLibrary (libbar.so)
+// CHECK4: 0x0000000000000001 NEEDED Shared library: [libbar.so]
// CHECK4-NOT: NEEDED
.global _start
diff --git a/test/ELF/no-symtab.s b/test/ELF/no-symtab.s
index 158bd727f..af9df13e9 100644
--- a/test/ELF/no-symtab.s
+++ b/test/ELF/no-symtab.s
@@ -1,3 +1,4 @@
+// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
// RUN: ld.lld %t.o %p/Inputs/no-symtab.o -o %t
.global _start
diff --git a/test/ELF/no-undefined.s b/test/ELF/no-undefined.s
index fa4d5e928..493a38987 100644
--- a/test/ELF/no-undefined.s
+++ b/test/ELF/no-undefined.s
@@ -1,3 +1,4 @@
+# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
# RUN: not ld.lld --no-undefined -shared %t -o %t.so
# RUN: ld.lld -shared %t -o %t1.so
diff --git a/test/ELF/non-abs-reloc.s b/test/ELF/non-abs-reloc.s
index ef9ba4466..454104cca 100644
--- a/test/ELF/non-abs-reloc.s
+++ b/test/ELF/non-abs-reloc.s
@@ -1,7 +1,7 @@
// 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
+// CHECK: {{.*}}:(.dummy+0x0): has non-ABS relocation R_X86_64_GOTPCREL against symbol 'foo'
.globl _start
_start:
diff --git a/test/ELF/noplt-pie.s b/test/ELF/noplt-pie.s
index 81e4410ac..7f6ccf23d 100644
--- a/test/ELF/noplt-pie.s
+++ b/test/ELF/noplt-pie.s
@@ -2,7 +2,7 @@
# 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: ld.lld %t1.o %t2.so -o %t.out
+# RUN: ld.lld --hash-style=sysv %t1.o %t2.so -o %t.out
# RUN: llvm-readobj -s -r %t.out | FileCheck %s
# CHECK: Section {
diff --git a/test/ELF/pie-weak.s b/test/ELF/pie-weak.s
index e74bcdfc0..c4d64e3ff 100644
--- a/test/ELF/pie-weak.s
+++ b/test/ELF/pie-weak.s
@@ -1,9 +1,13 @@
+# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -relax-relocations=false -triple=x86_64-unknown-linux %s -o %t.o
-# RUN: ld.lld -pie %t.o -o %t
+# RUN: ld.lld --hash-style=sysv -pie %t.o -o %t
# RUN: llvm-readobj -r %t | FileCheck --check-prefix=RELOCS %s
# RUN: llvm-objdump -d %t | FileCheck --check-prefix=DISASM %s
# RELOCS: Relocations [
+# RELOCS-NEXT: Section ({{.*}}) .rela.dyn {
+# RELOCS-NEXT: R_X86_64_GLOB_DAT foo 0x0
+# RELOCS-NEXT: }
# RELOCS-NEXT: ]
.weak foo
@@ -11,6 +15,6 @@
.globl _start
_start:
# DISASM: _start:
-# DISASM-NEXT: 1000: 48 8b 05 69 10 00 00 movq 4201(%rip), %rax
+# DISASM-NEXT: 1000: 48 8b 05 99 10 00 00 movq 4249(%rip), %rax
# ^ .got - (.text + 7)
mov foo@gotpcrel(%rip), %rax
diff --git a/test/ELF/pr34660.s b/test/ELF/pr34660.s
new file mode 100644
index 000000000..7c78bbc11
--- /dev/null
+++ b/test/ELF/pr34660.s
@@ -0,0 +1,25 @@
+# REQUIRES: aarch64
+
+# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-none %s -o %t.o
+# RUN: ld.lld --hash-style=sysv -shared %t.o -o %t
+# RUN: llvm-objdump %t -d | FileCheck %s --check-prefix=DISASM
+# RUN: llvm-readobj -elf-output-style=GNU %t -t | FileCheck %s --check-prefix=SYM
+
+# It would be much easier to understand/read this test if llvm-objdump would print
+# the immediates in hex.
+# IMM = hex(65540) = 0x10004
+# PC = 0x10000
+# As the relocation is PC-relative, IMM + PC = 0x20004 which is the VA of the
+# correct symbol.
+
+# DISASM: Disassembly of section .text:
+# DISASM-NEXT: $x.0:
+# DISASM-NEXT: 10000: 28 00 08 58 ldr x8, #65540
+
+# SYM: Symbol table '.symtab'
+# SYM: 0000000000020004 0 NOTYPE LOCAL DEFAULT 5 patatino
+
+ ldr x8, patatino
+ .data
+ .zero 4
+patatino:
diff --git a/test/ELF/pr34872.s b/test/ELF/pr34872.s
new file mode 100644
index 000000000..c656be2a9
--- /dev/null
+++ b/test/ELF/pr34872.s
@@ -0,0 +1,14 @@
+# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t.o
+# RUN: llvm-mc %p/Inputs/undefined-error.s -filetype=obj \
+# RUN: -triple=x86_64-pc-linux -o %t2.o
+# RUN: ld.lld -shared %t2.o -o %t2.so
+# RUN: not ld.lld %t2.so %t.o 2>&1 | FileCheck %s
+
+# CHECK: undefined symbol: fmod
+# Check we're not emitting other diagnostics for this symbol.
+# CHECK-NOT: fmod
+
+.global main
+
+main:
+ callq fmod
diff --git a/test/ELF/progname.s b/test/ELF/progname.s
index 6d91823c9..be8ab9e31 100644
--- a/test/ELF/progname.s
+++ b/test/ELF/progname.s
@@ -1,3 +1,4 @@
+// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
// RUN: echo .global __progname > %t2.s
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %t2.s -o %t2.o
diff --git a/test/ELF/relative-dynamic-reloc-pie.s b/test/ELF/relative-dynamic-reloc-pie.s
index 25b3e0ac2..f7c8b3c15 100644
--- a/test/ELF/relative-dynamic-reloc-pie.s
+++ b/test/ELF/relative-dynamic-reloc-pie.s
@@ -1,3 +1,4 @@
+# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
# RUN: ld.lld -pie %t.o -o %t.pie
# RUN: llvm-readobj -r -dyn-symbols %t.pie | FileCheck %s
diff --git a/test/ELF/relative-dynamic-reloc.s b/test/ELF/relative-dynamic-reloc.s
index 39382124e..0ed7e40f7 100644
--- a/test/ELF/relative-dynamic-reloc.s
+++ b/test/ELF/relative-dynamic-reloc.s
@@ -1,3 +1,4 @@
+// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
// RUN: ld.lld -shared %t.o -o %t.so
// RUN: llvm-readobj -t -r -dyn-symbols %t.so | FileCheck %s
diff --git a/test/ELF/relocatable-comdat2.s b/test/ELF/relocatable-comdat2.s
new file mode 100644
index 000000000..2643e645f
--- /dev/null
+++ b/test/ELF/relocatable-comdat2.s
@@ -0,0 +1,35 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld -r %t.o -o %t
+# RUN: llvm-readobj -elf-section-groups -s %t | FileCheck %s
+
+## Check .foo was not merged.
+# CHECK: Sections [
+# CHECK: Name: .foo
+# CHECK: Name: .foo
+# CHECK: Name: .foo
+
+# CHECK: Groups {
+# CHECK-NEXT: Group {
+# CHECK-NEXT: Name: .group
+# CHECK-NEXT: Index: 2
+# CHECK-NEXT: Type: COMDAT
+# CHECK-NEXT: Signature: bar
+# CHECK-NEXT: Section(s) in group [
+# CHECK-NEXT: .foo (3)
+# CHECK-NEXT: ]
+# CHECK-NEXT: }
+# CHECK-NEXT: Group {
+# CHECK-NEXT: Name: .group
+# CHECK-NEXT: Index: 4
+# CHECK-NEXT: Type: COMDAT
+# CHECK-NEXT: Signature: zed
+# CHECK-NEXT: Section(s) in group [
+# CHECK-NEXT: .foo (5)
+# CHECK-NEXT: ]
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+
+.section .foo,"axG",@progbits,bar,comdat
+.section .foo,"axG",@progbits,zed,comdat
+.section .foo,"ax",@progbits
diff --git a/test/ELF/relocatable-compressed-input.s b/test/ELF/relocatable-compressed-input.s
index bdfb2073a..3c0199c33 100644
--- a/test/ELF/relocatable-compressed-input.s
+++ b/test/ELF/relocatable-compressed-input.s
@@ -1,4 +1,4 @@
-# REQUIRES: zlib
+# REQUIRES: x86, zlib
# RUN: llvm-mc -compress-debug-sections=zlib-gnu -filetype=obj -triple=x86_64-unknown-linux %s -o %t1
# RUN: llvm-readobj -sections %t1 | FileCheck -check-prefix=GNU %s
diff --git a/test/ELF/relocatable-reloc.s b/test/ELF/relocatable-reloc.s
index c576073a7..7c699b9bf 100644
--- a/test/ELF/relocatable-reloc.s
+++ b/test/ELF/relocatable-reloc.s
@@ -1,3 +1,4 @@
+// REQUIRES: x86
// RUN: llvm-mc -filetype=obj %s -o %t.o -triple=x86_64-pc-linux
// RUN: ld.lld %t.o %t.o -r -o %t2.o
// RUN: llvm-readobj -r %t2.o | FileCheck %s
diff --git a/test/ELF/relocatable-script.s b/test/ELF/relocatable-script.s
new file mode 100644
index 000000000..133d61f48
--- /dev/null
+++ b/test/ELF/relocatable-script.s
@@ -0,0 +1,7 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux -o %t1.o %s
+# RUN: echo "SECTIONS { .foo : { BYTE(0x0) } }" > %t.script
+# RUN: ld.lld -r %t1.o -script %t.script -o %t2.o
+# RUN: llvm-readobj -sections %t2.o | FileCheck %s
+
+# CHECK: Name: .foo
diff --git a/test/ELF/relocatable-section-symbol.s b/test/ELF/relocatable-section-symbol.s
index 9ac3a91d2..57a75ab92 100644
--- a/test/ELF/relocatable-section-symbol.s
+++ b/test/ELF/relocatable-section-symbol.s
@@ -1,3 +1,4 @@
+# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
# RUN: ld.lld -r -o %t %t.o %t.o
# RUN: llvm-readobj -r %t | FileCheck --check-prefix=RELA %s
diff --git a/test/ELF/relocatable-sections.s b/test/ELF/relocatable-sections.s
index d6a922fba..75ede1372 100644
--- a/test/ELF/relocatable-sections.s
+++ b/test/ELF/relocatable-sections.s
@@ -1,3 +1,4 @@
+# REQUIRES: x86
# 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
diff --git a/test/ELF/relocatable-tls.s b/test/ELF/relocatable-tls.s
index 88d38c2ae..ff04dd2df 100644
--- a/test/ELF/relocatable-tls.s
+++ b/test/ELF/relocatable-tls.s
@@ -1,3 +1,4 @@
+# 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: %S/Inputs/relocatable-tls.s -o %t2.o
diff --git a/test/ELF/relocatable.s b/test/ELF/relocatable.s
index 00572d07c..7cb2a084c 100644
--- a/test/ELF/relocatable.s
+++ b/test/ELF/relocatable.s
@@ -81,7 +81,7 @@
# CHECKEXE-NEXT: Version: 1
# CHECKEXE-NEXT: Entry: 0x201000
# CHECKEXE-NEXT: ProgramHeaderOffset: 0x40
-# CHECKEXE-NEXT: SectionHeaderOffset: 0x11F8
+# CHECKEXE-NEXT: SectionHeaderOffset: 0x21A0
# CHECKEXE-NEXT: Flags [
# CHECKEXE-NEXT: ]
# CHECKEXE-NEXT: HeaderSize: 64
diff --git a/test/ELF/relocation-b-aarch64.test b/test/ELF/relocation-b-aarch64.test
new file mode 100644
index 000000000..24bf4b74e
--- /dev/null
+++ b/test/ELF/relocation-b-aarch64.test
@@ -0,0 +1,48 @@
+# REQUIRES: aarch64
+
+# RUN: yaml2obj %s -o %t.o
+# RUN: ld.lld %t.o -o %t.out
+# RUN: llvm-objdump -d -triple=aarch64-none-linux %t.out | FileCheck %s
+
+# Check that the R_AARCH64_JUMP26 writes the branch opcode as well as the
+# immediate. We use this property to overwrite instructions with a branch.
+
+# CHECK: Disassembly of section .text:
+# CHECK-NEXT: foo:
+# CHECK-NEXT: 20000: 01 00 00 14 b #4
+# CHECK: bar:
+# CHECK-NEXT: 20004: ff ff ff 17 b #-4
+
+!ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_AARCH64
+Sections:
+ - Type: SHT_PROGBITS
+ Name: .text
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Content: "0000000000000000"
+ - Type: SHT_RELA
+ Name: .rela.text
+ Link: .symtab
+ Info: .text
+ Relocations:
+ - Offset: 0
+ Symbol: bar
+ Type: R_AARCH64_JUMP26
+ - Offset: 4
+ Symbol: foo
+ Type: R_AARCH64_JUMP26
+
+Symbols:
+ Local:
+ - Type: STT_FUNC
+ Section: .text
+ Name: foo
+ Value: 0
+ - Type: STT_FUNC
+ Section: .text
+ Name: bar
+ Value: 4
diff --git a/test/ELF/relocation-copy-alias.s b/test/ELF/relocation-copy-alias.s
index 15712e39b..edb1550dd 100644
--- a/test/ELF/relocation-copy-alias.s
+++ b/test/ELF/relocation-copy-alias.s
@@ -1,8 +1,8 @@
// 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/relocation-copy-alias.s -o %t2.o
-// RUN: ld.lld -shared %t2.o -o %t.so
-// RUN: ld.lld %t.o %t.so -o %t3
+// RUN: ld.lld --hash-style=sysv -shared %t2.o -o %t.so
+// RUN: ld.lld --hash-style=sysv %t.o %t.so -o %t3
// RUN: llvm-readobj --dyn-symbols -r --expand-relocs %t3 | FileCheck %s
.global _start
diff --git a/test/ELF/relocation-copy-align-common.s b/test/ELF/relocation-copy-align-common.s
index a94c208a8..56eab76cb 100644
--- a/test/ELF/relocation-copy-align-common.s
+++ b/test/ELF/relocation-copy-align-common.s
@@ -3,7 +3,7 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux \
# RUN: %p/Inputs/relocation-copy-align-common.s -o %t2.o
# RUN: ld.lld -shared %t2.o -o %t.so
-# RUN: ld.lld %t.o %t.so -o %t3
+# RUN: ld.lld --hash-style=sysv %t.o %t.so -o %t3
# RUN: llvm-readobj -s -r --expand-relocs %t3 | FileCheck %s
# CHECK: Section {
diff --git a/test/ELF/relocation-copy-flags.s b/test/ELF/relocation-copy-flags.s
index 4d97e3d95..3f74bade3 100644
--- a/test/ELF/relocation-copy-flags.s
+++ b/test/ELF/relocation-copy-flags.s
@@ -3,7 +3,7 @@
// 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/relocation-copy.s -o %t2.o
// RUN: ld.lld %t2.o -o %t2.so -shared
-// RUN: ld.lld %t.o %t2.so -o %t.exe
+// RUN: ld.lld --hash-style=sysv %t.o %t2.so -o %t.exe
// RUN: llvm-readobj -s -section-data -r %t.exe | FileCheck %s
.global _start
diff --git a/test/ELF/relocation-copy-relro.s b/test/ELF/relocation-copy-relro.s
index 1684c409e..947c6c73f 100644
--- a/test/ELF/relocation-copy-relro.s
+++ b/test/ELF/relocation-copy-relro.s
@@ -2,7 +2,7 @@
// 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/relocation-copy-relro.s -o %t2.o
// RUN: ld.lld -shared %t2.o -o %t.so
-// RUN: ld.lld %t.o %t.so -o %t3
+// RUN: ld.lld --hash-style=sysv %t.o %t.so -o %t3
// RUN: llvm-readobj -program-headers -s -r %t3 | FileCheck %s
// CHECK: Name: .bss.rel.ro (48)
diff --git a/test/ELF/relocation-i686.s b/test/ELF/relocation-i686.s
index 4bb55d968..3986357d6 100644
--- a/test/ELF/relocation-i686.s
+++ b/test/ELF/relocation-i686.s
@@ -1,7 +1,7 @@
// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t
// 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: ld.lld %t %t2.so -o %t2
+// RUN: ld.lld --hash-style=sysv %t %t2.so -o %t2
// RUN: llvm-readobj -s %t2 | FileCheck --check-prefix=ADDR %s
// RUN: llvm-objdump -d %t2 | FileCheck %s
// REQUIRES: x86
diff --git a/test/ELF/relocation-relative-weak.s b/test/ELF/relocation-relative-weak.s
index c525012ac..7f28fced7 100644
--- a/test/ELF/relocation-relative-weak.s
+++ b/test/ELF/relocation-relative-weak.s
@@ -4,6 +4,7 @@
# RUN: llvm-readobj -dyn-relocations %t | FileCheck %s
# CHECK: Dynamic Relocations {
+# CHECK-NEXT: 0x2018 R_X86_64_JUMP_SLOT w 0x0
# CHECK-NEXT: }
.globl _start
diff --git a/test/ELF/relocation-shared.s b/test/ELF/relocation-shared.s
index e1850944c..4fba7a568 100644
--- a/test/ELF/relocation-shared.s
+++ b/test/ELF/relocation-shared.s
@@ -1,3 +1,4 @@
+// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
// RUN: ld.lld %t.o -shared -o %t.so
// RUN: llvm-readobj -r -s -section-data %t.so | FileCheck %s
diff --git a/test/ELF/relocation.s b/test/ELF/relocation.s
index 77c766960..3359a8bad 100644
--- a/test/ELF/relocation.s
+++ b/test/ELF/relocation.s
@@ -1,7 +1,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/shared.s -o %t2
// RUN: ld.lld %t2 -o %t2.so -shared
-// RUN: ld.lld %t %t2.so -o %t3
+// RUN: ld.lld --hash-style=sysv %t %t2.so -o %t3
// RUN: llvm-readobj -s %t3 | FileCheck --check-prefix=SEC %s
// RUN: llvm-objdump -s -d %t3 | FileCheck %s
// REQUIRES: x86
diff --git a/test/ELF/relro-omagic.s b/test/ELF/relro-omagic.s
index 6e9d59a35..97c3a8124 100644
--- a/test/ELF/relro-omagic.s
+++ b/test/ELF/relro-omagic.s
@@ -1,7 +1,7 @@
# 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 -soname relro-omagic.s.tmp2.so
-# RUN: ld.lld -N %t.o %t2.so -o %t
+# RUN: ld.lld --hash-style=sysv -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
diff --git a/test/ELF/reproduce-thin-archive.s b/test/ELF/reproduce-thin-archive.s
index 2de88d77f..c3e6e8875 100644
--- a/test/ELF/reproduce-thin-archive.s
+++ b/test/ELF/reproduce-thin-archive.s
@@ -5,11 +5,17 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.dir/foo.o
# RUN: cd %t.dir
# RUN: llvm-ar --format=gnu rcT foo.a foo.o
+
# RUN: ld.lld -m elf_x86_64 foo.a -o bar --reproduce repro.tar
# RUN: tar xf repro.tar
# RUN: diff foo.a repro/%:t.dir/foo.a
# RUN: diff foo.o repro/%:t.dir/foo.o
+# RUN: ld.lld -m elf_x86_64 --whole-archive foo.a -o bar --reproduce repro2.tar
+# RUN: tar xf repro2.tar
+# RUN: diff foo.a repro2/%:t.dir/foo.a
+# RUN: diff foo.o repro2/%:t.dir/foo.o
+
.globl _start
_start:
nop
diff --git a/test/ELF/reproduce.s b/test/ELF/reproduce.s
index 0a93be08d..a005cc017 100644
--- a/test/ELF/reproduce.s
+++ b/test/ELF/reproduce.s
@@ -33,27 +33,32 @@
# RUN: echo "{};" > dyn
# RUN: echo > file
# RUN: echo > file2
+# RUN: echo "_start" > order
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o 'foo bar'
# RUN: ld.lld --reproduce repro2.tar 'foo bar' -L"foo bar" -Lfile -Tfile2 \
-# RUN: --dynamic-list dyn -rpath file --script=file --version-script ver \
-# RUN: --dynamic-linker "some unusual/path" -soname 'foo bar' -soname='foo bar'
+# RUN: --dynamic-list dyn -rpath file --script=file --symbol-ordering-file order \
+# RUN: --version-script ver --dynamic-linker "some unusual/path" -soname 'foo bar' \
+# RUN: -soname='foo bar'
# RUN: tar xf repro2.tar
# RUN: FileCheck %s --check-prefix=RSP2 < repro2/response.txt
+# RSP2: --chroot .
# RSP2: "{{.*}}foo bar"
-# RSP2-NEXT: -L "{{.*}}foo bar"
-# RSP2-NEXT: -L {{.+}}file
+# RSP2-NEXT: --library-path "{{.*}}foo bar"
+# RSP2-NEXT: --library-path {{.+}}file
# RSP2-NEXT: --script {{.+}}file2
# RSP2-NEXT: --dynamic-list {{.+}}dyn
# RSP2-NEXT: -rpath {{.+}}file
# RSP2-NEXT: --script {{.+}}file
-# RSP2-NEXT: --version-script [[PATH:.*]]ver
+# RSP2-NEXT: --symbol-ordering-file [[PATH:.+]]order
+# RSP2-NEXT: --version-script [[PATH]]ver
# RSP2-NEXT: --dynamic-linker "some unusual/path"
-# RSP2-NEXT: -soname="foo bar"
-# RSP2-NEXT: -soname="foo bar"
+# RSP2-NEXT: -soname "foo bar"
+# RSP2-NEXT: -soname "foo bar"
# RUN: tar tf repro2.tar | FileCheck %s
# CHECK: repro2/response.txt
# CHECK-NEXT: repro2/version.txt
+# CHECK-NEXT: repro2/{{.*}}/order
# CHECK-NEXT: repro2/{{.*}}/dyn
# CHECK-NEXT: repro2/{{.*}}/ver
# CHECK-NEXT: repro2/{{.*}}/foo bar
diff --git a/test/ELF/resolution-end.s b/test/ELF/resolution-end.s
index aa1c999fb..26858372c 100644
--- a/test/ELF/resolution-end.s
+++ b/test/ELF/resolution-end.s
@@ -1,7 +1,7 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/resolution-end.s -o %t2.o
# RUN: ld.lld -shared -o %t2.so %t2.o
-# RUN: ld.lld %t1.o %t2.so -o %t
+# RUN: ld.lld --hash-style=sysv %t1.o %t2.so -o %t
# RUN: llvm-readobj -t -s -section-data %t | FileCheck %s
# REQUIRES: x86
diff --git a/test/ELF/retain-symbols-file.s b/test/ELF/retain-symbols-file.s
index aa7d35d91..79d569d69 100644
--- a/test/ELF/retain-symbols-file.s
+++ b/test/ELF/retain-symbols-file.s
@@ -2,11 +2,11 @@
# 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: ld.lld --hash-style=sysv -shared --retain-symbols-file=%t_retain.txt %t -o %t2
# RUN: llvm-readobj --dyn-symbols %t2 | FileCheck %s
## Check separate form.
-# RUN: ld.lld -shared --retain-symbols-file %t_retain.txt %t -o %t2
+# RUN: ld.lld --hash-style=sysv -shared --retain-symbols-file %t_retain.txt %t -o %t2
# RUN: llvm-readobj --dyn-symbols %t2 | FileCheck %s
# CHECK: DynamicSymbols [
diff --git a/test/ELF/section-metadata-err.s b/test/ELF/section-metadata-err.s
index f3b584294..1bcbedfba 100644
--- a/test/ELF/section-metadata-err.s
+++ b/test/ELF/section-metadata-err.s
@@ -3,7 +3,7 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
# RUN: not ld.lld %t.o -o %t 2>&1 | FileCheck %s
-# CHECK: error: Merge and .eh_frame sections are not supported with SHF_LINK_ORDER {{.*}}section-metadata-err.s.tmp.o:(.foo)
+# CHECK: error: a section with SHF_LINK_ORDER should not refer a non-regular section: {{.*}}section-metadata-err.s.tmp.o:(.foo)
.global _start
_start:
diff --git a/test/ELF/shared-be.s b/test/ELF/shared-be.s
index c969793d9..0b941d373 100644
--- a/test/ELF/shared-be.s
+++ b/test/ELF/shared-be.s
@@ -21,7 +21,7 @@
// CHECK: DynamicSection [
// CHECK-NEXT: Tag Type Name/Value
// CHECK-NEXT: 0x000000000000001D RUNPATH foo:bar
-// CHECK-NEXT: 0x0000000000000001 NEEDED SharedLibrary ({{.*}}2.so)
+// CHECK-NEXT: 0x0000000000000001 NEEDED Shared library: [{{.*}}2.so]
// CHECK-NEXT: 0x0000000000000015 DEBUG 0x0
// CHECK-NEXT: 0x0000000000000007 RELA [[RELADDR]]
// CHECK-NEXT: 0x0000000000000008 RELASZ [[RELSIZE]] (bytes)
diff --git a/test/ELF/shared-lazy.s b/test/ELF/shared-lazy.s
new file mode 100644
index 000000000..bc1e61c3c
--- /dev/null
+++ b/test/ELF/shared-lazy.s
@@ -0,0 +1,16 @@
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o
+// RUN: rm -f %t1.a
+// RUN: llvm-ar rc %t1.a %t1.o
+// RUN: ld.lld %t1.o -o %t1.so -shared
+// RUN: echo ".global foo" > %t2.s
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %t2.s -o %t2.o
+// RUN: ld.lld %t1.a %t1.so %t2.o -o %t.so -shared
+// RUN: llvm-readelf --dyn-symbols %t.so | FileCheck %s
+
+// Test that 'foo' from %t1.so is used and we don't fetch a member
+// from the archive.
+
+// CHECK: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND foo
+
+.global foo
+foo:
diff --git a/test/ELF/shared.s b/test/ELF/shared.s
index 350ef5c60..65ad2c613 100644
--- a/test/ELF/shared.s
+++ b/test/ELF/shared.s
@@ -1,10 +1,10 @@
// 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: ld.lld --hash-style=sysv -shared %t2.o -o %t2.so
// RUN: llvm-readobj -s %t2.so | FileCheck --check-prefix=SO %s
-// RUN: ld.lld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -rpath foo -rpath bar --export-dynamic %t.o %t2.so -o %t
+// RUN: ld.lld --hash-style=sysv -dynamic-linker /lib64/ld-linux-x86-64.so.2 -rpath foo -rpath bar --export-dynamic %t.o %t2.so -o %t
// RUN: llvm-readobj --program-headers --dynamic-table -t -s -dyn-symbols -section-data -hash-table %t | FileCheck %s
-// RUN: ld.lld %t.o %t2.so %t2.so -o %t2
+// RUN: ld.lld --hash-style=sysv %t.o %t2.so %t2.so -o %t2
// RUN: llvm-readobj -dyn-symbols %t2 | FileCheck --check-prefix=DONT_EXPORT %s
// REQUIRES: x86
@@ -254,7 +254,7 @@
// CHECK: DynamicSection [
// CHECK-NEXT: Tag Type Name/Value
// CHECK-NEXT: 0x0000001D RUNPATH foo:bar
-// CHECK-NEXT: 0x00000001 NEEDED SharedLibrary ({{.*}}2.so)
+// CHECK-NEXT: 0x00000001 NEEDED Shared library: [{{.*}}2.so]
// CHECK-NEXT: 0x00000015 DEBUG 0x0
// CHECK-NEXT: 0x00000011 REL [[RELADDR]]
// CHECK-NEXT: 0x00000012 RELSZ [[RELSIZE]] (bytes)
diff --git a/test/ELF/soname.s b/test/ELF/soname.s
index 65e95ce85..a26bb30f7 100644
--- a/test/ELF/soname.s
+++ b/test/ELF/soname.s
@@ -4,7 +4,7 @@
// RUN: ld.lld %t.o %t.so %t2.so -o %t
// RUN: llvm-readobj --dynamic-table %t | FileCheck %s
-// CHECK: 0x0000000000000001 NEEDED SharedLibrary (bar)
+// CHECK: 0x0000000000000001 NEEDED Shared library: [bar]
// CHECK-NOT: NEEDED
.global _start
diff --git a/test/ELF/soname2.s b/test/ELF/soname2.s
index d446766a7..9fb8da519 100644
--- a/test/ELF/soname2.s
+++ b/test/ELF/soname2.s
@@ -2,7 +2,7 @@
// RUN: ld.lld %t.o -shared -soname=foo.so -o %t
// RUN: llvm-readobj --dynamic-table %t | FileCheck %s
-// CHECK: 0x000000000000000E SONAME LibrarySoname (foo.so)
+// CHECK: 0x000000000000000E SONAME Library soname: [foo.so]
.global _start
_start:
diff --git a/test/ELF/sort-norosegment.s b/test/ELF/sort-norosegment.s
index c5a759a08..bd74f2eb3 100644
--- a/test/ELF/sort-norosegment.s
+++ b/test/ELF/sort-norosegment.s
@@ -1,7 +1,7 @@
# 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: ld.lld --hash-style=sysv -no-rosegment -o %t1 %t -shared
# RUN: llvm-readobj -elf-output-style=GNU -s %t1 | FileCheck %s
# CHECK: .text {{.*}} AX
diff --git a/test/ELF/startstop-gccollect.s b/test/ELF/startstop-gccollect.s
index daff08187..cb0009c54 100644
--- a/test/ELF/startstop-gccollect.s
+++ b/test/ELF/startstop-gccollect.s
@@ -11,21 +11,27 @@
# RUN: ld.lld %t --gc-sections -o %tout
# RUN: llvm-objdump -d %tout | FileCheck -check-prefix=DISASM %s
+# RUN: echo ".global __start_foo; __start_foo:" > %t2.s
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %t2.s -o %t2.o
+# RUN: ld.lld -shared %t2.o -o %t2.so
+# RUN: ld.lld %t --gc-sections -o %tout %t2.so
+# RUN: llvm-objdump -d %tout | FileCheck -check-prefix=DISASM %s
+
# DISASM: _start:
# DISASM-NEXT: 201000: e8 05 00 00 00 callq 5 <__start_foo>
-# DISASM-NEXT: 201005: e8 01 00 00 00 callq 1 <__start_bar>
+# DISASM-NEXT: 201005: e8 02 00 00 00 callq 2 <__stop_bar>
# DISASM-NEXT: Disassembly of section foo:
# DISASM-NEXT: __start_foo:
# DISASM-NEXT: 20100a: 90 nop
# DISASM-NEXT: Disassembly of section bar:
-# DISASM-NEXT: __start_bar:
+# DISASM-NEXT: bar:
# DISASM-NEXT: 20100b: 90 nop
.global _start
.text
_start:
callq __start_foo
- callq __start_bar
+ callq __stop_bar
.section foo,"ax"
nop
diff --git a/test/ELF/startstop.s b/test/ELF/startstop.s
index 250fb2cb3..1e75042a0 100644
--- a/test/ELF/startstop.s
+++ b/test/ELF/startstop.s
@@ -1,6 +1,6 @@
// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-// RUN: ld.lld %t -o %tout -shared
+// RUN: ld.lld --hash-style=sysv %t -o %tout -shared
// RUN: llvm-objdump -d %tout | FileCheck -check-prefix=DISASM %s
// RUN: llvm-readobj -symbols -r %tout | FileCheck -check-prefix=SYMBOL %s
diff --git a/test/ELF/string-gc.s b/test/ELF/string-gc.s
index 3157a79a6..96c75e923 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: 0x200125
+// CHECK-NEXT: Value: 0x200120
// 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: 0x200120
+// CHECK-NEXT: Value: 0x200125
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local (0x0)
// CHECK-NEXT: Type: Object (0x1)
diff --git a/test/ELF/symbol-ordering-file2.s b/test/ELF/symbol-ordering-file2.s
new file mode 100644
index 000000000..723eef1df
--- /dev/null
+++ b/test/ELF/symbol-ordering-file2.s
@@ -0,0 +1,21 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "bar" > %t.order
+# RUN: ld.lld --symbol-ordering-file %t.order -shared %t.o -o %t.so
+# RUN: llvm-nm %t.so | FileCheck %s
+
+# CHECK: 0000000000002000 d _DYNAMIC
+# CHECK-NEXT: 0000000000001000 T bar
+# CHECK-NEXT: 0000000000001004 T foo
+
+ .section .text.foo,"ax",@progbits
+ .align 4
+ .global foo
+foo:
+ retq
+
+ .section .text.bar,"ax",@progbits
+ .align 4
+ .global bar
+bar:
+ retq
diff --git a/test/ELF/symver-archive.s b/test/ELF/symver-archive.s
new file mode 100644
index 000000000..be50503a3
--- /dev/null
+++ b/test/ELF/symver-archive.s
@@ -0,0 +1,15 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1
+# RUN: rm -f %t.a
+# RUN: llvm-ar rcs %t.a %t1
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/symver-archive1.s -o %t2.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/symver-archive2.s -o %t3.o
+# RUN: ld.lld -o %t.out %t2.o %t3.o %t.a
+
+.text
+.globl x
+.type x, @function
+x:
+
+.globl xx
+xx = x
diff --git a/test/ELF/synthetic-got.s b/test/ELF/synthetic-got.s
index 8d82f3177..375a4387f 100644
--- a/test/ELF/synthetic-got.s
+++ b/test/ELF/synthetic-got.s
@@ -1,7 +1,7 @@
# 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: ld.lld --hash-style=sysv -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
@@ -16,7 +16,7 @@
# GOTDATA-NEXT: 01d0 00000000 00000000
# RUN: echo "SECTIONS { .mygot : { *(.got) *(.got.plt) } }" > %t1.script
-# RUN: ld.lld -shared %t.o -o %t1.out --script %t1.script
+# RUN: ld.lld --hash-style=sysv -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
diff --git a/test/ELF/sysroot.s b/test/ELF/sysroot.s
index 358b4d9df..6b40c7d39 100644
--- a/test/ELF/sysroot.s
+++ b/test/ELF/sysroot.s
@@ -28,6 +28,8 @@
// Should substitute SysRoot if specified
// RUN: ld.lld -o %t/r %t/m.o --sysroot=%t -L=lib -l:libls.a
// RUN: ld.lld -o %t/r %t/m.o --sysroot=%t -L=/lib -l:libls.a
+// Check alias.
+// RUN: ld.lld -o %t/r %t/m.o --sysroot %t -L=lib -l:libls.a
// Should not substitute SysRoot if the directory name does not start with '='
// RUN: not ld.lld -o %t/r %r/m.o --sysroot=%t -Llib -l:libls.a
diff --git a/test/ELF/tls-dynamic-i686.s b/test/ELF/tls-dynamic-i686.s
index ac88e6eae..1b13f26cc 100644
--- a/test/ELF/tls-dynamic-i686.s
+++ b/test/ELF/tls-dynamic-i686.s
@@ -1,6 +1,6 @@
// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t
-// RUN: ld.lld -shared %t -o %tout
+// RUN: ld.lld --hash-style=sysv -shared %t -o %tout
// RUN: llvm-readobj -sections -relocations %tout | FileCheck %s
// RUN: llvm-objdump -d %tout | FileCheck %s --check-prefix=DIS
diff --git a/test/ELF/tls-dynamic.s b/test/ELF/tls-dynamic.s
index 05473d4b5..167f76874 100644
--- a/test/ELF/tls-dynamic.s
+++ b/test/ELF/tls-dynamic.s
@@ -1,6 +1,6 @@
// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-// RUN: ld.lld -shared %t -o %tout
+// RUN: ld.lld --hash-style=sysv -shared %t -o %tout
// RUN: llvm-readobj -sections -relocations %tout | FileCheck %s
// RUN: llvm-objdump -d %tout | FileCheck %s --check-prefix=DIS
diff --git a/test/ELF/tls-got.s b/test/ELF/tls-got.s
index 450dd634d..b1686cd6d 100644
--- a/test/ELF/tls-got.s
+++ b/test/ELF/tls-got.s
@@ -1,7 +1,7 @@
// 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/tls-got.s -o %t2.o
// RUN: ld.lld -shared %t2.o -o %t2.so
-// RUN: ld.lld -e main %t1.o %t2.so -o %t3
+// RUN: ld.lld --hash-style=sysv -e main %t1.o %t2.so -o %t3
// RUN: llvm-readobj -s -r %t3 | FileCheck %s
// RUN: llvm-objdump -d %t3 | FileCheck --check-prefix=DISASM %s
diff --git a/test/ELF/tls-i686.s b/test/ELF/tls-i686.s
index 7f2dd605c..c411fc74c 100644
--- a/test/ELF/tls-i686.s
+++ b/test/ELF/tls-i686.s
@@ -1,7 +1,7 @@
// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t
// RUN: ld.lld %t -o %tout
-// RUN: ld.lld %t -shared -o %tsharedout
+// RUN: ld.lld --hash-style=sysv %t -shared -o %tsharedout
// RUN: llvm-objdump -d %tout | FileCheck %s --check-prefix=DIS
// RUN: llvm-readobj -r %tout | FileCheck %s --check-prefix=RELOC
// RUN: llvm-objdump -d %tsharedout | FileCheck %s --check-prefix=DISSHARED
diff --git a/test/ELF/tls-initial-exec-local.s b/test/ELF/tls-initial-exec-local.s
index 0aef3c823..e65fb294b 100644
--- a/test/ELF/tls-initial-exec-local.s
+++ b/test/ELF/tls-initial-exec-local.s
@@ -1,6 +1,6 @@
// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-// RUN: ld.lld -shared %t.o -o %t
+// RUN: ld.lld --hash-style=sysv -shared %t.o -o %t
// RUN: llvm-readobj -r -s %t | FileCheck %s
// RUN: llvm-objdump -d %t | FileCheck --check-prefix=DISASM %s
diff --git a/test/ELF/tls-opt-gdie.s b/test/ELF/tls-opt-gdie.s
index c423a3e0f..6e8531257 100644
--- a/test/ELF/tls-opt-gdie.s
+++ b/test/ELF/tls-opt-gdie.s
@@ -1,7 +1,7 @@
// 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/tls-opt-gdie.s -o %tso.o
// RUN: ld.lld -shared %tso.o -o %t.so
-// RUN: ld.lld %t.o %t.so -o %t1
+// RUN: ld.lld --hash-style=sysv %t.o %t.so -o %t1
// RUN: llvm-readobj -s -r %t1 | FileCheck --check-prefix=RELOC %s
// RUN: llvm-objdump -d %t1 | FileCheck --check-prefix=DISASM %s
diff --git a/test/ELF/tls-opt-gdiele-i686.s b/test/ELF/tls-opt-gdiele-i686.s
index 1432a9607..2dc3731eb 100644
--- a/test/ELF/tls-opt-gdiele-i686.s
+++ b/test/ELF/tls-opt-gdiele-i686.s
@@ -1,7 +1,7 @@
// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %p/Inputs/tls-opt-gdiele-i686.s -o %tso.o
// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o
// RUN: ld.lld -shared %tso.o -o %tso
-// RUN: ld.lld %t.o %tso -o %tout
+// RUN: ld.lld --hash-style=sysv %t.o %tso -o %tout
// RUN: llvm-readobj -r %tout | FileCheck --check-prefix=NORELOC %s
// RUN: llvm-objdump -d %tout | FileCheck --check-prefix=DISASM %s
diff --git a/test/ELF/tls-opt-iele-i686-nopic.s b/test/ELF/tls-opt-iele-i686-nopic.s
index b6608c165..02148b5db 100644
--- a/test/ELF/tls-opt-iele-i686-nopic.s
+++ b/test/ELF/tls-opt-iele-i686-nopic.s
@@ -1,7 +1,7 @@
// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o
// 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 %t.o %tso -o %t1
+// RUN: ld.lld --hash-style=sysv %t.o %tso -o %t1
// RUN: llvm-readobj -s -r %t1 | FileCheck --check-prefix=GOTREL %s
// RUN: llvm-objdump -d %t1 | FileCheck --check-prefix=DISASM %s
diff --git a/test/ELF/tls-static.s b/test/ELF/tls-static.s
index 81ecc8275..338d95c81 100644
--- a/test/ELF/tls-static.s
+++ b/test/ELF/tls-static.s
@@ -3,12 +3,20 @@
// RUN: ld.lld -static %t -o %tout
// RUN: ld.lld %t -o %tout
// RUN: ld.lld -shared %tso -o %tshared
-// RUN: not ld.lld -static %t %tshared -o %tout 2>&1 | FileCheck %s
+// RUN: ld.lld -static %t %tshared -o %tout
// REQUIRES: x86
.global _start
_start:
- call __tls_get_addr
+ data16
+ leaq foobar@TLSGD(%rip), %rdi
+ data16
+ data16
+ rex64
+ callq __tls_get_addr@PLT
-// CHECK: error: undefined symbol: __tls_get_addr
-// CHECK: >>> referenced by {{.*}}:(.text+0x1)
+
+.section .tdata,"awT",@progbits
+.global foobar
+foobar:
+ .long 42
diff --git a/test/ELF/tls-two-relocs.s b/test/ELF/tls-two-relocs.s
index 7c5d6abf7..d5687e9bf 100644
--- a/test/ELF/tls-two-relocs.s
+++ b/test/ELF/tls-two-relocs.s
@@ -1,6 +1,6 @@
// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-// RUN: ld.lld %t -o %tout -shared
+// RUN: ld.lld --hash-style=sysv %t -o %tout -shared
// RUN: llvm-readobj -r %tout | FileCheck %s
data16
diff --git a/test/ELF/unresolved-symbols.s b/test/ELF/unresolved-symbols.s
index 18656dcce..97ecd5014 100644
--- a/test/ELF/unresolved-symbols.s
+++ b/test/ELF/unresolved-symbols.s
@@ -13,6 +13,9 @@
# RUN: not ld.lld %t1.o %t2.o -o %t --unresolved-symbols=xxx 2>&1 | \
# RUN: FileCheck -check-prefix=ERR1 %s
# ERR1: unknown --unresolved-symbols value: xxx
+## Check alias.
+# RUN: not ld.lld %t1.o %t2.o -o %t --unresolved-symbols xxx 2>&1 | \
+# RUN: FileCheck -check-prefix=ERR1 %s
## Ignore all should not produce error for symbols from object except
## case when --no-undefined specified.
diff --git a/test/ELF/verdef-defaultver.s b/test/ELF/verdef-defaultver.s
index c37c90a66..496a29e3d 100644
--- a/test/ELF/verdef-defaultver.s
+++ b/test/ELF/verdef-defaultver.s
@@ -3,7 +3,7 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/verdef-defaultver.s -o %t1
# 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: ld.lld --hash-style=sysv -shared -soname shared %t1 --version-script %t.script -o %t.so
# RUN: llvm-readobj -V -dyn-symbols %t.so | FileCheck --check-prefix=DSO %s
# DSO: DynamicSymbols [
@@ -107,7 +107,7 @@
## Check that we can link against DSO produced.
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t2
-# RUN: ld.lld %t2 %t.so -o %t3
+# RUN: ld.lld --hash-style=sysv %t2 %t.so -o %t3
# RUN: llvm-readobj -V -dyn-symbols %t3 | FileCheck --check-prefix=EXE %s
# EXE: DynamicSymbols [
diff --git a/test/ELF/verdef.s b/test/ELF/verdef.s
index 7fd60a95d..b5d12ee38 100644
--- a/test/ELF/verdef.s
+++ b/test/ELF/verdef.s
@@ -3,7 +3,7 @@
# 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: ld.lld --hash-style=sysv --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
# DSO: Version symbols {
@@ -65,7 +65,7 @@
## Check that we can link agains DSO we produced.
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/verdef.s -o %tmain.o
-# RUN: ld.lld %tmain.o %t.so -o %tout
+# RUN: ld.lld --hash-style=sysv %tmain.o %t.so -o %tout
# RUN: llvm-readobj -V %tout | FileCheck --check-prefix=MAIN %s
# MAIN: Version symbols {
@@ -100,7 +100,7 @@
# 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: ld.lld --hash-style=sysv --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
diff --git a/test/ELF/verneed-as-needed-weak.s b/test/ELF/verneed-as-needed-weak.s
index a8efdc42d..df15bc78c 100644
--- a/test/ELF/verneed-as-needed-weak.s
+++ b/test/ELF/verneed-as-needed-weak.s
@@ -1,6 +1,10 @@
# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/verneed1.s -o %t1.o
+# RUN: echo "v1 {}; v2 {}; v3 { local: *; };" > %t.script
+# RUN: ld.lld -shared %t1.o --version-script %t.script -o %t.so
+
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-# RUN: ld.lld %t.o --as-needed %S/Inputs/verneed1.so -o %t
+# RUN: ld.lld %t.o --as-needed %t.so -o %t
# RUN: llvm-readobj -V %t | FileCheck %s
# CHECK: SHT_GNU_verneed {
diff --git a/test/ELF/verneed-local.s b/test/ELF/verneed-local.s
index 9ab6cd791..208d8ecf8 100644
--- a/test/ELF/verneed-local.s
+++ b/test/ELF/verneed-local.s
@@ -1,6 +1,10 @@
# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/verneed1.s -o %t1.o
+# RUN: echo "v1 {}; v2 {}; v3 { local: *; };" > %t.script
+# RUN: ld.lld -shared %t1.o --version-script %t.script -o %t.so
+
# 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
+# RUN: not ld.lld %t.o %t.so -o %t 2>&1 | FileCheck %s
# CHECK: error: undefined symbol: f3
# CHECK: >>> referenced by {{.*}}:(.text+0x1)
diff --git a/test/ELF/verneed.s b/test/ELF/verneed.s
index d0d006725..27ab047e8 100644
--- a/test/ELF/verneed.s
+++ b/test/ELF/verneed.s
@@ -1,6 +1,12 @@
# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/verneed1.s -o %t1.o
+# RUN: echo "v1 {}; v2 {}; v3 { local: *; };" > %t.script
+# RUN: ld.lld -shared %t1.o --version-script %t.script -o %t1.so -soname verneed1.so.0
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/verneed2.s -o %t2.o
+# RUN: ld.lld -shared %t2.o --version-script %t.script -o %t2.so -soname verneed2.so.0
+
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-# RUN: ld.lld %t.o %S/Inputs/verneed1.so %S/Inputs/verneed2.so -o %t
+# RUN: ld.lld --hash-style=sysv %t.o %t1.so %t2.so -o %t
# RUN: llvm-readobj -V -sections -section-data -dyn-symbols -dynamic-table %t | FileCheck %s
# CHECK: Section {
diff --git a/test/ELF/version-script-err.s b/test/ELF/version-script-err.s
index ea3f664ea..bd786d913 100644
--- a/test/ELF/version-script-err.s
+++ b/test/ELF/version-script-err.s
@@ -8,4 +8,3 @@
// RUN: not ld.lld --version-script %terr1.script -shared %t.o -o %t.so 2>&1 | \
// RUN: FileCheck -check-prefix=ERR1 %s
// ERR1: {{.*}}:1: unclosed quote
-// ERR1-NEXT: {{.*}}: unexpected EOF
diff --git a/test/ELF/version-script-extern.s b/test/ELF/version-script-extern.s
index 2b89839c3..c63ff817f 100644
--- a/test/ELF/version-script-extern.s
+++ b/test/ELF/version-script-extern.s
@@ -7,7 +7,7 @@
# 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: ld.lld --hash-style=sysv --version-script %t.script -shared %t.o -o %t.so
# RUN: llvm-readobj -V -dyn-symbols %t.so | FileCheck --check-prefix=DSO %s
# DSO: DynamicSymbols [
diff --git a/test/ELF/version-script-no-warn2.s b/test/ELF/version-script-no-warn2.s
new file mode 100644
index 000000000..52beff366
--- /dev/null
+++ b/test/ELF/version-script-no-warn2.s
@@ -0,0 +1,8 @@
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/version-script-no-warn2.s -o %t1.o
+# RUN: ld.lld %t1.o -o %t1.so -shared
+# RUN: echo "{ global: foo; local: *; };" > %t.script
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t2.o
+# RUN: ld.lld -shared --version-script %t.script %t2.o %t1.so -o %t2.so --fatal-warnings
+
+.global foo
+foo:
diff --git a/test/ELF/version-script-symver.s b/test/ELF/version-script-symver.s
new file mode 100644
index 000000000..0a4eddd46
--- /dev/null
+++ b/test/ELF/version-script-symver.s
@@ -0,0 +1,9 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t
+
+.global _start
+.global bar
+.symver _start, bar@@VERSION
+_start:
+ jmp bar
diff --git a/test/ELF/version-script-symver2.s b/test/ELF/version-script-symver2.s
new file mode 100644
index 000000000..5961d9a7c
--- /dev/null
+++ b/test/ELF/version-script-symver2.s
@@ -0,0 +1,28 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: echo "VER1 { global: foo; local: *; }; VER2 { global: foo; }; VER3 { global: foo; };" > %t.map
+# RUN: ld.lld -shared %t.o --version-script %t.map -o %t.so --fatal-warnings
+# RUN: llvm-readobj -V %t.so | FileCheck %s
+
+# CHECK: Symbols [
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Version: 0
+# CHECK-NEXT: Name: @
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Version: 3
+# CHECK-NEXT: Name: foo@@VER2
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Version: 2
+# CHECK-NEXT: Name: foo@VER1
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+.global bar
+bar:
+.symver bar, foo@VER1
+
+.global zed
+zed:
+.symver zed, foo@@VER2
diff --git a/test/ELF/version-script-twice.s b/test/ELF/version-script-twice.s
new file mode 100644
index 000000000..1227dcd50
--- /dev/null
+++ b/test/ELF/version-script-twice.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+
+# RUN: echo "FBSD_1.1 {}; FBSD_1.2 {};" > %t.ver
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld -shared %t.o -o %t.so --version-script=%t.ver
+# RUN: llvm-readobj --dyn-symbols --elf-output-style=GNU %t.so | FileCheck %s
+
+ .weak openat
+openat:
+
+ .global openat@FBSD_1.1
+openat@FBSD_1.1 = openat
+
+ .global openat@@FBSD_1.2
+openat@@FBSD_1.2 = openat
+
+# CHECK-DAG: openat@FBSD_1.1
+# CHECK-DAG: openat@@FBSD_1.2
diff --git a/test/ELF/version-script-undef-version.s b/test/ELF/version-script-undef-version.s
new file mode 100644
index 000000000..40dc816f5
--- /dev/null
+++ b/test/ELF/version-script-undef-version.s
@@ -0,0 +1,12 @@
+# REQUIRES: x86
+
+# Test that we don't error on undefined versions when static linking.
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t
+# RUN: echo "DEFINED { global: *; };" > %t.map
+# RUN: ld.lld %t.o --version-script %t.map -o %t
+
+.global _start
+.global bar
+.symver _start, bar@@UNDEFINED
+_start:
diff --git a/test/ELF/version-script-weak.s b/test/ELF/version-script-weak.s
new file mode 100644
index 000000000..cc3df8da5
--- /dev/null
+++ b/test/ELF/version-script-weak.s
@@ -0,0 +1,28 @@
+# 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 %S/Inputs/version-script-weak.s -o %tmp.o
+# RUN: rm -f %t.a
+# RUN: llvm-ar rcs %t.a %tmp.o
+# RUN: echo "{ local: *; };" > %t.script
+# RUN: ld.lld -shared --version-script %t.script %t.o %t.a -o %t.so
+# RUN: llvm-readobj -dyn-symbols -r %t.so | FileCheck %s
+
+# CHECK: Relocations [
+# CHECK-NEXT: Section ({{.*}}) .rela.plt {
+# CHECK-NEXT: 0x2018 R_X86_64_JUMP_SLOT foo
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+# CHECK: Symbol {
+# CHECK: Name: foo@
+# CHECK-NEXT: Value: 0x0
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Weak
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Undefined
+# CHECK-NEXT: }
+
+.text
+ callq foo@PLT
+.weak foo
diff --git a/test/ELF/version-script.s b/test/ELF/version-script.s
index 72f9eeb94..66e850620 100644
--- a/test/ELF/version-script.s
+++ b/test/ELF/version-script.s
@@ -142,12 +142,12 @@
# VERDSO-NEXT: ]
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
-# RUN: ld.lld -shared %t.o %t2.so -o %t.so
+# RUN: ld.lld --hash-style=sysv -shared %t.o %t2.so -o %t.so
# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck --check-prefix=ALL %s
# RUN: echo "{ global: foo1; foo3; };" > %t2.script
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
-# RUN: ld.lld --version-script %t2.script -shared %t.o %t2.so -o %t.so
+# RUN: ld.lld --hash-style=sysv --version-script %t2.script -shared %t.o %t2.so -o %t.so
# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck --check-prefix=ALL %s
# ALL: DynamicSymbols [
diff --git a/test/ELF/weak-entry.s b/test/ELF/weak-entry.s
new file mode 100644
index 000000000..427230ab8
--- /dev/null
+++ b/test/ELF/weak-entry.s
@@ -0,0 +1,13 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
+# RUN: ld.lld %t -o %tout
+# RUN: llvm-nm %tout | FileCheck %s
+
+# CHECK: w _start
+# CHECK-NEXT: T foo
+
+.global foo
+.weak _start
+.text
+foo:
+ .dc.a _start
diff --git a/test/ELF/weak-undef-export.s b/test/ELF/weak-undef-export.s
new file mode 100644
index 000000000..164bc1730
--- /dev/null
+++ b/test/ELF/weak-undef-export.s
@@ -0,0 +1,31 @@
+# REQUIRES: x86
+
+# Test that we don't fail with foo being undefined.
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld --export-dynamic %t.o -o %t
+# RUN: llvm-readobj -dyn-symbols %t | FileCheck %s
+
+# CHECK: DynamicSymbols [
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: @ (0)
+# CHECK-NEXT: Value: 0x0
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Local (0x0)
+# CHECK-NEXT: Type: None (0x0)
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Undefined (0x0)
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: foo@ (1)
+# CHECK-NEXT: Value: 0x0
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Weak (0x2)
+# CHECK-NEXT: Type: None (0x0)
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Undefined (0x0)
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+ .weak foo
+ .quad foo
diff --git a/test/ELF/weak-undef-lazy.s b/test/ELF/weak-undef-lazy.s
new file mode 100644
index 000000000..113013ea2
--- /dev/null
+++ b/test/ELF/weak-undef-lazy.s
@@ -0,0 +1,11 @@
+# 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 %S/Inputs/weak-undef-lazy.s -o %t2.o
+# RUN: rm -f %t2.a
+# RUN: llvm-ar rc %t2.a %t2.o
+# RUN: ld.lld %t.o %t2.a -o %t --export-dynamic
+
+ .global _start
+_start:
+ .weak foobar
+ .quad foobar
diff --git a/test/ELF/weak-undef-val.s b/test/ELF/weak-undef-val.s
new file mode 100644
index 000000000..acd42f484
--- /dev/null
+++ b/test/ELF/weak-undef-val.s
@@ -0,0 +1,26 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t --export-dynamic
+# RUN: llvm-readobj -s -section-data %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: 0x201000
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Link:
+# CHECK-NEXT: Info:
+# CHECK-NEXT: AddressAlignment:
+# CHECK-NEXT: EntrySize:
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 00F0DFFF |
+# CHECK-NEXT: )
+
+ .global _start
+_start:
+ .weak foobar
+ .long foobar - .
diff --git a/test/ELF/weak-undef.s b/test/ELF/weak-undef.s
index b6340339e..7ee505be6 100644
--- a/test/ELF/weak-undef.s
+++ b/test/ELF/weak-undef.s
@@ -13,6 +13,15 @@
# CHECK-NEXT: Other: 0
# CHECK-NEXT: Section: Undefined (0x0)
# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: foo@
+# CHECK-NEXT: Value: 0x0
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Weak
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Undefined
+# CHECK-NEXT: }
# CHECK-NEXT: ]
.weak foo
diff --git a/test/ELF/wrap-dynamic-undef.s b/test/ELF/wrap-dynamic-undef.s
new file mode 100644
index 000000000..95b985981
--- /dev/null
+++ b/test/ELF/wrap-dynamic-undef.s
@@ -0,0 +1,15 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/wrap-dynamic-undef.s -o %t2.o
+# RUN: ld.lld %t2.o -o %t2.so -shared
+# RUN: ld.lld %t1.o %t2.so -o %t --wrap foo
+# RUN: llvm-readobj -dyn-symbols --elf-output-style=GNU %t | FileCheck %s
+
+# Test that the dynamic relocation uses foo. We used to produce a
+# relocation with __real_foo.
+
+# CHECK: NOTYPE GLOBAL DEFAULT UND foo
+
+.global _start
+_start:
+ callq __real_foo@plt
diff --git a/test/ELF/wrap-no-real.s b/test/ELF/wrap-no-real.s
new file mode 100644
index 000000000..1e2f3d649
--- /dev/null
+++ b/test/ELF/wrap-no-real.s
@@ -0,0 +1,31 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/wrap-no-real.s -o %t2.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/wrap-no-real2.s -o %t3.o
+// RUN: ld.lld -o %t3.so -shared %t3.o
+
+// RUN: ld.lld -o %t %t1.o %t2.o -wrap foo
+// RUN: llvm-objdump -d -print-imm-hex %t | FileCheck %s
+
+// RUN: ld.lld -o %t %t1.o %t2.o %t3.so -wrap foo
+// RUN: llvm-objdump -d -print-imm-hex %t | FileCheck %s
+
+// CHECK: _start:
+// CHECK-NEXT: movl $0x11010, %edx
+// CHECK-NEXT: movl $0x11010, %edx
+// CHECK-NEXT: movl $0x11000, %edx
+
+// RUN: llvm-readobj -t -s %t | FileCheck -check-prefix=SYM %s
+// SYM-NOT: Name: __real_foo
+// SYM: Name: foo
+// SYM-NEXT: Value: 0x11000
+// SYM-NOT: Name: __real_foo
+// SYM: Name: __wrap_foo
+// SYM-NEXT: Value: 0x11010
+// SYM-NOT: Name: __real_foo
+
+.global _start
+_start:
+ movl $foo, %edx
+ movl $__wrap_foo, %edx
+ movl $__real_foo, %edx
diff --git a/test/ELF/wrap.s b/test/ELF/wrap.s
index 17aac2db6..e0fbfe427 100644
--- a/test/ELF/wrap.s
+++ b/test/ELF/wrap.s
@@ -12,6 +12,14 @@
// CHECK-NEXT: movl $0x11010, %edx
// CHECK-NEXT: movl $0x11000, %edx
+// RUN: llvm-readobj -t -s %t3 | FileCheck -check-prefix=SYM %s
+// SYM: Name: foo
+// SYM-NEXT: Value: 0x11000
+// SYM: Name: __wrap_foo
+// SYM-NEXT: Value: 0x11010
+// SYM: Name: __real_foo
+// SYM-NEXT: Value: 0x11020
+
.global _start
_start:
movl $foo, %edx
diff --git a/test/ELF/x86-64-dyn-rel-error.s b/test/ELF/x86-64-dyn-rel-error.s
index ee39e2cb8..f479eca2e 100644
--- a/test/ELF/x86-64-dyn-rel-error.s
+++ b/test/ELF/x86-64-dyn-rel-error.s
@@ -10,3 +10,5 @@ _start:
.long bar
// CHECK: relocation R_X86_64_32 cannot be used against shared object; recompile with -fPIC
+
+// RUN: ld.lld --noinhibit-exec %t.o %t2.so -o %t 2>&1 | FileCheck %s
diff --git a/test/ELF/x86-64-relax-got-abs.s b/test/ELF/x86-64-relax-got-abs.s
index c4291202f..0caf6dcec 100644
--- a/test/ELF/x86-64-relax-got-abs.s
+++ b/test/ELF/x86-64-relax-got-abs.s
@@ -1,7 +1,7 @@
// 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: ld.lld --hash-style=sysv %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
diff --git a/test/ELF/x86-64-tls-gd-local.s b/test/ELF/x86-64-tls-gd-local.s
index ec6115dc2..8879737ad 100644
--- a/test/ELF/x86-64-tls-gd-local.s
+++ b/test/ELF/x86-64-tls-gd-local.s
@@ -1,6 +1,6 @@
// REQUIRES: x86
// RUN: llvm-mc %s -o %t.o -filetype=obj -triple=x86_64-pc-linux
-// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: ld.lld --hash-style=sysv %t.o -o %t.so -shared
// RUN: llvm-readobj -r -s -section-data %t.so | FileCheck %s
.byte 0x66
diff --git a/test/MinGW/driver.test b/test/MinGW/driver.test
new file mode 100644
index 000000000..8360ccb70
--- /dev/null
+++ b/test/MinGW/driver.test
@@ -0,0 +1,80 @@
+RUN: ld.lld -### foo.o -m i386pe | FileCheck -check-prefix=X86 %s
+X86: -out:a.exe
+X86-SAME: -machine:x86
+X86-SAME: -alternatename:__image_base__=___ImageBase
+X86-SAME: foo.o
+
+RUN: ld.lld -### foo.o -m i386pep | FileCheck -check-prefix=X64 %s
+X64: -out:a.exe
+X64-SAME: -machine:x64
+X64-SAME: -alternatename:__image_base__=__ImageBase
+X64-SAME: foo.o
+
+RUN: ld.lld -### foo.o -m thumb2pe | FileCheck -check-prefix=ARM %s
+ARM: -out:a.exe
+ARM-SAME: -machine:arm
+ARM-SAME: -alternatename:__image_base__=__ImageBase
+ARM-SAME: foo.o
+
+RUN: ld.lld -### foo.o -m arm64pe | FileCheck -check-prefix=ARM64 %s
+ARM64: -out:a.exe
+ARM64-SAME: -machine:arm64
+ARM64-SAME: -alternatename:__image_base__=__ImageBase
+ARM64-SAME: foo.o
+
+RUN: ld.lld -### foo.o -m i386pep -shared | FileCheck -check-prefix=SHARED %s
+RUN: ld.lld -### foo.o -m i386pep --shared | FileCheck -check-prefix=SHARED %s
+SHARED: -out:a.dll
+SHARED-SAME: -dll
+
+RUN: ld.lld -### foo.o -m i386pep -shared foo.def | FileCheck -check-prefix=DEF1 %s
+DEF1: -def:foo.def
+
+RUN: ld.lld -### foo.o -m i386pep -shared FOO.DEF | FileCheck -check-prefix=DEF2 %s
+DEF2: -def:FOO.DEF
+
+RUN: ld.lld -### foo.o -m i386pep -obar.exe | FileCheck -check-prefix=OUT %s
+RUN: ld.lld -### foo.o -m i386pep -o bar.exe | FileCheck -check-prefix=OUT %s
+OUT: -out:bar.exe
+
+RUN: ld.lld -### foo.o -m i386pep --out-implib bar | FileCheck -check-prefix=IMPLIB %s
+IMPLIB: -implib:bar
+
+RUN: ld.lld -### foo.o -m i386pep -out-implib bar | FileCheck -check-prefix=NOIMPLIB %s
+NOIMPLIB: -out:ut-implib
+
+RUN: ld.lld -### foo.o -m i386pep -e bar | FileCheck -check-prefix=ENTRY %s
+RUN: ld.lld -### foo.o -m i386pep -entry bar | FileCheck -check-prefix=ENTRY %s
+RUN: ld.lld -### foo.o -m i386pep --entry bar | FileCheck -check-prefix=ENTRY %s
+ENTRY: -entry:bar
+
+RUN: ld.lld -### foo.o -m i386pep -mllvm bar -mllvm baz | FileCheck -check-prefix=MLLVM %s
+MLLVM: -mllvm:bar -mllvm:baz
+
+RUN: ld.lld -### foo.o -m i386pep -subsystem console | FileCheck -check-prefix=SUBSYSTEM %s
+RUN: ld.lld -### foo.o -m i386pep --subsystem console | FileCheck -check-prefix=SUBSYSTEM %s
+SUBSYSTEM: -subsystem:console
+
+RUN: ld.lld -### foo.o -m i386pep -stack 4194304,8192 | FileCheck -check-prefix=STACK %s
+RUN: ld.lld -### foo.o -m i386pep --stack 4194304,8192 | FileCheck -check-prefix=STACK %s
+STACK: -stack:4194304,8192
+
+RUN: ld.lld -### foo.o -m i386pep -verbose | FileCheck -check-prefix=VERBOSE %s
+RUN: ld.lld -### foo.o -m i386pep --verbose | FileCheck -check-prefix=VERBOSE %s
+VERBOSE: -verbose
+
+RUN: ld.lld -### -shared -m i386pe -e _DllMainCRTStartup@12 foo.o | FileCheck -check-prefix I386-ENTRY %s
+I386-ENTRY: -entry:DllMainCRTStartup@12
+
+RUN: ld.lld -### -m i386pep foo.o --whole-archive bar.a --no-whole-archive baz.a | FileCheck -check-prefix WHOLE-ARCHIVE %s
+RUN: ld.lld -### -m i386pep foo.o -whole-archive bar.a -no-whole-archive baz.a | FileCheck -check-prefix WHOLE-ARCHIVE %s
+WHOLE-ARCHIVE: foo.o -wholearchive:bar.a baz.a
+
+RUN: ld.lld -### -m i386pep foo.o | FileCheck -check-prefix MINGW-FLAG %s
+MINGW-FLAG: -lldmingw
+
+RUN: ld.lld -### -m i386pep foo.o --export-all-symbols | FileCheck -check-prefix EXPORT-ALL %s
+EXPORT-ALL: -export-all-symbols
+
+RUN: ld.lld -### -m i386pep foo.o --output-def out.def | FileCheck -check-prefix OUTPUT-DEF %s
+OUTPUT-DEF: -output-def:out.def
diff --git a/test/MinGW/lib.test b/test/MinGW/lib.test
new file mode 100644
index 000000000..56104d6d9
--- /dev/null
+++ b/test/MinGW/lib.test
@@ -0,0 +1,21 @@
+RUN: rm -rf %t/lib
+RUN: mkdir -p %t/lib
+RUN: not ld.lld -### -m i386pep -lfoo -L%t/lib 2>&1 | FileCheck -check-prefix=LIB1 %s
+LIB1: unable to find library -lfoo
+
+RUN: echo > %t/lib/libfoo.dll.a
+RUN: ld.lld -### -m i386pep -lfoo -L%t/lib | FileCheck -check-prefix=LIB2 %s
+LIB2: libfoo.dll.a
+
+RUN: not ld.lld -### -m i386pep -Bstatic -lfoo -L%t/lib 2>&1 | FileCheck -check-prefix=LIB3 %s
+LIB3: unable to find library -lfoo
+
+RUN: echo > %t/lib/libfoo.a
+RUN: ld.lld -### -m i386pep -Bstatic -lfoo -L%t/lib | FileCheck -check-prefix=LIB4 %s
+LIB4: libfoo.a
+
+RUN: echo > %t/lib/libbar.dll.a
+RUN: echo > %t/lib/libbar.a
+RUN: ld.lld -### -m i386pep -Bstatic -lfoo -Bdynamic -lbar -L%t/lib | FileCheck -check-prefix=LIB5 %s
+LIB5: libfoo.a
+LIB5-SAME: libbar.dll.a
diff --git a/test/Unit/lit.cfg b/test/Unit/lit.cfg.py
index 4bc973a58..dac9f5dc6 100644
--- a/test/Unit/lit.cfg
+++ b/test/Unit/lit.cfg.py
@@ -17,7 +17,21 @@ config.suffixes = []
config.test_source_root = os.path.join(config.lld_obj_root, 'unittests')
config.test_exec_root = config.test_source_root
+
+# Tweak the PATH to include the tools dir.
+path = os.path.pathsep.join((config.lld_tools_dir, config.llvm_tools_dir, config.environment['PATH']))
+config.environment['PATH'] = path
+
+path = os.path.pathsep.join((config.lld_libs_dir, config.llvm_libs_dir,
+ config.environment.get('LD_LIBRARY_PATH','')))
+config.environment['LD_LIBRARY_PATH'] = path
+
+# Propagate LLVM_SRC_ROOT into the environment.
+config.environment['LLVM_SRC_ROOT'] = config.llvm_src_root
+
+# Propagate PYTHON_EXECUTABLE into the environment
+config.environment['PYTHON_EXECUTABLE'] = sys.executable
+
+
# testFormat: The test format to use to interpret tests.
-if not hasattr(config, 'llvm_build_mode'):
- lit_config.fatal("unable to find llvm_build_mode value on config")
config.test_format = lit.formats.GoogleTest(config.llvm_build_mode, 'Tests')
diff --git a/test/Unit/lit.site.cfg.in b/test/Unit/lit.site.cfg.py.in
index c2f3054a2..cc12117ad 100644
--- a/test/Unit/lit.site.cfg.in
+++ b/test/Unit/lit.site.cfg.py.in
@@ -8,6 +8,8 @@ config.llvm_build_mode = "@LLVM_BUILD_MODE@"
config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@"
config.lld_obj_root = "@LLD_BINARY_DIR@"
config.lld_src_root = "@LLD_SOURCE_DIR@"
+config.lld_libs_dir = "@LLVM_LIBRARY_OUTPUT_INTDIR@"
+config.lld_tools_dir = "@LLVM_RUNTIME_OUTPUT_INTDIR@"
config.target_triple = "@TARGET_TRIPLE@"
config.python_executable = "@PYTHON_EXECUTABLE@"
@@ -22,4 +24,4 @@ except KeyError as e:
lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key))
# Let the main config do the real work.
-lit_config.load_config(config, "@LLD_SOURCE_DIR@/test/Unit/lit.cfg")
+lit_config.load_config(config, "@LLD_SOURCE_DIR@/test/Unit/lit.cfg.py")
diff --git a/test/lit.cfg b/test/lit.cfg
deleted file mode 100644
index c4e8f91e6..000000000
--- a/test/lit.cfg
+++ /dev/null
@@ -1,267 +0,0 @@
-# -*- Python -*-
-
-import os
-import platform
-import re
-import subprocess
-import locale
-
-import lit.formats
-import lit.util
-
-# Configuration file for the 'lit' test runner.
-
-# name: The name of this test suite.
-config.name = 'lld'
-
-# Tweak PATH for Win32
-if sys.platform in ['win32']:
- # Seek sane tools in directories and set to $PATH.
- path = getattr(config, 'lit_tools_dir', None)
- path = lit_config.getToolsPath(path,
- config.environment['PATH'],
- ['cmp.exe', 'grep.exe', 'sed.exe'])
- if path is not None:
- path = os.path.pathsep.join((path,
- config.environment['PATH']))
- config.environment['PATH'] = path
-
-# Choose between lit's internal shell pipeline runner and a real shell. If
-# LIT_USE_INTERNAL_SHELL is in the environment, we use that as an override.
-use_lit_shell = os.environ.get("LIT_USE_INTERNAL_SHELL")
-if use_lit_shell:
- # 0 is external, "" is default, and everything else is internal.
- execute_external = (use_lit_shell == "0")
-else:
- # Otherwise we default to internal on Windows and external elsewhere, as
- # bash on Windows is usually very slow.
- execute_external = (not sys.platform in ['win32'])
-
-
-# testFormat: The test format to use to interpret tests.
-#
-# For now we require '&&' between commands, until they get globally killed and
-# the test runner updated.
-config.test_format = lit.formats.ShTest(execute_external)
-
-# suffixes: A list of file extensions to treat as test files.
-config.suffixes = ['.ll', '.s', '.objtxt', '.test']
-
-# excludes: A list of directories to exclude from the testsuite. The 'Inputs'
-# subdirectories contain auxiliary inputs for various tests in their parent
-# directories.
-config.excludes = ['Inputs']
-
-# test_source_root: The root path where tests are located.
-config.test_source_root = os.path.dirname(__file__)
-
-# test_exec_root: The root path where tests should be run.
-lld_obj_root = getattr(config, 'lld_obj_root', None)
-if lld_obj_root is not None:
- config.test_exec_root = os.path.join(lld_obj_root, 'test')
-
-# Set llvm_{src,obj}_root for use by others.
-config.llvm_src_root = getattr(config, 'llvm_src_root', None)
-config.llvm_obj_root = getattr(config, 'llvm_obj_root', None)
-
-# Tweak the PATH to include the tools dir and the scripts dir.
-if lld_obj_root is not None:
- lld_tools_dir = getattr(config, 'lld_tools_dir', None)
- if not lld_tools_dir:
- lit_config.fatal('No LLD tools dir set!')
- llvm_tools_dir = getattr(config, 'llvm_tools_dir', None)
- if not llvm_tools_dir:
- lit_config.fatal('No LLVM tools dir set!')
- path = os.path.pathsep.join((lld_tools_dir, llvm_tools_dir, config.environment['PATH']))
- path = os.path.pathsep.join((os.path.join(getattr(config, 'llvm_src_root', None),'test','Scripts'),path))
-
- config.environment['PATH'] = path
-
- lld_libs_dir = getattr(config, 'lld_libs_dir', None)
- if not lld_libs_dir:
- lit_config.fatal('No LLD libs dir set!')
- llvm_libs_dir = getattr(config, 'llvm_libs_dir', None)
- if not llvm_libs_dir:
- lit_config.fatal('No LLVM libs dir set!')
- path = os.path.pathsep.join((lld_libs_dir, llvm_libs_dir,
- config.environment.get('LD_LIBRARY_PATH','')))
- config.environment['LD_LIBRARY_PATH'] = path
-
- # Propagate LLVM_SRC_ROOT into the environment.
- config.environment['LLVM_SRC_ROOT'] = getattr(config, 'llvm_src_root', '')
-
- # Propagate PYTHON_EXECUTABLE into the environment
- config.environment['PYTHON_EXECUTABLE'] = getattr(config, 'python_executable',
- '')
-###
-
-# Check that the object root is known.
-if config.test_exec_root is None:
- # Otherwise, we haven't loaded the site specific configuration (the user is
- # probably trying to run on a test file directly, and either the site
- # configuration hasn't been created by the build system, or we are in an
- # out-of-tree build situation).
-
- # Check for 'lld_site_config' user parameter, and use that if available.
- site_cfg = lit_config.params.get('lld_site_config', None)
- if site_cfg and os.path.exists(site_cfg):
- lit_config.load_config(config, site_cfg)
- raise SystemExit
-
- # Try to detect the situation where we are using an out-of-tree build by
- # looking for 'llvm-config'.
- #
- # FIXME: I debated (i.e., wrote and threw away) adding logic to
- # automagically generate the lit.site.cfg if we are in some kind of fresh
- # build situation. This means knowing how to invoke the build system though,
- # and I decided it was too much magic. We should solve this by just having
- # the .cfg files generated during the configuration step.
-
- llvm_config = lit.util.which('llvm-config', config.environment['PATH'])
- if not llvm_config:
- lit_config.fatal('No site specific configuration available!')
-
- # Get the source and object roots.
- llvm_src_root = lit.util.capture(['llvm-config', '--src-root']).strip()
- llvm_obj_root = lit.util.capture(['llvm-config', '--obj-root']).strip()
- lld_src_root = os.path.join(llvm_src_root, "tools", "lld")
- lld_obj_root = os.path.join(llvm_obj_root, "tools", "lld")
-
- # Validate that we got a tree which points to here, using the standard
- # tools/lld layout.
- this_src_root = os.path.dirname(config.test_source_root)
- if os.path.realpath(lld_src_root) != os.path.realpath(this_src_root):
- lit_config.fatal('No site specific configuration available!')
-
- # Check that the site specific configuration exists.
- site_cfg = os.path.join(lld_obj_root, 'test', 'lit.site.cfg')
- if not os.path.exists(site_cfg):
- lit_config.fatal(
- 'No site specific configuration available! You may need to '
- 'run "make test" in your lld build directory.')
-
- # Okay, that worked. Notify the user of the automagic, and reconfigure.
- lit_config.note('using out-of-tree build at %r' % lld_obj_root)
- lit_config.load_config(config, site_cfg)
- raise SystemExit
-
-# For each occurrence of a lld tool name as its own word, replace it
-# with the full path to the build directory holding that tool. This
-# ensures that we are testing the tools just built and not some random
-# tools that might happen to be in the user's PATH.
-
-# Regex assertions to reject neighbor hyphens/dots (seen in some tests).
-# For example, we want to prefix 'lld' and 'ld.lld' but not the 'lld' inside
-# of 'ld.lld'.
-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-pdbutil\b",
- r"\bllvm-readobj\b",
- r"\bobj2yaml\b",
- r"\byaml2obj\b"]
-
-for pattern in tool_patterns:
- # Extract the tool name from the pattern. This relies on the tool
- # name being surrounded by \b word match operators. If the
- # pattern starts with "| ", include it in the string to be
- # substituted.
- tool_match = re.match(r"^(\\)?((\| )?)\W+b([0-9A-Za-z-_\.]+)\\b\W*$",
- pattern)
- tool_pipe = tool_match.group(2)
- tool_name = tool_match.group(4)
- tool_path = lit.util.which(tool_name, config.environment['PATH'])
- if not tool_path:
- # Warn, but still provide a substitution.
- lit_config.note('Did not find ' + tool_name + ' in ' + path)
- tool_path = llvm_tools_dir + '/' + tool_name
- config.substitutions.append((pattern, tool_pipe + tool_path))
-
-# Add site-specific substitutions.
-config.substitutions.append( ('%python', config.python_executable) )
-
-###
-
-# When running under valgrind, we mangle '-vg' onto the end of the triple so we
-# can check it with XFAIL and XTARGET.
-if lit_config.useValgrind:
- config.target_triple += '-vg'
-
-# Shell execution
-if execute_external:
- config.available_features.add('shell')
-
-# zlib compression library
-if config.have_zlib:
- config.available_features.add("zlib")
-
-# Running on Darwin OS
-if platform.system() in ['Darwin']:
- config.available_features.add('system-linker-mach-o')
-
-# Running on ELF based *nix
-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')
-
-# llvm-config knows whether it is compiled with asserts (and)
-# whether we are operating in release/debug mode.
-import subprocess
-try:
- llvm_config_cmd = \
- subprocess.Popen([os.path.join(llvm_tools_dir, 'llvm-config'),
- '--build-mode', '--assertion-mode', '--targets-built'],
- stdout = subprocess.PIPE)
-except OSError as why:
- print("Could not find llvm-config in " + llvm_tools_dir)
- exit(42)
-
-llvm_config_output = llvm_config_cmd.stdout.read().decode('utf_8')
-llvm_config_output_list = llvm_config_output.split("\n")
-
-if re.search(r'DEBUG', llvm_config_output_list[0]):
- config.available_features.add('debug')
-if re.search(r'ON', llvm_config_output_list[1]):
- config.available_features.add('asserts')
-
-archs = llvm_config_output_list[2]
-if re.search(r'AArch64', archs):
- config.available_features.add('aarch64')
-if re.search(r'ARM', archs):
- config.available_features.add('arm')
-if re.search(r'AVR', archs):
- config.available_features.add('avr')
-if re.search(r'Mips', archs):
- config.available_features.add('mips')
-if re.search(r'X86', archs):
- config.available_features.add('x86')
-if re.search(r'PowerPC', archs):
- config.available_features.add('ppc')
-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 the mt.exe Microsoft utility exists.
-if lit.util.which('mt.exe', config.environment['PATH']):
- config.available_features.add('win_mt')
diff --git a/test/lit.cfg.py b/test/lit.cfg.py
new file mode 100644
index 000000000..2a4404461
--- /dev/null
+++ b/test/lit.cfg.py
@@ -0,0 +1,84 @@
+# -*- Python -*-
+
+import os
+import platform
+import re
+import subprocess
+import locale
+
+import lit.formats
+import lit.util
+
+from lit.llvm import llvm_config
+
+# Configuration file for the 'lit' test runner.
+
+# name: The name of this test suite.
+config.name = 'lld'
+
+# testFormat: The test format to use to interpret tests.
+#
+# For now we require '&&' between commands, until they get globally killed and
+# the test runner updated.
+config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell)
+
+# suffixes: A list of file extensions to treat as test files.
+config.suffixes = ['.ll', '.s', '.test', '.yaml', '.objtxt']
+
+# excludes: A list of directories to exclude from the testsuite. The 'Inputs'
+# subdirectories contain auxiliary inputs for various tests in their parent
+# directories.
+config.excludes = ['Inputs']
+
+# test_source_root: The root path where tests are located.
+config.test_source_root = os.path.dirname(__file__)
+
+config.test_exec_root = os.path.join(config.lld_obj_root, 'test')
+
+llvm_config.use_default_substitutions()
+llvm_config.use_lld()
+
+tool_patterns = [
+ 'llvm-as', 'llvm-mc', 'llvm-nm',
+ 'llvm-objdump', 'llvm-pdbutil', 'llvm-readobj', 'obj2yaml', 'yaml2obj']
+
+llvm_config.add_tool_substitutions(tool_patterns)
+
+# When running under valgrind, we mangle '-vg' onto the end of the triple so we
+# can check it with XFAIL and XTARGET.
+if lit_config.useValgrind:
+ config.target_triple += '-vg'
+
+# Running on ELF based *nix
+if platform.system() in ['FreeBSD', 'Linux']:
+ config.available_features.add('system-linker-elf')
+
+# Set if host-cxxabi's demangler can handle target's symbols.
+if platform.system() not in ['Windows']:
+ config.available_features.add('demangler')
+
+llvm_config.feature_config(
+ [('--build-mode', {'DEBUG': 'debug'}),
+ ('--assertion-mode', {'ON': 'asserts'}),
+ ('--targets-built', {'AArch64': 'aarch64',
+ 'AMDGPU': 'amdgpu',
+ 'ARM': 'arm',
+ 'AVR': 'avr',
+ 'Mips': 'mips',
+ 'PowerPC': 'ppc',
+ 'Sparc': 'sparc',
+ 'X86': 'x86'})
+ ])
+
+# Set a fake constant version so that we get consitent output.
+config.environment['LLD_VERSION'] = 'LLD 1.0'
+
+# Indirectly check if the mt.exe Microsoft utility exists by searching for
+# cvtres, which always accompanies it. Alternatively, check if we can use
+# libxml2 to merge manifests.
+if (lit.util.which('cvtres', config.environment['PATH'])) or \
+ (config.llvm_libxml2_enabled == '1'):
+ config.available_features.add('manifest_tool')
+
+if (config.llvm_libxml2_enabled == '1'):
+ config.available_features.add('libxml2')
diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.py.in
index 1fb8d3690..50593f7d0 100644
--- a/test/lit.site.cfg.in
+++ b/test/lit.site.cfg.py.in
@@ -4,6 +4,7 @@ config.llvm_src_root = "@LLVM_SOURCE_DIR@"
config.llvm_obj_root = "@LLVM_BINARY_DIR@"
config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
config.llvm_libs_dir = "@LLVM_LIBS_DIR@"
+config.llvm_libxml2_enabled = "@LLVM_LIBXML2_ENABLED@"
config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@"
config.lld_obj_root = "@LLD_BINARY_DIR@"
config.lld_libs_dir = "@LLVM_LIBRARY_OUTPUT_INTDIR@"
@@ -21,5 +22,7 @@ except KeyError as e:
key, = e.args
lit_config.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key,key))
+@LIT_SITE_CFG_IN_FOOTER@
+
# Let the main config do the real work.
-lit_config.load_config(config, "@LLD_SOURCE_DIR@/test/lit.cfg")
+lit_config.load_config(config, "@LLD_SOURCE_DIR@/test/lit.cfg.py")
diff --git a/test/mach-o/executable-exports.yaml b/test/mach-o/executable-exports.yaml
index 0b8717eb7..f16cbd5ce 100644
--- a/test/mach-o/executable-exports.yaml
+++ b/test/mach-o/executable-exports.yaml
@@ -42,5 +42,5 @@ global-symbols:
...
# CHECK-NOT: _myHidden
-# CHECK: 0x00000FFD _myRegular
-# CHECK: 0x00000FFE _myWeak [weak_def]
+# CHECK: 0x100000FFD _myRegular
+# CHECK: 0x100000FFE _myWeak [weak_def]
diff --git a/test/mach-o/lazy-bind-x86_64.yaml b/test/mach-o/lazy-bind-x86_64.yaml
index 5c588c571..1322719e5 100644
--- a/test/mach-o/lazy-bind-x86_64.yaml
+++ b/test/mach-o/lazy-bind-x86_64.yaml
@@ -80,8 +80,8 @@ undefined-symbols:
# CHECK-HELPERS:Disassembly of section __TEXT,__stub_helper:
# CHECK-HELPERS: 68 00 00 00 00 pushq $0
-# CHECK-HELPERS: 68 10 00 00 00 pushq $16
-# CHECK-HELPERS: 68 20 00 00 00 pushq $32
+# CHECK-HELPERS: 68 0b 00 00 00 pushq $11
+# CHECK-HELPERS: 68 16 00 00 00 pushq $22
# Make sure the stub helper is correctly aligned
# CHECK-DYLIBS: sectname __stub_helper
diff --git a/tools/lld/CMakeLists.txt b/tools/lld/CMakeLists.txt
index 2df10697f..01f8837ae 100644
--- a/tools/lld/CMakeLists.txt
+++ b/tools/lld/CMakeLists.txt
@@ -7,16 +7,17 @@ add_lld_tool(lld
)
target_link_libraries(lld
- lldDriver
lldCOFF
+ lldDriver
lldELF
+ lldMinGW
)
install(TARGETS lld
RUNTIME DESTINATION bin)
if(NOT LLD_SYMLINKS_TO_CREATE)
- set(LLD_SYMLINKS_TO_CREATE lld-link ld.lld)
+ set(LLD_SYMLINKS_TO_CREATE lld-link ld.lld ld64.lld)
endif()
foreach(link ${LLD_SYMLINKS_TO_CREATE})
diff --git a/tools/lld/lld.cpp b/tools/lld/lld.cpp
index 09f807901..bbdba4837 100644
--- a/tools/lld/lld.cpp
+++ b/tools/lld/lld.cpp
@@ -16,7 +16,7 @@
//
//===----------------------------------------------------------------------===//
-#include "lld/Driver/Driver.h"
+#include "lld/Common/Driver.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Twine.h"
@@ -45,10 +45,20 @@ static Flavor getFlavor(StringRef S) {
return StringSwitch<Flavor>(S)
.CasesLower("ld", "ld.lld", "gnu", Gnu)
.CaseLower("link", WinLink)
- .CaseLower("darwin", Darwin)
+ .CasesLower("ld64", "ld64.lld", "darwin", Darwin)
.Default(Invalid);
}
+static bool isPETarget(const std::vector<const char *> &V) {
+ for (auto It = V.begin(); It + 1 != V.end(); ++It) {
+ if (StringRef(*It) != "-m")
+ continue;
+ StringRef S = *(It + 1);
+ return S == "i386pe" || S == "i386pep" || S == "thumb2pe" || S == "arm64pe";
+ }
+ return false;
+}
+
static Flavor parseProgname(StringRef Progname) {
#if __APPLE__
// Use Darwin driver for "ld" on Darwin.
@@ -101,6 +111,8 @@ int main(int Argc, const char **Argv) {
std::vector<const char *> Args(Argv, Argv + Argc);
switch (parseFlavor(Args)) {
case Gnu:
+ if (isPETarget(Args))
+ return !mingw::link(Args);
return !elf::link(Args, true);
case WinLink:
return !coff::link(Args);
diff --git a/unittests/DriverTests/DarwinLdDriverTest.cpp b/unittests/DriverTests/DarwinLdDriverTest.cpp
index d81f1543f..696be69bc 100644
--- a/unittests/DriverTests/DarwinLdDriverTest.cpp
+++ b/unittests/DriverTests/DarwinLdDriverTest.cpp
@@ -12,7 +12,7 @@
///
//===----------------------------------------------------------------------===//
-#include "lld/Driver/Driver.h"
+#include "lld/Common/Driver.h"
#include "lld/ReaderWriter/MachOLinkingContext.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/Support/raw_ostream.h"